Compare commits

..

No commits in common. "3c8e33f681d5a9d9d3e1ee2003636de14cae7c0c" and "a3b89f77010a85d858ee1d26a7e46d184bb07b64" have entirely different histories.

18 changed files with 729 additions and 355 deletions

View File

@ -1,2 +0,0 @@
[registries.crates-io]
protocol = "sparse"

731
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
[package]
name = "magnetar"
description = "An exploratory ActivityPub project"
version = "0.1.1"
version = "0.1.0"
license = "AGPL-3.0-only"
edition = "2021"
@ -33,6 +33,10 @@ tower-http = { version = "0.4", features = ["cors", "trace"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing = "0.1"
ring = "0.16"
rand = { version = "0.8", features = ["getrandom"] }
rsa = "0.8"
percent-encoding = "2.2"
serde = { version = "1.0", features = ["derive"] }

View File

@ -0,0 +1,151 @@
use crate::web_model::ListContaining;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
pub struct ContextActivityStreams;
impl AsRef<str> for ContextActivityStreams {
fn as_ref(&self) -> &'static str {
"https://www.w3.org/ns/activitystreams"
}
}
impl Serialize for ContextActivityStreams {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_ref())
}
}
impl FromStr for ContextActivityStreams {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if matches!(
s,
"https://www.w3.org/ns/activitystreams" | "http://www.w3.org/ns/activitystreams"
) {
Ok(Self)
} else {
Err(format!("Invalid context: {s}"))
}
}
}
impl<'de> Deserialize<'de> for ContextActivityStreams {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let context = String::deserialize(deserializer)?;
ContextActivityStreams::from_str(&context).map_err(Error::custom)
}
}
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Context {
String(ContextActivityStreams),
Object {
#[serde(rename = "@vocab")]
ld_vocab: ContextActivityStreams,
},
List(ListContaining<ContextActivityStreams>),
}
impl Default for Context {
fn default() -> Self {
Context::String(ContextActivityStreams)
}
}
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
struct ActivityStreamsDocument<T> {
#[serde(rename = "@context", default)]
ld_context: Context,
#[serde(flatten)]
data: T,
}
#[cfg(test)]
mod test {
use crate::web_model::activity_streams::{
ActivityStreamsDocument, Context, ContextActivityStreams,
};
use crate::web_model::ListContaining;
use serde_json::json;
#[test]
fn should_parse_context() {
let json = json!({
"@context": "https://www.w3.org/ns/activitystreams",
"some": "stuff"
});
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
assert_eq!(doc.ld_context, Context::String(ContextActivityStreams));
}
#[test]
fn should_parse_missing_context() {
let json = json!({
"some": "stuff"
});
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
assert_eq!(doc.ld_context, Context::String(ContextActivityStreams));
}
#[test]
fn should_parse_context_http() {
let json = json!({
"@context": "http://www.w3.org/ns/activitystreams",
"some": "stuff"
});
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
assert_eq!(doc.ld_context, Context::String(ContextActivityStreams));
}
#[test]
fn should_parse_context_vocab() {
let json = json!({
"@context": {
"@vocab": "https://www.w3.org/ns/activitystreams",
"foo": "bar"
},
"some": "stuff"
});
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
assert_eq!(
doc.ld_context,
Context::Object {
ld_vocab: ContextActivityStreams
}
);
}
#[test]
fn should_parse_context_array() {
let json = json!({
"@context": [
{
"foo": "bar"
},
"https://www.w3.org/ns/activitystreams",
],
"some": "stuff"
});
let doc: ActivityStreamsDocument<()> = serde_json::from_value(json).unwrap();
assert_eq!(
doc.ld_context,
Context::List(ListContaining(ContextActivityStreams))
);
}
}

View File

@ -5,6 +5,7 @@ use std::fmt::Debug;
use std::str::FromStr;
pub mod acct;
pub mod activity_streams;
pub trait ContentType: Serialize {
fn mime_type(&self) -> &'static str;

View File

@ -20,8 +20,6 @@ pub struct Model {
#[sea_orm(column_name = "publicUrl")]
pub public_url: String,
pub license: Option<String>,
pub width: Option<i32>,
pub height: Option<i32>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -179,14 +179,6 @@ pub struct Model {
pub enable_guest_timeline: bool,
#[sea_orm(column_name = "defaultReaction")]
pub default_reaction: String,
#[sea_orm(column_name = "libreTranslateApiUrl")]
pub libre_translate_api_url: Option<String>,
#[sea_orm(column_name = "libreTranslateApiKey")]
pub libre_translate_api_key: Option<String>,
#[sea_orm(column_name = "silencedHosts")]
pub silenced_hosts: Vec<String>,
#[sea_orm(column_name = "experimentalFeatures")]
pub experimental_features: Json,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -34,7 +34,6 @@ pub mod moderation_log;
pub mod muted_note;
pub mod muting;
pub mod note;
pub mod note_edit;
pub mod note_favorite;
pub mod note_reaction;
pub mod note_thread_muting;
@ -52,6 +51,8 @@ pub mod registration_ticket;
pub mod registry_item;
pub mod relay;
pub mod renote_muting;
pub mod reversi_game;
pub mod reversi_matching;
pub mod sea_orm_active_enums;
pub mod signin;
pub mod sw_subscription;

View File

@ -58,8 +58,6 @@ pub struct Model {
pub channel_id: Option<String>,
#[sea_orm(column_name = "threadId")]
pub thread_id: Option<String>,
#[sea_orm(column_name = "updatedAt")]
pub updated_at: Option<DateTimeWithTimeZone>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
@ -96,8 +94,6 @@ pub enum Relation {
on_delete = "Cascade"
)]
SelfRef1,
#[sea_orm(has_many = "super::note_edit::Entity")]
NoteEdit,
#[sea_orm(has_many = "super::note_favorite::Entity")]
NoteFavorite,
#[sea_orm(has_many = "super::note_reaction::Entity")]
@ -158,12 +154,6 @@ impl Related<super::muted_note::Entity> for Entity {
}
}
impl Related<super::note_edit::Entity> for Entity {
fn to() -> RelationDef {
Relation::NoteEdit.def()
}
}
impl Related<super::note_favorite::Entity> for Entity {
fn to() -> RelationDef {
Relation::NoteFavorite.def()

View File

@ -1,39 +0,0 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.7
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "note_edit")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "noteId")]
pub note_id: String,
#[sea_orm(column_type = "Text", nullable)]
pub text: Option<String>,
pub cw: Option<String>,
#[sea_orm(column_name = "fileIds")]
pub file_ids: Vec<String>,
#[sea_orm(column_name = "updatedAt")]
pub updated_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::note::Entity",
from = "Column::NoteId",
to = "super::note::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Note,
}
impl Related<super::note::Entity> for Entity {
fn to() -> RelationDef {
Relation::Note.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -32,7 +32,6 @@ pub use super::moderation_log::Entity as ModerationLog;
pub use super::muted_note::Entity as MutedNote;
pub use super::muting::Entity as Muting;
pub use super::note::Entity as Note;
pub use super::note_edit::Entity as NoteEdit;
pub use super::note_favorite::Entity as NoteFavorite;
pub use super::note_reaction::Entity as NoteReaction;
pub use super::note_thread_muting::Entity as NoteThreadMuting;
@ -50,6 +49,8 @@ pub use super::registration_ticket::Entity as RegistrationTicket;
pub use super::registry_item::Entity as RegistryItem;
pub use super::relay::Entity as Relay;
pub use super::renote_muting::Entity as RenoteMuting;
pub use super::reversi_game::Entity as ReversiGame;
pub use super::reversi_matching::Entity as ReversiMatching;
pub use super::signin::Entity as Signin;
pub use super::sw_subscription::Entity as SwSubscription;
pub use super::used_username::Entity as UsedUsername;

View File

@ -0,0 +1,64 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.7
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "reversi_game")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "createdAt")]
pub created_at: DateTimeWithTimeZone,
#[sea_orm(column_name = "startedAt")]
pub started_at: Option<DateTimeWithTimeZone>,
#[sea_orm(column_name = "user1Id")]
pub user1_id: String,
#[sea_orm(column_name = "user2Id")]
pub user2_id: String,
#[sea_orm(column_name = "user1Accepted")]
pub user1_accepted: bool,
#[sea_orm(column_name = "user2Accepted")]
pub user2_accepted: bool,
pub black: Option<i32>,
#[sea_orm(column_name = "isStarted")]
pub is_started: bool,
#[sea_orm(column_name = "isEnded")]
pub is_ended: bool,
#[sea_orm(column_name = "winnerId")]
pub winner_id: Option<String>,
pub surrendered: Option<String>,
pub logs: Json,
pub map: Vec<String>,
pub bw: String,
#[sea_orm(column_name = "isLlotheo")]
pub is_llotheo: bool,
#[sea_orm(column_name = "canPutEverywhere")]
pub can_put_everywhere: bool,
#[sea_orm(column_name = "loopedBoard")]
pub looped_board: bool,
pub form1: Option<Json>,
pub form2: Option<Json>,
pub crc32: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::user::Entity",
from = "Column::User2Id",
to = "super::user::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
User2,
#[sea_orm(
belongs_to = "super::user::Entity",
from = "Column::User1Id",
to = "super::user::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
User1,
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,38 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.7
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "reversi_matching")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "createdAt")]
pub created_at: DateTimeWithTimeZone,
#[sea_orm(column_name = "parentId")]
pub parent_id: String,
#[sea_orm(column_name = "childId")]
pub child_id: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::user::Entity",
from = "Column::ParentId",
to = "super::user::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
User2,
#[sea_orm(
belongs_to = "super::user::Entity",
from = "Column::ChildId",
to = "super::user::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
User1,
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -77,8 +77,6 @@ pub enum MutedNoteReasonEnum {
pub enum NoteVisibilityEnum {
#[sea_orm(string_value = "followers")]
Followers,
#[sea_orm(string_value = "hidden")]
Hidden,
#[sea_orm(string_value = "home")]
Home,
#[sea_orm(string_value = "public")]

View File

@ -61,6 +61,8 @@ pub struct Model {
pub hide_online_status: bool,
#[sea_orm(column_name = "isDeleted")]
pub is_deleted: bool,
#[sea_orm(column_name = "showTimelineReplies")]
pub show_timeline_replies: bool,
#[sea_orm(column_name = "driveCapacityOverrideMb")]
pub drive_capacity_override_mb: Option<i32>,
#[sea_orm(column_name = "movedToUri")]

View File

@ -68,8 +68,6 @@ pub struct Model {
pub auto_sensitive: bool,
#[sea_orm(column_name = "moderationNote")]
pub moderation_note: String,
#[sea_orm(column_name = "preventAiLearning")]
pub prevent_ai_learning: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -94,21 +94,3 @@ pub async fn handle_nodeinfo_20(State(config): State<&'static MagnetarConfig>) -
metadata: HashMap::new(),
})
}
#[cfg(test)]
mod tests {
use crate::config::MagnetarConfig;
use axum::extract::State;
#[tokio::test]
async fn test_nodeinfo() {
std::env::set_var("MAG_C_HOST", "nattyarch.local");
std::env::set_var("MAG_C_DATABASE_URL", "dummy");
let config = MagnetarConfig::default();
let config_ref = Box::leak(Box::new(config));
let nodeinfo = crate::nodeinfo::handle_nodeinfo(State(config_ref)).await;
println!("{:#?}", nodeinfo);
}
}

View File

@ -52,7 +52,7 @@ pub async fn handle_webfinger(
StatusCode::INTERNAL_SERVER_ERROR
})?
}
// TODO: Make this work for local users
// Kinda a
WebFingerSubject::Url(url) => ck.get_user_by_uri(&url).await.map_err(|e| {
error!("Data error: {e}");
StatusCode::INTERNAL_SERVER_ERROR