diff --git a/src/migrations/m20240421_000001_add_attachments.rs b/src/migrations/m20240421_000001_add_attachments.rs new file mode 100644 index 00000000..dd66d90d --- /dev/null +++ b/src/migrations/m20240421_000001_add_attachments.rs @@ -0,0 +1,66 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Attachments::Table) + .col( + ColumnDef::new(Attachments::Id) + .integer() + .not_null() + .auto_increment() + .primary_key() + ) + .col(ColumnDef::new(Attachments::Url).string().not_null()) + .col(ColumnDef::new(Attachments::Object).string().not_null()) + .col(ColumnDef::new(Attachments::DocumentType).string().not_null()) + .col(ColumnDef::new(Attachments::Name).string().null()) + .col(ColumnDef::new(Attachments::MediaType).string().not_null()) + .col(ColumnDef::new(Attachments::Created).date_time().not_null()) + .to_owned() + ) + .await?; + + manager + .create_index( + Index::create() + .name("attachment-object-index") + .table(Attachments::Table) + .col(Attachments::Object) + .to_owned() + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Attachments::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("attachment-object-index").to_owned()) + .await?; + + Ok(()) + } +} + +#[derive(DeriveIden)] +enum Attachments { + Table, + Id, + Url, + Object, + DocumentType, + Name, + MediaType, + Created, +} diff --git a/src/migrations/mod.rs b/src/migrations/mod.rs index 19d63012..9396eba3 100644 --- a/src/migrations/mod.rs +++ b/src/migrations/mod.rs @@ -10,6 +10,7 @@ mod m20240324_000001_add_addressing; mod m20240325_000001_add_deliveries; mod m20240325_000002_add_system_key; mod m20240418_000001_add_statuses_and_reply_to; +mod m20240421_000001_add_attachments; pub struct Migrator; @@ -27,6 +28,7 @@ impl MigratorTrait for Migrator { Box::new(m20240325_000001_add_deliveries::Migration), Box::new(m20240325_000002_add_system_key::Migration), Box::new(m20240418_000001_add_statuses_and_reply_to::Migration), + Box::new(m20240421_000001_add_attachments::Migration), ] } } diff --git a/src/model/attachment.rs b/src/model/attachment.rs new file mode 100644 index 00000000..4a89634a --- /dev/null +++ b/src/model/attachment.rs @@ -0,0 +1,61 @@ +use apb::{DocumentMut, ObjectMut}; +use sea_orm::{entity::prelude::*, Set}; + +use crate::routes::activitypub::jsonld::LD; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "attachments")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + + pub url: String, + pub object: String, + pub document_type: apb::DocumentType, + pub name: Option, + pub media_type: String, + pub created: ChronoDateTimeUtc, +} + +impl ActiveModel { + pub fn new(document: &impl apb::Document, object: String) -> Result { + Ok(ActiveModel { + id: sea_orm::ActiveValue::NotSet, + object: Set(object), + url: Set(document.url().id().ok_or(super::FieldError("url"))?), + document_type: Set(document.document_type().ok_or(super::FieldError("type"))?), + media_type: Set(document.media_type().ok_or(super::FieldError("mediaType"))?.to_string()), + name: Set(document.name().map(|x| x.to_string())), + created: Set(document.published().unwrap_or(chrono::Utc::now())), + }) + } +} + +impl Model { + pub fn ap(self) -> serde_json::Value { + serde_json::Value::new_object() + .set_url(apb::Node::link(self.url)) + .set_document_type(Some(self.document_type)) + .set_media_type(Some(&self.media_type)) + .set_name(self.name.as_deref()) + .set_published(Some(self.created)) + } +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::object::Entity", + from = "Column::Object", + to = "super::object::Column::Id" + )] + Object, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Object.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/mod.rs b/src/model/mod.rs index cfac749a..22acc2bf 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -10,6 +10,7 @@ pub mod like; pub mod credential; pub mod session; pub mod delivery; +pub mod attachment; pub mod application; #[cfg(feature = "faker")] diff --git a/src/model/object.rs b/src/model/object.rs index c95886e7..1101a510 100644 --- a/src/model/object.rs +++ b/src/model/object.rs @@ -48,7 +48,7 @@ impl Model { bcc: object.bcc().into(), }) } - // TODO this is used outside /routes, maybe move in model? + pub fn ap(self) -> serde_json::Value { serde_json::Value::new_object() .set_id(Some(&self.id)) @@ -81,6 +81,9 @@ pub enum Relation { #[sea_orm(has_many = "super::addressing::Entity")] Addressing, + + #[sea_orm(has_many = "super::attachment::Entity")] + Attachment, } impl Related for Entity { @@ -101,4 +104,10 @@ impl Related for Entity { } } +impl Related for Entity { + fn to() -> RelationDef { + Relation::Attachment.def() + } +} + impl ActiveModelBehavior for ActiveModel {} diff --git a/src/server/fetcher.rs b/src/server/fetcher.rs index c6ae905f..42e6078a 100644 --- a/src/server/fetcher.rs +++ b/src/server/fetcher.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use apb::target::Addressed; +use apb::{target::Addressed, Object}; use base64::Engine; use reqwest::{header::{ACCEPT, CONTENT_TYPE, USER_AGENT}, Method, Response}; use sea_orm::{EntityTrait, IntoActiveModel}; @@ -131,6 +131,13 @@ impl Fetcher for Context { let addressed = object.addressed(); let object_model = model::object::Model::new(&object)?; + for attachment in object.attachment() { + let attachment_model = model::attachment::ActiveModel::new(&attachment, object_model.id.clone())?; + model::attachment::Entity::insert(attachment_model) + .exec(self.db()) + .await?; + } + let expanded_addresses = self.expand_addressing(addressed).await?; self.address_to(None, Some(&object_model.id), &expanded_addresses).await?; diff --git a/src/server/inbox.rs b/src/server/inbox.rs index 678f4a5f..94a4c725 100644 --- a/src/server/inbox.rs +++ b/src/server/inbox.rs @@ -33,6 +33,12 @@ impl apb::server::Inbox for Context { let oid = object_model.id.clone(); model::object::Entity::insert(object_model.into_active_model()).exec(self.db()).await?; model::activity::Entity::insert(activity_model.into_active_model()).exec(self.db()).await?; + for attachment in object_node.attachment() { + let attachment_model = model::attachment::ActiveModel::new(&attachment, oid.clone())?; + model::attachment::Entity::insert(attachment_model) + .exec(self.db()) + .await?; + } let expanded_addressing = self.expand_addressing(activity.addressed()).await?; self.address_to(Some(&aid), Some(&oid), &expanded_addressing).await?; tracing::info!("{} posted {}", aid, oid);