From 58c20b7ba5d4785c9758f39456315e56ace9eda9 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 24 May 2024 03:41:45 +0200 Subject: [PATCH 01/90] feat!: restructured db, remade all migrations sorry! your db is now useless (: you weren't using upub in production, were you?? brb gonna migrate 50k posts from test db... --- .../m20240316_000001_create_table.rs | 169 -------- .../m20240322_000001_create_relations.rs | 44 -- .../m20240322_000002_add_likes_shares.rs | 80 ---- .../m20240322_000003_add_indexes.rs | 176 -------- .../m20240323_000001_add_user_configs.rs | 49 --- ...m20240323_000002_add_simple_credentials.rs | 67 ---- .../m20240324_000001_add_addressing.rs | 110 ----- .../m20240325_000001_add_deliveries.rs | 66 --- .../m20240325_000002_add_system_key.rs | 46 --- ...240418_000001_add_statuses_and_reply_to.rs | 72 ---- .../m20240421_000001_add_attachments.rs | 66 --- .../m20240424_000001_add_sensitive_field.rs | 45 --- .../m20240429_000001_add_relays_table.rs | 43 -- .../m20240502_000001_add_object_updated.rs | 40 -- .../m20240512_000001_add_url_to_objects.rs | 40 -- ...add_published_to_addressing_actor_index.rs | 82 ---- ...001_create_actor_activity_object_tables.rs | 375 ++++++++++++++++++ ...24_000002_create_relations_likes_shares.rs | 239 +++++++++++ ...524_000003_create_users_auth_and_config.rs | 167 ++++++++ ...524_000004_create_addressing_deliveries.rs | 195 +++++++++ ...000005_create_attachments_tags_mentions.rs | 207 ++++++++++ src/migrations/mod.rs | 42 +- 22 files changed, 1193 insertions(+), 1227 deletions(-) delete mode 100644 src/migrations/m20240316_000001_create_table.rs delete mode 100644 src/migrations/m20240322_000001_create_relations.rs delete mode 100644 src/migrations/m20240322_000002_add_likes_shares.rs delete mode 100644 src/migrations/m20240322_000003_add_indexes.rs delete mode 100644 src/migrations/m20240323_000001_add_user_configs.rs delete mode 100644 src/migrations/m20240323_000002_add_simple_credentials.rs delete mode 100644 src/migrations/m20240324_000001_add_addressing.rs delete mode 100644 src/migrations/m20240325_000001_add_deliveries.rs delete mode 100644 src/migrations/m20240325_000002_add_system_key.rs delete mode 100644 src/migrations/m20240418_000001_add_statuses_and_reply_to.rs delete mode 100644 src/migrations/m20240421_000001_add_attachments.rs delete mode 100644 src/migrations/m20240424_000001_add_sensitive_field.rs delete mode 100644 src/migrations/m20240429_000001_add_relays_table.rs delete mode 100644 src/migrations/m20240502_000001_add_object_updated.rs delete mode 100644 src/migrations/m20240512_000001_add_url_to_objects.rs delete mode 100644 src/migrations/m20240520_000001_add_published_to_addressing_actor_index.rs create mode 100644 src/migrations/m20240524_000001_create_actor_activity_object_tables.rs create mode 100644 src/migrations/m20240524_000002_create_relations_likes_shares.rs create mode 100644 src/migrations/m20240524_000003_create_users_auth_and_config.rs create mode 100644 src/migrations/m20240524_000004_create_addressing_deliveries.rs create mode 100644 src/migrations/m20240524_000005_create_attachments_tags_mentions.rs diff --git a/src/migrations/m20240316_000001_create_table.rs b/src/migrations/m20240316_000001_create_table.rs deleted file mode 100644 index 8fbcdc7..0000000 --- a/src/migrations/m20240316_000001_create_table.rs +++ /dev/null @@ -1,169 +0,0 @@ -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(Users::Table) - .col( - ColumnDef::new(Users::Id) - .string() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Users::ActorType).string().not_null()) - .col(ColumnDef::new(Users::Domain).string().not_null()) - .col(ColumnDef::new(Users::Name).string().null()) - .col(ColumnDef::new(Users::Summary).string().null()) - .col(ColumnDef::new(Users::Image).string().null()) - .col(ColumnDef::new(Users::Icon).string().null()) - .col(ColumnDef::new(Users::PreferredUsername).string().not_null()) - .col(ColumnDef::new(Users::Inbox).string().null()) - .col(ColumnDef::new(Users::SharedInbox).string().null()) - .col(ColumnDef::new(Users::Outbox).string().null()) - .col(ColumnDef::new(Users::Following).string().null()) - .col(ColumnDef::new(Users::Followers).string().null()) - .col(ColumnDef::new(Users::FollowingCount).integer().not_null().default(0)) - .col(ColumnDef::new(Users::FollowersCount).integer().not_null().default(0)) - // .col(ColumnDef::new(Users::StatusesCount).integer().not_null().default(0)) - .col(ColumnDef::new(Users::PublicKey).string().not_null()) - .col(ColumnDef::new(Users::PrivateKey).string().null()) - .col(ColumnDef::new(Users::Created).date_time().not_null()) - .col(ColumnDef::new(Users::Updated).date_time().not_null()) - .to_owned() - ) - .await?; - - manager - .create_table( - Table::create() - .table(Activities::Table) - .col( - ColumnDef::new(Activities::Id) - .string() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Activities::ActivityType).string().not_null()) - .col(ColumnDef::new(Activities::Actor).string().not_null()) - .col(ColumnDef::new(Activities::Object).string().null()) - .col(ColumnDef::new(Activities::Target).string().null()) - .col(ColumnDef::new(Activities::To).json().null()) - .col(ColumnDef::new(Activities::Bto).json().null()) - .col(ColumnDef::new(Activities::Cc).json().null()) - .col(ColumnDef::new(Activities::Bcc).json().null()) - .col(ColumnDef::new(Activities::Published).date_time().not_null()) - .to_owned() - ).await?; - - manager - .create_table( - Table::create() - .table(Objects::Table) - .col( - ColumnDef::new(Objects::Id) - .string() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Objects::ObjectType).string().not_null()) - .col(ColumnDef::new(Objects::AttributedTo).string().null()) - .col(ColumnDef::new(Objects::Name).string().null()) - .col(ColumnDef::new(Objects::Summary).string().null()) - .col(ColumnDef::new(Objects::Content).string().null()) - .col(ColumnDef::new(Objects::Likes).integer().not_null().default(0)) - .col(ColumnDef::new(Objects::Shares).integer().not_null().default(0)) - .col(ColumnDef::new(Objects::Comments).integer().not_null().default(0)) - .col(ColumnDef::new(Objects::Context).string().null()) - .col(ColumnDef::new(Objects::To).json().null()) - .col(ColumnDef::new(Objects::Bto).json().null()) - .col(ColumnDef::new(Objects::Cc).json().null()) - .col(ColumnDef::new(Objects::Bcc).json().null()) - .col(ColumnDef::new(Objects::Published).string().not_null()) - .to_owned() - ).await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Users::Table).to_owned()) - .await?; - - manager - .drop_table(Table::drop().table(Activities::Table).to_owned()) - .await?; - - manager - .drop_table(Table::drop().table(Objects::Table).to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Users { - Table, - Id, - Domain, - ActorType, - Name, - Summary, - Image, - Icon, - PreferredUsername, - Inbox, - SharedInbox, - Outbox, - Following, - FollowingCount, - Followers, - FollowersCount, - // StatusesCount, - PublicKey, - PrivateKey, - Created, - Updated, -} - -#[derive(DeriveIden)] -enum Activities { - Table, - Id, - ActivityType, - Actor, - Object, - Target, - Cc, - Bcc, - To, - Bto, - Published, -} - -#[derive(DeriveIden)] -enum Objects { - Table, - Id, - ObjectType, - AttributedTo, - Name, - Summary, - Content, - Likes, - Shares, - Comments, - Context, - Cc, - Bcc, - To, - Bto, - Published, -} diff --git a/src/migrations/m20240322_000001_create_relations.rs b/src/migrations/m20240322_000001_create_relations.rs deleted file mode 100644 index e797c4d..0000000 --- a/src/migrations/m20240322_000001_create_relations.rs +++ /dev/null @@ -1,44 +0,0 @@ -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(Relations::Table) - .col( - ColumnDef::new(Relations::Id) - .integer() - .auto_increment() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Relations::Follower).string().not_null()) - .col(ColumnDef::new(Relations::Following).string().not_null()) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Relations::Table).to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Relations { - Table, - Id, - Follower, - Following, -} diff --git a/src/migrations/m20240322_000002_add_likes_shares.rs b/src/migrations/m20240322_000002_add_likes_shares.rs deleted file mode 100644 index 54361a8..0000000 --- a/src/migrations/m20240322_000002_add_likes_shares.rs +++ /dev/null @@ -1,80 +0,0 @@ -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(Likes::Table) - .col( - ColumnDef::new(Likes::Id) - .integer() - .auto_increment() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Likes::Actor).string().not_null()) - .col(ColumnDef::new(Likes::Likes).string().not_null()) - .col(ColumnDef::new(Likes::Date).date_time().not_null()) - .to_owned() - ) - .await?; - - manager - .create_table( - Table::create() - .table(Shares::Table) - .col( - ColumnDef::new(Shares::Id) - .integer() - .auto_increment() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Shares::Actor).string().not_null()) - .col(ColumnDef::new(Shares::Shares).string().not_null()) - .col(ColumnDef::new(Shares::Date).date_time().not_null()) - .to_owned() - ) - .await?; - - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Likes::Table).to_owned()) - .await?; - - manager - .drop_table(Table::drop().table(Shares::Table).to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -#[allow(clippy::enum_variant_names)] -enum Likes { - Table, - Id, - Actor, - Likes, - Date, -} - -#[derive(DeriveIden)] -#[allow(clippy::enum_variant_names)] -enum Shares { - Table, - Id, - Actor, - Shares, - Date, -} diff --git a/src/migrations/m20240322_000003_add_indexes.rs b/src/migrations/m20240322_000003_add_indexes.rs deleted file mode 100644 index 3469e4a..0000000 --- a/src/migrations/m20240322_000003_add_indexes.rs +++ /dev/null @@ -1,176 +0,0 @@ -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_index( - Index::create() - .name("user-domain-index") - .table(Users::Table) - .col(Users::Domain) - .to_owned() - ) - .await?; - - manager - .create_index( - Index::create() - .name("activities-published-descending-index") - .table(Activities::Table) - .col((Activities::Published, IndexOrder::Desc)) - .to_owned() - ) - .await?; - - manager - .create_index( - Index::create() - .name("activities-actor-index") - .table(Activities::Table) - .col(Activities::Actor) - .to_owned() - ) - .await?; - - manager - .create_index( - Index::create() - .name("activities-object-index") - .table(Activities::Table) - .col(Activities::Object) - .to_owned() - ).await?; - - manager - .create_index( - Index::create() - .name("objects-attributed-to-index") - .table(Objects::Table) - .col(Objects::AttributedTo) - .to_owned() - ).await?; - - manager - .create_index( - Index::create() - .name("shares-actor-index") - .table(Shares::Table) - .col(Shares::Actor) - .to_owned() - ).await?; - - manager - .create_index( - Index::create() - .name("shares-shares-index") - .table(Shares::Table) - .col(Shares::Shares) - .to_owned() - ).await?; - - manager - .create_index( - Index::create() - .name("likes-actor-index") - .table(Likes::Table) - .col(Likes::Actor) - .to_owned() - ).await?; - - manager - .create_index( - Index::create() - .name("likes-likes-index") - .table(Likes::Table) - .col(Likes::Likes) - .to_owned() - ).await?; - - manager - .create_index( - Index::create() - .name("likes-actor-likes-index") - .table(Likes::Table) - .col(Likes::Actor) - .col(Likes::Likes) - .unique() - .to_owned() - ).await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_index(Index::drop().name("user-domain-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("activities-published-descending-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("activities-actor-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("activities-object-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("objects-attributed-to-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("shares-actor-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("shares-shares-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("likes-actor-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("likes-likes-index").to_owned()) - .await?; - manager - .drop_index(Index::drop().name("likes-actor-likes-index").to_owned()) - .await?; - Ok(()) - } -} - -#[derive(DeriveIden)] -#[allow(clippy::enum_variant_names)] -enum Likes { - Table, - Actor, - Likes, -} - -#[derive(DeriveIden)] -#[allow(clippy::enum_variant_names)] -enum Shares { - Table, - Actor, - Shares, -} - -#[derive(DeriveIden)] -enum Users { - Table, - Domain, -} - -#[derive(DeriveIden)] -enum Activities { - Table, - Actor, - Object, - Published, -} - -#[derive(DeriveIden)] -enum Objects { - Table, - AttributedTo, -} diff --git a/src/migrations/m20240323_000001_add_user_configs.rs b/src/migrations/m20240323_000001_add_user_configs.rs deleted file mode 100644 index 42dc8fd..0000000 --- a/src/migrations/m20240323_000001_add_user_configs.rs +++ /dev/null @@ -1,49 +0,0 @@ -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(Configs::Table) - .col( - ColumnDef::new(Configs::Id) - .string() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Configs::AcceptFollowRequests).boolean().not_null()) - .col(ColumnDef::new(Configs::ShowFollowersCount).boolean().not_null()) - .col(ColumnDef::new(Configs::ShowFollowingCount).boolean().not_null()) - .col(ColumnDef::new(Configs::ShowFollowers).boolean().not_null()) - .col(ColumnDef::new(Configs::ShowFollowing).boolean().not_null()) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Configs::Table).to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Configs { - Table, - Id, - AcceptFollowRequests, - ShowFollowersCount, - ShowFollowingCount, - ShowFollowers, - ShowFollowing, -} diff --git a/src/migrations/m20240323_000002_add_simple_credentials.rs b/src/migrations/m20240323_000002_add_simple_credentials.rs deleted file mode 100644 index 4600716..0000000 --- a/src/migrations/m20240323_000002_add_simple_credentials.rs +++ /dev/null @@ -1,67 +0,0 @@ -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(Credentials::Table) - .col( - ColumnDef::new(Credentials::Id) - .string() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Credentials::Email).string().not_null()) - .col(ColumnDef::new(Credentials::Password).string().not_null()) - .to_owned() - ) - .await?; - - manager - .create_table( - Table::create() - .table(Sessions::Table) - .col( - ColumnDef::new(Sessions::Id) - .string() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Sessions::Actor).string().not_null()) - .col(ColumnDef::new(Sessions::Expires).date_time().not_null()) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Credentials::Table).to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Credentials { - Table, - Id, - Email, - Password, -} - -#[derive(DeriveIden)] -enum Sessions { - Table, - Id, // TODO here ID is the session "secret" but in Credentials it's the actor ID (String) ??? weird!! - Actor, - Expires, -} diff --git a/src/migrations/m20240324_000001_add_addressing.rs b/src/migrations/m20240324_000001_add_addressing.rs deleted file mode 100644 index 358a556..0000000 --- a/src/migrations/m20240324_000001_add_addressing.rs +++ /dev/null @@ -1,110 +0,0 @@ -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(Addressing::Table) - .col( - ColumnDef::new(Addressing::Id) - .integer() - .not_null() - .auto_increment() - .primary_key() - ) - .col(ColumnDef::new(Addressing::Actor).string().not_null()) - .col(ColumnDef::new(Addressing::Server).string().not_null()) - .col(ColumnDef::new(Addressing::Activity).string().null()) - .col(ColumnDef::new(Addressing::Object).string().null()) - .col(ColumnDef::new(Addressing::Published).date_time().not_null()) - .to_owned() - ) - .await?; - - // TODO these indexes may not be ordered, killing out timeline query performance - // it may be necessary to include datetime in the index itself? or maybe specify - // some ordering to use another type of indes? - - manager - .create_index( - Index::create() - .name("addressing-actor-index") - .table(Addressing::Table) - .col(Addressing::Actor) - .to_owned() - ) - .await?; - - manager - .create_index( - Index::create() - .name("addressing-server-index") - .table(Addressing::Table) - .col(Addressing::Server) - .to_owned() - ) - .await?; - - manager - .create_index( - Index::create() - .name("addressing-activity-index") - .table(Addressing::Table) - .col(Addressing::Activity) - .to_owned() - ) - .await?; - - manager - .create_index( - Index::create() - .name("addressing-object-index") - .table(Addressing::Table) - .col(Addressing::Object) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Addressing::Table).to_owned()) - .await?; - - manager - .drop_index(Index::drop().name("addressing-actor-index").to_owned()) - .await?; - - manager - .drop_index(Index::drop().name("addressing-server-index").to_owned()) - .await?; - - manager - .drop_index(Index::drop().name("addressing-activity-index").to_owned()) - .await?; - - manager - .drop_index(Index::drop().name("addressing-object-index").to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Addressing { - Table, - Id, - Actor, - Server, - Activity, - Object, - Published, -} diff --git a/src/migrations/m20240325_000001_add_deliveries.rs b/src/migrations/m20240325_000001_add_deliveries.rs deleted file mode 100644 index a2b9cc1..0000000 --- a/src/migrations/m20240325_000001_add_deliveries.rs +++ /dev/null @@ -1,66 +0,0 @@ -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(Deliveries::Table) - .col( - ColumnDef::new(Deliveries::Id) - .integer() - .not_null() - .auto_increment() - .primary_key() - ) - .col(ColumnDef::new(Deliveries::Actor).string().not_null()) - .col(ColumnDef::new(Deliveries::Target).string().not_null()) - .col(ColumnDef::new(Deliveries::Activity).string().not_null()) - .col(ColumnDef::new(Deliveries::Created).date_time().not_null()) - .col(ColumnDef::new(Deliveries::NotBefore).date_time().not_null()) - .col(ColumnDef::new(Deliveries::Attempt).integer().not_null()) - .to_owned() - ) - .await?; - - manager - .create_index( - Index::create() - .name("deliveries-notbefore-index") - .table(Deliveries::Table) - .col((Deliveries::NotBefore, IndexOrder::Asc)) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Deliveries::Table).to_owned()) - .await?; - - manager - .drop_index(Index::drop().name("deliveries-notbefore-index").to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Deliveries { - Table, - Id, - Actor, - Target, - Activity, - Created, - NotBefore, - Attempt, -} diff --git a/src/migrations/m20240325_000002_add_system_key.rs b/src/migrations/m20240325_000002_add_system_key.rs deleted file mode 100644 index dbd1954..0000000 --- a/src/migrations/m20240325_000002_add_system_key.rs +++ /dev/null @@ -1,46 +0,0 @@ -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(Application::Table) - .col( - ColumnDef::new(Application::Id) - .integer() - .not_null() - .auto_increment() - .primary_key() - ) - .col(ColumnDef::new(Application::PrivateKey).string().not_null()) - .col(ColumnDef::new(Application::PublicKey).string().not_null()) - .col(ColumnDef::new(Application::Created).date_time().not_null()) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Application::Table).to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Application { - Table, - Id, - PrivateKey, - PublicKey, - Created, -} diff --git a/src/migrations/m20240418_000001_add_statuses_and_reply_to.rs b/src/migrations/m20240418_000001_add_statuses_and_reply_to.rs deleted file mode 100644 index effb34f..0000000 --- a/src/migrations/m20240418_000001_add_statuses_and_reply_to.rs +++ /dev/null @@ -1,72 +0,0 @@ -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 - .alter_table( - Table::alter() - .table(Users::Table) - .add_column( - ColumnDef::new(Users::StatusesCount) - .integer() - .not_null() - .default(0) - ) - .to_owned() - ) - .await?; - - manager - .alter_table( - Table::alter() - .table(Objects::Table) - .add_column( - ColumnDef::new(Objects::InReplyTo) - .string() - .null() - ) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .alter_table( - Table::alter() - .table(Users::Table) - .drop_column(Users::StatusesCount) - .to_owned() - ) - .await?; - - manager - .alter_table( - Table::alter() - .table(Objects::Table) - .drop_column(Objects::InReplyTo) - .to_owned() - ) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Users { - Table, - StatusesCount, -} - -#[derive(DeriveIden)] -enum Objects { - Table, - InReplyTo, -} diff --git a/src/migrations/m20240421_000001_add_attachments.rs b/src/migrations/m20240421_000001_add_attachments.rs deleted file mode 100644 index dd66d90..0000000 --- a/src/migrations/m20240421_000001_add_attachments.rs +++ /dev/null @@ -1,66 +0,0 @@ -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/m20240424_000001_add_sensitive_field.rs b/src/migrations/m20240424_000001_add_sensitive_field.rs deleted file mode 100644 index 1ec8fbd..0000000 --- a/src/migrations/m20240424_000001_add_sensitive_field.rs +++ /dev/null @@ -1,45 +0,0 @@ -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 - .alter_table( - Table::alter() - .table(Objects::Table) - .add_column( - ColumnDef::new(Objects::Sensitive) - .boolean() - .not_null() - .default(false) - ) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .alter_table( - Table::alter() - .table(Objects::Table) - .drop_column(Objects::Sensitive) - .to_owned() - ) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Objects { - Table, - Sensitive, -} - diff --git a/src/migrations/m20240429_000001_add_relays_table.rs b/src/migrations/m20240429_000001_add_relays_table.rs deleted file mode 100644 index 21b4141..0000000 --- a/src/migrations/m20240429_000001_add_relays_table.rs +++ /dev/null @@ -1,43 +0,0 @@ -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(Relays::Table) - .col( - ColumnDef::new(Relays::Id) - .string() - .not_null() - .primary_key() - ) - .col(ColumnDef::new(Relays::Accepted).boolean().not_null().default(false)) - .col(ColumnDef::new(Relays::Forwarding).boolean().not_null().default(false)) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Relays::Table).to_owned()) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Relays { - Table, - Id, - Accepted, - Forwarding, -} diff --git a/src/migrations/m20240502_000001_add_object_updated.rs b/src/migrations/m20240502_000001_add_object_updated.rs deleted file mode 100644 index 863c5e0..0000000 --- a/src/migrations/m20240502_000001_add_object_updated.rs +++ /dev/null @@ -1,40 +0,0 @@ -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 - .alter_table( - Table::alter() - .table(Objects::Table) - .add_column(ColumnDef::new(Objects::Updated).date_time().null()) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .alter_table( - Table::alter() - .table(Objects::Table) - .drop_column(Objects::Updated) - .to_owned() - ) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Objects { - Table, - Updated, -} - diff --git a/src/migrations/m20240512_000001_add_url_to_objects.rs b/src/migrations/m20240512_000001_add_url_to_objects.rs deleted file mode 100644 index 373549a..0000000 --- a/src/migrations/m20240512_000001_add_url_to_objects.rs +++ /dev/null @@ -1,40 +0,0 @@ -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 - .alter_table( - Table::alter() - .table(Objects::Table) - .add_column(ColumnDef::new(Objects::Url).string().null()) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .alter_table( - Table::alter() - .table(Objects::Table) - .drop_column(Objects::Url) - .to_owned() - ) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Objects { - Table, - Url, -} - diff --git a/src/migrations/m20240520_000001_add_published_to_addressing_actor_index.rs b/src/migrations/m20240520_000001_add_published_to_addressing_actor_index.rs deleted file mode 100644 index 477dbe7..0000000 --- a/src/migrations/m20240520_000001_add_published_to_addressing_actor_index.rs +++ /dev/null @@ -1,82 +0,0 @@ -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 - .drop_index(Index::drop().name("addressing-actor-index").to_owned()) - .await?; - - manager - .create_index( - Index::create() - .name("addressing-actor-published-index") - .table(Addressing::Table) - .col(Addressing::Actor) - .col(Addressing::Published) - .to_owned() - ) - .await?; - - manager - .drop_index(Index::drop().name("addressing-server-index").to_owned()) - .await?; - - manager - .create_index( - Index::create() - .name("addressing-server-published-index") - .table(Addressing::Table) - .col(Addressing::Server) - .col(Addressing::Published) - .to_owned() - ) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_index(Index::drop().name("addressing-actor-published-index").to_owned()) - .await?; - - manager - .create_index( - Index::create() - .name("addressing-actor-index") - .table(Addressing::Table) - .col(Addressing::Actor) - .to_owned() - ) - .await?; - - manager - .drop_index(Index::drop().name("addressing-server-published-index").to_owned()) - .await?; - - manager - .create_index( - Index::create() - .name("addressing-server-index") - .table(Addressing::Table) - .col(Addressing::Server) - .to_owned() - ) - .await?; - - Ok(()) - } -} - -#[derive(DeriveIden)] -enum Addressing { - Table, - Actor, - Server, - Published, -} diff --git a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs new file mode 100644 index 0000000..e0ffd7a --- /dev/null +++ b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs @@ -0,0 +1,375 @@ +use sea_orm_migration::prelude::*; +#[derive(DeriveIden)] +pub enum Actors { + Table, + Id, + ApId, + Instance, + ActorType, + Name, + Summary, + Image, + Icon, + PreferredUsername, + Inbox, + SharedInbox, + Outbox, + Following, + FollowingCount, + Followers, + FollowersCount, + StatusesCount, + PublicKey, + PrivateKey, + Created, + Updated, +} + +#[derive(DeriveIden)] +pub enum Activities { + Table, + Id, + ApId, + ActivityType, + Actor, + Object, + Target, + Cc, + Bcc, + To, + Bto, + Published, +} + +#[derive(DeriveIden)] +pub enum Objects { + Table, + Id, + ApId, + ObjectType, + AttributedTo, + Name, + Summary, + Content, + Sensitive, + Url, + Likes, + Announces, + Replies, + Context, + InReplyTo, + Cc, + Bcc, + To, + Bto, + Published, + Updated, +} + +#[derive(DeriveIden)] +pub enum Instances { + Table, + Id, + Name, + Domain, + Software, + Version, + DownSince, + Users, + Posts, + Published, + Updated, +} + + +#[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(Actors::Table) + .comment("main actors table, with users and applications") + .col( + ColumnDef::new(Actors::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Actors::ApId).string().not_null().unique_key()) + .col(ColumnDef::new(Actors::ActorType).string().not_null()) + .col(ColumnDef::new(Actors::Instance).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-actors-instances") + .from(Actors::Table, Actors::Instance) + .to(Instances::Table, Instances::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Actors::Name).string().null()) + .col(ColumnDef::new(Actors::Summary).string().null()) + .col(ColumnDef::new(Actors::Image).string().null()) + .col(ColumnDef::new(Actors::Icon).string().null()) + .col(ColumnDef::new(Actors::PreferredUsername).string().not_null().unique_key()) + .col(ColumnDef::new(Actors::Inbox).string().null()) + .col(ColumnDef::new(Actors::SharedInbox).string().null()) + .col(ColumnDef::new(Actors::Outbox).string().null()) + .col(ColumnDef::new(Actors::Following).string().null()) + .col(ColumnDef::new(Actors::Followers).string().null()) + .col(ColumnDef::new(Actors::FollowingCount).integer().not_null().default(0)) + .col(ColumnDef::new(Actors::FollowersCount).integer().not_null().default(0)) + .col(ColumnDef::new(Actors::StatusesCount).integer().not_null().default(0)) + .col(ColumnDef::new(Actors::PublicKey).string().not_null()) + .col(ColumnDef::new(Actors::PrivateKey).string().null()) + .col(ColumnDef::new(Actors::Created).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Actors::Updated).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().unique().name("index-actors-ap-id").table(Actors::Table).col(Actors::ApId).to_owned()) + .await?; + + manager + .create_index(Index::create().unique().name("index-actors-preferred-username").table(Actors::Table).col(Actors::PreferredUsername).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-actors-instance").table(Actors::Table).col(Actors::Instance).to_owned()) + .await?; + + + + manager + .create_table( + Table::create() + .table(Activities::Table) + .comment("all activities this instance ever received or generated") + .col( + ColumnDef::new(Activities::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Activities::ApId).string().not_null().unique_key()) + .col(ColumnDef::new(Activities::ActivityType).string().not_null()) + .col(ColumnDef::new(Activities::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-activities-actors") + .from(Activities::Table, Activities::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Activities::Object).integer().null()) + .foreign_key( + ForeignKey::create() + .name("fkey-activities-objects") + .from(Activities::Table, Activities::Object) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Activities::Target).string().null()) + .col(ColumnDef::new(Activities::To).json().null()) + .col(ColumnDef::new(Activities::Bto).json().null()) + .col(ColumnDef::new(Activities::Cc).json().null()) + .col(ColumnDef::new(Activities::Bcc).json().null()) + .col(ColumnDef::new(Activities::Published).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ).await?; + + manager + .create_index(Index::create().unique().name("index-activities-ap-id").table(Activities::Table).col(Activities::ApId).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-activities-actor").table(Activities::Table).col(Activities::Actor).to_owned()) + .await?; + + manager + .create_index(Index::create().name("activities-object-index").table(Activities::Table).col(Activities::Object).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-activities-published-descending").table(Activities::Table).col((Activities::Published, IndexOrder::Desc)).to_owned()) + .await?; + + + + manager + .create_table( + Table::create() + .table(Objects::Table) + .comment("objects are all AP documents which are neither actors nor activities") + .col( + ColumnDef::new(Objects::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Objects::ApId).string().not_null().unique_key()) + .col(ColumnDef::new(Objects::ObjectType).string().not_null()) + .col(ColumnDef::new(Objects::AttributedTo).integer().null()) + .foreign_key( + ForeignKey::create() + .name("fkey-objects-attributed-to") + .from(Objects::Table, Objects::AttributedTo) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Objects::Name).string().null()) + .col(ColumnDef::new(Objects::Summary).string().null()) + .col(ColumnDef::new(Objects::Content).string().null()) + .col(ColumnDef::new(Objects::Sensitive).boolean().not_null().default(false)) + .col(ColumnDef::new(Objects::InReplyTo).string().null()) + .col(ColumnDef::new(Objects::Url).string().null()) + .col(ColumnDef::new(Objects::Likes).integer().not_null().default(0)) + .col(ColumnDef::new(Objects::Announces).integer().not_null().default(0)) + .col(ColumnDef::new(Objects::Replies).integer().not_null().default(0)) + .col(ColumnDef::new(Objects::Context).string().null()) + .col(ColumnDef::new(Objects::To).json().null()) + .col(ColumnDef::new(Objects::Bto).json().null()) + .col(ColumnDef::new(Objects::Cc).json().null()) + .col(ColumnDef::new(Objects::Bcc).json().null()) + .col(ColumnDef::new(Objects::Published).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Objects::Updated).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ).await?; + + manager + .create_index(Index::create().unique().name("index-objects-ap-id").table(Objects::Table).col(Objects::ApId).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-objects-attributed-to").table(Objects::Table).col(Objects::AttributedTo).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-objects-in-reply-to").table(Objects::Table).col(Objects::InReplyTo).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-objects-content-text").table(Objects::Table).col(Objects::Content).full_text().to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-objects-context").table(Objects::Table).col(Objects::Context).to_owned()) + .await?; + + + + manager + .create_table( + Table::create() + .table(Instances::Table) + .comment("known other instances in the fediverse") + .col( + ColumnDef::new(Instances::Id) + .integer() + .not_null() + .auto_increment() + .primary_key() + ) + .col(ColumnDef::new(Instances::Name).string().null()) + .col(ColumnDef::new(Instances::Domain).string().not_null()) + .col(ColumnDef::new(Instances::Software).string().null()) + .col(ColumnDef::new(Instances::Version).string().null()) + .col(ColumnDef::new(Instances::DownSince).date_time().null()) + .col(ColumnDef::new(Instances::Users).integer().null()) + .col(ColumnDef::new(Instances::Posts).integer().null()) + .col(ColumnDef::new(Instances::Published).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Instances::Updated).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-instances-domain").table(Instances::Table).col(Instances::Domain).to_owned()) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Actors::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-actors-ap-id").table(Actors::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-actors-preferred-username").table(Actors::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-actors-instance").table(Actors::Table).to_owned()) + .await?; + + + manager + .drop_table(Table::drop().table(Activities::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-activities-ap-id").table(Activities::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-activities-actor").table(Activities::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("activities-object-index").table(Activities::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-activities-published-descending").table(Activities::Table).to_owned()) + .await?; + + + manager + .drop_table(Table::drop().table(Objects::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-objects-ap-id").table(Objects::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-objects-attributed-to").table(Objects::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-objects-in-reply-to").table(Objects::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-objects-content-text").table(Objects::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-objects-context").table(Objects::Table).to_owned()) + .await?; + + + manager + .drop_table(Table::drop().table(Instances::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-instances-domain").table(Instances::Table).to_owned()) + .await?; + + Ok(()) + } +} diff --git a/src/migrations/m20240524_000002_create_relations_likes_shares.rs b/src/migrations/m20240524_000002_create_relations_likes_shares.rs new file mode 100644 index 0000000..1893ba9 --- /dev/null +++ b/src/migrations/m20240524_000002_create_relations_likes_shares.rs @@ -0,0 +1,239 @@ +use sea_orm_migration::prelude::*; + +use super::m20240524_000001_create_actor_activity_object_tables::{Activities, Actors, Objects}; + +#[derive(DeriveIden)] +pub enum Relations { + Table, + Id, + Follower, + Following, + Activity, + Accept, +} + +#[derive(DeriveIden)] +#[allow(clippy::enum_variant_names)] +pub enum Likes { + Table, + Id, + Actor, + Likes, + Published, +} + +#[derive(DeriveIden)] +#[allow(clippy::enum_variant_names)] +pub enum Announces { + Table, + Id, + Actor, + Announces, + Published, +} + +#[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(Relations::Table) + .comment("follow relations between actors (applications too! for relays)") + .col( + ColumnDef::new(Relations::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Relations::Follower).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-relations-follower") + .from(Relations::Table, Relations::Follower) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Relations::Following).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-relations-following") + .from(Relations::Table, Relations::Following) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Relations::Accept).integer().null()) + .foreign_key( + ForeignKey::create() + .name("fkey-relations-accept") + .from(Relations::Table, Relations::Accept) + .to(Activities::Table, Activities::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Relations::Activity).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-relations-activity") + .from(Relations::Table, Relations::Activity) + .to(Activities::Table, Activities::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-relations-follower").table(Relations::Table).col(Relations::Follower).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-relations-following").table(Relations::Table).col(Relations::Following).to_owned()) + .await?; + + manager + .create_table( + Table::create() + .table(Likes::Table) + .comment("all like events, joining actor to object") + .col( + ColumnDef::new(Likes::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Likes::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-likes-actor") + .from(Likes::Table, Likes::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Likes::Likes).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-likes-likes") + .from(Likes::Table, Likes::Likes) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Likes::Published).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-likes-actor").table(Likes::Table).col(Likes::Actor).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-likes-likes").table(Likes::Table).col(Likes::Likes).to_owned()) + .await?; + + manager + .create_index( + Index::create() + .unique() + .name("index-likes-actor-likes") + .table(Likes::Table) + .col(Likes::Actor) + .col(Likes::Likes) + .to_owned() + ).await?; + + manager + .create_table( + Table::create() + .table(Announces::Table) + .comment("all share/boost/reblog events, joining actor to object") + .col( + ColumnDef::new(Announces::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Announces::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-announces-actor") + .from(Announces::Table, Announces::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Announces::Announces).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-announces-announces") + .from(Announces::Table, Announces::Announces) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Announces::Published).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-announces-actor").table(Announces::Table).col(Announces::Actor).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-announces-announces").table(Announces::Table).col(Announces::Announces).to_owned()) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Relations::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-relations-follower").table(Relations::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-relations-following").table(Relations::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Likes::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-likes-actor").table(Likes::Table).to_owned()) + .await?; + manager + .drop_index(Index::drop().name("index-likes-likes").table(Likes::Table).to_owned()) + .await?; + manager + .drop_index(Index::drop().name("index-likes-actor-likes").table(Likes::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Announces::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("shares-actor-index").table(Announces::Table).to_owned()) + .await?; + manager + .drop_index(Index::drop().name("shares-shares-index").table(Announces::Table).to_owned()) + .await?; + + Ok(()) + } +} diff --git a/src/migrations/m20240524_000003_create_users_auth_and_config.rs b/src/migrations/m20240524_000003_create_users_auth_and_config.rs new file mode 100644 index 0000000..bd83a33 --- /dev/null +++ b/src/migrations/m20240524_000003_create_users_auth_and_config.rs @@ -0,0 +1,167 @@ +use sea_orm_migration::prelude::*; + +use super::m20240524_000001_create_actor_activity_object_tables::Actors; + +#[derive(DeriveIden)] +pub enum Configs { + Table, + Id, + Actor, + AcceptFollowRequests, + ShowFollowersCount, + ShowFollowingCount, + ShowFollowers, + ShowFollowing, +} + +#[derive(DeriveIden)] +pub enum Credentials { + Table, + Id, + Actor, + Login, + Password, +} + +#[derive(DeriveIden)] +pub enum Sessions { + Table, + Id, + Actor, + Secret, + Expires, +} + +#[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(Configs::Table) + .comment("configuration for each local user") + .col( + ColumnDef::new(Configs::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Configs::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-config-actor") + .from(Configs::Table, Configs::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Configs::AcceptFollowRequests).boolean().not_null()) + .col(ColumnDef::new(Configs::ShowFollowersCount).boolean().not_null()) + .col(ColumnDef::new(Configs::ShowFollowingCount).boolean().not_null()) + .col(ColumnDef::new(Configs::ShowFollowers).boolean().not_null()) + .col(ColumnDef::new(Configs::ShowFollowing).boolean().not_null()) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-configs-actor").table(Configs::Table).col(Configs::Actor).to_owned()) + .await?; + + manager + .create_table( + Table::create() + .table(Credentials::Table) + .comment("simple login credentials to authenticate local users") + .col( + ColumnDef::new(Credentials::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Credentials::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-credentials-actor") + .from(Credentials::Table, Credentials::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Credentials::Login).string().not_null()) + .col(ColumnDef::new(Credentials::Password).string().not_null()) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-credentials-login").table(Credentials::Table).col(Credentials::Login).to_owned()) + .await?; + + manager + .create_table( + Table::create() + .table(Sessions::Table) + .comment("authenticated sessions from local users") + .col( + ColumnDef::new(Sessions::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Sessions::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-sessions-actor") + .from(Sessions::Table, Sessions::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Sessions::Secret).string().not_null()) + .col(ColumnDef::new(Sessions::Expires).date_time().not_null()) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-sessions-secret").table(Sessions::Table).col(Sessions::Secret).to_owned()) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Configs::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-configs-actor").table(Configs::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Credentials::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-credentials-login").table(Credentials::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Sessions::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-sessions-secret").table(Sessions::Table).to_owned()) + .await?; + + Ok(()) + } +} diff --git a/src/migrations/m20240524_000004_create_addressing_deliveries.rs b/src/migrations/m20240524_000004_create_addressing_deliveries.rs new file mode 100644 index 0000000..5aecd0a --- /dev/null +++ b/src/migrations/m20240524_000004_create_addressing_deliveries.rs @@ -0,0 +1,195 @@ +use sea_orm_migration::prelude::*; + +use super::m20240524_000001_create_actor_activity_object_tables::{Activities, Actors, Instances, Objects}; + +#[derive(DeriveIden)] +pub enum Addressing { + Table, + Id, + Actor, + Instance, + Activity, + Object, + Published, +} + +#[derive(DeriveIden)] +pub enum Deliveries { + Table, + Id, + Actor, + Target, + Activity, + Created, + NotBefore, + Attempt, +} + +#[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(Addressing::Table) + .comment("this join table contains all addressing relations, serving effectively as permissions truth table") + .col( + ColumnDef::new(Addressing::Id) + .integer() + .not_null() + .auto_increment() + .primary_key() + ) + .col(ColumnDef::new(Addressing::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-addressing-actor") + .from(Addressing::Table, Addressing::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Addressing::Instance).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-addressing-instance") + .from(Addressing::Table, Addressing::Instance) + .to(Instances::Table, Instances::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Addressing::Activity).integer().null()) + .foreign_key( + ForeignKey::create() + .name("fkey-addressing-activity") + .from(Addressing::Table, Addressing::Activity) + .to(Activities::Table, Activities::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Addressing::Object).string().null()) + .foreign_key( + ForeignKey::create() + .name("fkey-addressing-object") + .from(Addressing::Table, Addressing::Object) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Addressing::Published).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index( + Index::create() + .name("index-addressing-actor-published") + .table(Addressing::Table) + .col(Addressing::Actor) + .col(Addressing::Published) + .to_owned() + ) + .await?; + + manager + .create_index( + Index::create() + .name("index-addressing-instance-published") + .table(Addressing::Table) + .col(Addressing::Instance) + .col(Addressing::Published) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-addressing-activity").table(Addressing::Table).col(Addressing::Activity).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-addressing-object").table(Addressing::Table).col(Addressing::Object).to_owned()) + .await?; + + manager + .create_table( + Table::create() + .table(Deliveries::Table) + .comment("this table contains all enqueued outgoing delivery jobs") + .col( + ColumnDef::new(Deliveries::Id) + .integer() + .not_null() + .auto_increment() + .primary_key() + ) + .col(ColumnDef::new(Deliveries::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-deliveries-actor") + .from(Deliveries::Table, Deliveries::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Deliveries::Target).string().not_null()) + .col(ColumnDef::new(Deliveries::Activity).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-deliveries-activity") + .from(Deliveries::Table, Deliveries::Activity) + .to(Activities::Table, Activities::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Deliveries::Created).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Deliveries::NotBefore).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Deliveries::Attempt).integer().not_null().default(0)) + .to_owned() + ) + .await?; + + manager + .create_index( + Index::create() + .name("index-deliveries-not-before") + .table(Deliveries::Table) + .col((Deliveries::NotBefore, IndexOrder::Asc)) + .to_owned() + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Addressing::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-addressing-actor").to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-addressing-server").to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-addressing-activity").to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-addressing-object").to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Deliveries::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-deliveries-not-before").to_owned()) + .await?; + + Ok(()) + } +} diff --git a/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs b/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs new file mode 100644 index 0000000..1e6ed27 --- /dev/null +++ b/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs @@ -0,0 +1,207 @@ +use sea_orm_migration::prelude::*; + +use super::m20240524_000001_create_actor_activity_object_tables::{Actors, Objects}; + +#[derive(DeriveIden)] +pub enum Attachments { + Table, + Id, + DocumentType, + Url, + Object, + Name, + MediaType, + Created, +} + +#[derive(DeriveIden)] +pub enum Mentions { + Table, + Id, + Object, + Actor, + Published, +} + +#[derive(DeriveIden)] +pub enum Hashtags { + Table, + Id, + Object, + Name, + Published, +} + +#[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) + .comment("media attachments related to objects") + .col( + ColumnDef::new(Attachments::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Attachments::Url).string().not_null().unique_key()) + .col(ColumnDef::new(Attachments::Object).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-attachments-object") + .from(Attachments::Table, Attachments::Object) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .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().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-attachment-object").table(Attachments::Table).col(Attachments::Object).to_owned()) + .await?; + + manager + .create_table( + Table::create() + .table(Mentions::Table) + .comment("join table relating posts to mentioned users") + .col( + ColumnDef::new(Mentions::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Mentions::Object).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-mentions-object") + .from(Mentions::Table, Mentions::Object) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Mentions::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-mentions-actor") + .from(Mentions::Table, Mentions::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Mentions::Published).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-mentions-object").table(Mentions::Table).col(Mentions::Object).to_owned()) + .await?; + + manager + .create_index( + Index::create() + .name("index-mentions-actor-published") + .table(Mentions::Table) + .col(Mentions::Actor) + .col((Mentions::Published, IndexOrder::Desc)) + .to_owned() + ) + .await?; + + manager + .create_table( + Table::create() + .table(Hashtags::Table) + .comment("join table relating posts to hashtags") + .col( + ColumnDef::new(Hashtags::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Hashtags::Object).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-hashtags-object") + .from(Hashtags::Table, Hashtags::Object) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + .on_delete(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Hashtags::Name).string().not_null()) + .col(ColumnDef::new(Hashtags::Published).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-hashtags-object").table(Hashtags::Table).col(Hashtags::Object).to_owned()) + .await?; + + manager + .create_index( + Index::create() + .name("index-hashtags-name-published") + .table(Hashtags::Table) + .col(Hashtags::Name) + .col((Hashtags::Published, IndexOrder::Desc)) + .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("index-attachment-object").to_owned()) + .await?; + + + manager + .drop_table(Table::drop().table(Mentions::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-mentions-object").table(Mentions::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-mentions-actor-published").table(Mentions::Table).to_owned()) + .await?; + + + manager + .drop_table(Table::drop().table(Hashtags::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-hashtags-object").table(Hashtags::Table).to_owned()) + .await?; + + manager + .drop_index(Index::drop().name("index-hashtags-name-published").table(Hashtags::Table).to_owned()) + .await?; + + Ok(()) + } +} diff --git a/src/migrations/mod.rs b/src/migrations/mod.rs index 392d0fb..f97d3ec 100644 --- a/src/migrations/mod.rs +++ b/src/migrations/mod.rs @@ -1,21 +1,10 @@ use sea_orm_migration::prelude::*; -mod m20240316_000001_create_table; -mod m20240322_000001_create_relations; -mod m20240322_000002_add_likes_shares; -mod m20240322_000003_add_indexes; -mod m20240323_000001_add_user_configs; -mod m20240323_000002_add_simple_credentials; -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; -mod m20240424_000001_add_sensitive_field; -mod m20240429_000001_add_relays_table; -mod m20240502_000001_add_object_updated; -mod m20240512_000001_add_url_to_objects; -mod m20240520_000001_add_published_to_addressing_actor_index; +mod m20240524_000001_create_actor_activity_object_tables; +mod m20240524_000002_create_relations_likes_shares; +mod m20240524_000003_create_users_auth_and_config; +mod m20240524_000004_create_addressing_deliveries; +mod m20240524_000005_create_attachments_tags_mentions; pub struct Migrator; @@ -23,22 +12,11 @@ pub struct Migrator; impl MigratorTrait for Migrator { fn migrations() -> Vec> { vec![ - Box::new(m20240316_000001_create_table::Migration), - Box::new(m20240322_000001_create_relations::Migration), - Box::new(m20240322_000002_add_likes_shares::Migration), - Box::new(m20240322_000003_add_indexes::Migration), - Box::new(m20240323_000001_add_user_configs::Migration), - Box::new(m20240323_000002_add_simple_credentials::Migration), - Box::new(m20240324_000001_add_addressing::Migration), - 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), - Box::new(m20240424_000001_add_sensitive_field::Migration), - Box::new(m20240429_000001_add_relays_table::Migration), - Box::new(m20240502_000001_add_object_updated::Migration), - Box::new(m20240512_000001_add_url_to_objects::Migration), - Box::new(m20240520_000001_add_published_to_addressing_actor_index::Migration), + Box::new(m20240524_000001_create_actor_activity_object_tables::Migration), + Box::new(m20240524_000002_create_relations_likes_shares::Migration), + Box::new(m20240524_000003_create_users_auth_and_config::Migration), + Box::new(m20240524_000004_create_addressing_deliveries::Migration), + Box::new(m20240524_000005_create_attachments_tags_mentions::Migration), ] } } From ab0e7007c593bad246b09e4253037293f0c9bcd1 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 24 May 2024 03:53:03 +0200 Subject: [PATCH 02/90] fix: oops must first create instances --- ...001_create_actor_activity_object_tables.rs | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs index e0ffd7a..e2b9b99 100644 --- a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs +++ b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs @@ -89,6 +89,37 @@ pub struct Migration; impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Instances::Table) + .comment("known other instances in the fediverse") + .col( + ColumnDef::new(Instances::Id) + .integer() + .not_null() + .auto_increment() + .primary_key() + ) + .col(ColumnDef::new(Instances::Name).string().null()) + .col(ColumnDef::new(Instances::Domain).string().not_null()) + .col(ColumnDef::new(Instances::Software).string().null()) + .col(ColumnDef::new(Instances::Version).string().null()) + .col(ColumnDef::new(Instances::DownSince).date_time().null()) + .col(ColumnDef::new(Instances::Users).integer().null()) + .col(ColumnDef::new(Instances::Posts).integer().null()) + .col(ColumnDef::new(Instances::Published).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Instances::Updated).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ) + .await?; + + manager + .create_index(Index::create().name("index-instances-domain").table(Instances::Table).col(Instances::Domain).to_owned()) + .await?; + + + manager .create_table( Table::create() @@ -264,37 +295,6 @@ impl MigrationTrait for Migration { .create_index(Index::create().name("index-objects-context").table(Objects::Table).col(Objects::Context).to_owned()) .await?; - - - manager - .create_table( - Table::create() - .table(Instances::Table) - .comment("known other instances in the fediverse") - .col( - ColumnDef::new(Instances::Id) - .integer() - .not_null() - .auto_increment() - .primary_key() - ) - .col(ColumnDef::new(Instances::Name).string().null()) - .col(ColumnDef::new(Instances::Domain).string().not_null()) - .col(ColumnDef::new(Instances::Software).string().null()) - .col(ColumnDef::new(Instances::Version).string().null()) - .col(ColumnDef::new(Instances::DownSince).date_time().null()) - .col(ColumnDef::new(Instances::Users).integer().null()) - .col(ColumnDef::new(Instances::Posts).integer().null()) - .col(ColumnDef::new(Instances::Published).date_time().not_null().default(Expr::current_timestamp())) - .col(ColumnDef::new(Instances::Updated).date_time().not_null().default(Expr::current_timestamp())) - .to_owned() - ) - .await?; - - manager - .create_index(Index::create().name("index-instances-domain").table(Instances::Table).col(Instances::Domain).to_owned()) - .await?; - Ok(()) } From e3dc3f9ae70b0f8ccd025e6d503f3fe6494e7d4b Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 24 May 2024 03:55:43 +0200 Subject: [PATCH 03/90] fix: objects before activities --- ...001_create_actor_activity_object_tables.rs | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs index e2b9b99..7b2a791 100644 --- a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs +++ b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs @@ -177,63 +177,6 @@ impl MigrationTrait for Migration { - manager - .create_table( - Table::create() - .table(Activities::Table) - .comment("all activities this instance ever received or generated") - .col( - ColumnDef::new(Activities::Id) - .integer() - .not_null() - .primary_key() - .auto_increment() - ) - .col(ColumnDef::new(Activities::ApId).string().not_null().unique_key()) - .col(ColumnDef::new(Activities::ActivityType).string().not_null()) - .col(ColumnDef::new(Activities::Actor).integer().not_null()) - .foreign_key( - ForeignKey::create() - .name("fkey-activities-actors") - .from(Activities::Table, Activities::Actor) - .to(Actors::Table, Actors::Id) - .on_update(ForeignKeyAction::Cascade) - ) - .col(ColumnDef::new(Activities::Object).integer().null()) - .foreign_key( - ForeignKey::create() - .name("fkey-activities-objects") - .from(Activities::Table, Activities::Object) - .to(Objects::Table, Objects::Id) - .on_update(ForeignKeyAction::Cascade) - ) - .col(ColumnDef::new(Activities::Target).string().null()) - .col(ColumnDef::new(Activities::To).json().null()) - .col(ColumnDef::new(Activities::Bto).json().null()) - .col(ColumnDef::new(Activities::Cc).json().null()) - .col(ColumnDef::new(Activities::Bcc).json().null()) - .col(ColumnDef::new(Activities::Published).date_time().not_null().default(Expr::current_timestamp())) - .to_owned() - ).await?; - - manager - .create_index(Index::create().unique().name("index-activities-ap-id").table(Activities::Table).col(Activities::ApId).to_owned()) - .await?; - - manager - .create_index(Index::create().name("index-activities-actor").table(Activities::Table).col(Activities::Actor).to_owned()) - .await?; - - manager - .create_index(Index::create().name("activities-object-index").table(Activities::Table).col(Activities::Object).to_owned()) - .await?; - - manager - .create_index(Index::create().name("index-activities-published-descending").table(Activities::Table).col((Activities::Published, IndexOrder::Desc)).to_owned()) - .await?; - - - manager .create_table( Table::create() @@ -295,6 +238,63 @@ impl MigrationTrait for Migration { .create_index(Index::create().name("index-objects-context").table(Objects::Table).col(Objects::Context).to_owned()) .await?; + + + manager + .create_table( + Table::create() + .table(Activities::Table) + .comment("all activities this instance ever received or generated") + .col( + ColumnDef::new(Activities::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Activities::ApId).string().not_null().unique_key()) + .col(ColumnDef::new(Activities::ActivityType).string().not_null()) + .col(ColumnDef::new(Activities::Actor).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fkey-activities-actors") + .from(Activities::Table, Activities::Actor) + .to(Actors::Table, Actors::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Activities::Object).integer().null()) + .foreign_key( + ForeignKey::create() + .name("fkey-activities-objects") + .from(Activities::Table, Activities::Object) + .to(Objects::Table, Objects::Id) + .on_update(ForeignKeyAction::Cascade) + ) + .col(ColumnDef::new(Activities::Target).string().null()) + .col(ColumnDef::new(Activities::To).json().null()) + .col(ColumnDef::new(Activities::Bto).json().null()) + .col(ColumnDef::new(Activities::Cc).json().null()) + .col(ColumnDef::new(Activities::Bcc).json().null()) + .col(ColumnDef::new(Activities::Published).date_time().not_null().default(Expr::current_timestamp())) + .to_owned() + ).await?; + + manager + .create_index(Index::create().unique().name("index-activities-ap-id").table(Activities::Table).col(Activities::ApId).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-activities-actor").table(Activities::Table).col(Activities::Actor).to_owned()) + .await?; + + manager + .create_index(Index::create().name("activities-object-index").table(Activities::Table).col(Activities::Object).to_owned()) + .await?; + + manager + .create_index(Index::create().name("index-activities-published-descending").table(Activities::Table).col((Activities::Published, IndexOrder::Desc)).to_owned()) + .await?; + Ok(()) } From 749dcc1ae7064b0c32562de36e994e717d500440 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 24 May 2024 04:13:19 +0200 Subject: [PATCH 04/90] fix: oops slipped --- src/migrations/m20240524_000004_create_addressing_deliveries.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/migrations/m20240524_000004_create_addressing_deliveries.rs b/src/migrations/m20240524_000004_create_addressing_deliveries.rs index 5aecd0a..6d7e330 100644 --- a/src/migrations/m20240524_000004_create_addressing_deliveries.rs +++ b/src/migrations/m20240524_000004_create_addressing_deliveries.rs @@ -67,7 +67,7 @@ impl MigrationTrait for Migration { .to(Activities::Table, Activities::Id) .on_update(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Addressing::Object).string().null()) + .col(ColumnDef::new(Addressing::Object).integer().null()) .foreign_key( ForeignKey::create() .name("fkey-addressing-object") From 12c5a6f3a5afe20f97c602c43bf7dd5669fd8dc2 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 24 May 2024 05:05:14 +0200 Subject: [PATCH 05/90] feat: updated entities i think it's all of them? constructors are still weird --- src/model/activity.rs | 152 +++++++++--------- src/model/{user.rs => actor.rs} | 242 +++++++++++++++++------------ src/model/addressing.rs | 68 +++++--- src/model/announce.rs | 45 ++++++ src/model/application.rs | 18 --- src/model/attachment.rs | 47 +++--- src/model/config.rs | 19 ++- src/model/credential.rs | 19 ++- src/model/delivery.rs | 28 +++- src/model/{share.rs => hashtag.rs} | 18 ++- src/model/instance.rs | 39 +++++ src/model/like.rs | 28 +++- src/model/mention.rs | 45 ++++++ src/model/mod.rs | 21 ++- src/model/object.rs | 233 ++++++++++++++------------- src/model/relation.rs | 44 +++++- src/model/relay.rs | 16 -- src/model/session.rs | 17 +- 18 files changed, 678 insertions(+), 421 deletions(-) rename src/model/{user.rs => actor.rs} (50%) create mode 100644 src/model/announce.rs delete mode 100644 src/model/application.rs rename src/model/{share.rs => hashtag.rs} (65%) create mode 100644 src/model/instance.rs create mode 100644 src/model/mention.rs delete mode 100644 src/model/relay.rs diff --git a/src/model/activity.rs b/src/model/activity.rs index 3b48cef..351d2f4 100644 --- a/src/model/activity.rs +++ b/src/model/activity.rs @@ -1,46 +1,95 @@ -use apb::{ActivityMut, BaseMut, ObjectMut}; +use apb::{ActivityMut, ActivityType, BaseMut, ObjectMut}; use sea_orm::entity::prelude::*; use crate::routes::activitypub::jsonld::LD; -use super::Audience; - #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "activities")] pub struct Model { #[sea_orm(primary_key)] - pub id: String, - - pub activity_type: apb::ActivityType, - pub actor: String, - pub object: Option, - - pub target: Option, // TODO relates to USER maybe?? - pub cc: Audience, - pub bcc: Audience, - pub to: Audience, - pub bto: Audience, + pub id: i32, + #[sea_orm(unique)] + pub ap_id: String, + pub activity_type: ActivityType, + pub actor: i32, + pub object: Option, + pub target: Option, + pub to: Option, + pub bto: Option, + pub cc: Option, + pub bcc: Option, pub published: ChronoDateTimeUtc, +} - // TODO: origin, result, instrument +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Actors, + #[sea_orm(has_many = "super::addressing::Entity")] + Addressing, + #[sea_orm(has_many = "super::delivery::Entity")] + Deliveries, + #[sea_orm( + belongs_to = "super::object::Entity", + from = "Column::Object", + to = "super::object::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Objects, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Addressing.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Deliveries.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Objects.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + +impl ActiveModel { + pub fn new(activity: &impl apb::Activity) -> Result { + Ok(ActiveModel { + id: sea_orm::ActiveValue::NotSet, + ap_id: sea_orm::ActiveValue::Set(activity.id().ok_or(super::FieldError("id"))?.to_string()), + activity_type: sea_orm::ActiveValue::Set(activity.activity_type().ok_or(super::FieldError("type"))?), + actor: sea_orm::ActiveValue::Set(activity.actor().id().ok_or(super::FieldError("actor"))?), + object: sea_orm::ActiveValue::Set(activity.object().id()), + target: sea_orm::ActiveValue::Set(activity.target().id()), + published: sea_orm::ActiveValue::Set(activity.published().unwrap_or(chrono::Utc::now())), + to: sea_orm::ActiveValue::Set(activity.to().into()), + bto: sea_orm::ActiveValue::Set(activity.bto().into()), + cc: sea_orm::ActiveValue::Set(activity.cc().into()), + bcc: sea_orm::ActiveValue::Set(activity.bcc().into()), + }) + } } impl Model { - pub fn new(activity: &impl apb::Activity) -> Result { - Ok(Model { - id: activity.id().ok_or(super::FieldError("id"))?.to_string(), - activity_type: activity.activity_type().ok_or(super::FieldError("type"))?, - actor: activity.actor().id().ok_or(super::FieldError("actor"))?, - object: activity.object().id(), - target: activity.target().id(), - published: activity.published().unwrap_or(chrono::Utc::now()), - to: activity.to().into(), - bto: activity.bto().into(), - cc: activity.cc().into(), - bcc: activity.bcc().into(), - }) - } - pub fn ap(self) -> serde_json::Value { serde_json::Value::new_object() .set_id(Some(&self.id)) @@ -56,49 +105,6 @@ impl Model { } } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::user::Entity", - from = "Column::Actor", - to = "super::user::Column::Id" - )] - User, - - #[sea_orm( - belongs_to = "super::object::Entity", - from = "Column::Object", - to = "super::object::Column::Id" - )] - Object, - - #[sea_orm(has_many = "super::addressing::Entity")] - Addressing, - - #[sea_orm(has_many = "super::delivery::Entity")] - Delivery, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::User.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Object.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Addressing.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} - impl apb::target::Addressed for Model { fn addressed(&self) -> Vec { let mut to : Vec = self.to.0.clone(); diff --git a/src/model/user.rs b/src/model/actor.rs similarity index 50% rename from src/model/user.rs rename to src/model/actor.rs index 9aaf341..66de0a1 100644 --- a/src/model/user.rs +++ b/src/model/actor.rs @@ -5,68 +5,165 @@ use apb::{Actor, ActorMut, ActorType, BaseMut, DocumentMut, Endpoints, Endpoints use crate::routes::activitypub::jsonld::LD; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "users")] +#[sea_orm(table_name = "actors")] pub struct Model { #[sea_orm(primary_key)] - pub id: String, - pub domain: String, + pub id: i32, + #[sea_orm(unique)] + pub ap_id: String, pub actor_type: ActorType, - pub preferred_username: String, - + pub instance: i32, pub name: Option, pub summary: Option, pub image: Option, pub icon: Option, - + #[sea_orm(unique)] + pub preferred_username: String, pub inbox: Option, pub shared_inbox: Option, pub outbox: Option, pub following: Option, pub followers: Option, - - pub following_count: i64, - pub followers_count: i64, - pub statuses_count: i64, - + pub following_count: i32, + pub followers_count: i32, + pub statuses_count: i32, pub public_key: String, pub private_key: Option, - pub created: ChronoDateTimeUtc, pub updated: ChronoDateTimeUtc, - - // TODO these are also suggested - // pub liked: Option, - // pub streams: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::activity::Entity")] + Activities, + #[sea_orm(has_many = "super::addressing::Entity")] + Addressing, + #[sea_orm(has_many = "super::announce::Entity")] + Announces, + #[sea_orm(has_many = "super::config::Entity")] + Configs, + #[sea_orm(has_many = "super::credential::Entity")] + Credentials, + #[sea_orm(has_many = "super::delivery::Entity")] + Deliveries, + #[sea_orm( + belongs_to = "super::instance::Entity", + from = "Column::Instance", + to = "super::instance::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Instances, + #[sea_orm(has_many = "super::like::Entity")] + Likes, + #[sea_orm(has_many = "super::mention::Entity")] + Mentions, + #[sea_orm(has_many = "super::object::Entity")] + Objects, + #[sea_orm(has_many = "super::session::Entity")] + Sessions, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Activities.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Addressing.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Announces.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Configs.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Credentials.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Deliveries.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Instances.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Likes.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Mentions.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Objects.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Sessions.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + +impl ActiveModel { + pub fn new(object: &impl Actor, instance: i32) -> Result { + let ap_id = object.id().ok_or(super::FieldError("id"))?.to_string(); + let (_domain, fallback_preferred_username) = split_user_id(&ap_id); + Ok(ActiveModel { + instance: sea_orm::ActiveValue::Set(instance), // TODO receiving it from outside is cheap + id: sea_orm::ActiveValue::NotSet, + ap_id: sea_orm::ActiveValue::Set(ap_id), + preferred_username: sea_orm::ActiveValue::Set(object.preferred_username().unwrap_or(&fallback_preferred_username).to_string()), + actor_type: sea_orm::ActiveValue::Set(object.actor_type().ok_or(super::FieldError("type"))?), + name: sea_orm::ActiveValue::Set(object.name().map(|x| x.to_string())), + summary: sea_orm::ActiveValue::Set(object.summary().map(|x| x.to_string())), + icon: sea_orm::ActiveValue::Set(object.icon().get().and_then(|x| x.url().id())), + image: sea_orm::ActiveValue::Set(object.image().get().and_then(|x| x.url().id())), + inbox: sea_orm::ActiveValue::Set(object.inbox().id()), + outbox: sea_orm::ActiveValue::Set(object.outbox().id()), + shared_inbox: sea_orm::ActiveValue::Set(object.endpoints().get().and_then(|x| Some(x.shared_inbox()?.to_string()))), + followers: sea_orm::ActiveValue::Set(object.followers().id()), + following: sea_orm::ActiveValue::Set(object.following().id()), + created: sea_orm::ActiveValue::Set(object.published().unwrap_or(chrono::Utc::now())), + updated: sea_orm::ActiveValue::Set(chrono::Utc::now()), + following_count: sea_orm::ActiveValue::Set(object.following_count().unwrap_or(0) as i64), + followers_count: sea_orm::ActiveValue::Set(object.followers_count().unwrap_or(0) as i64), + statuses_count: sea_orm::ActiveValue::Set(object.statuses_count().unwrap_or(0) as i64), + public_key: sea_orm::ActiveValue::Set(object.public_key().get().ok_or(super::FieldError("publicKey"))?.public_key_pem().to_string()), + private_key: sea_orm::ActiveValue::Set(None), // there's no way to transport privkey over AP json, must come from DB + }) + } } impl Model { - pub fn new(object: &impl Actor) -> Result { - let ap_id = object.id().ok_or(super::FieldError("id"))?.to_string(); - let (domain, fallback_preferred_username) = split_user_id(&ap_id); - Ok(Model { - id: ap_id, - domain, - preferred_username: object.preferred_username().unwrap_or(&fallback_preferred_username).to_string(), - actor_type: object.actor_type().ok_or(super::FieldError("type"))?, - name: object.name().map(|x| x.to_string()), - summary: object.summary().map(|x| x.to_string()), - icon: object.icon().get().and_then(|x| x.url().id()), - image: object.image().get().and_then(|x| x.url().id()), - inbox: object.inbox().id(), - outbox: object.outbox().id(), - shared_inbox: object.endpoints().get().and_then(|x| Some(x.shared_inbox()?.to_string())), - followers: object.followers().id(), - following: object.following().id(), - created: object.published().unwrap_or(chrono::Utc::now()), - updated: chrono::Utc::now(), - following_count: object.following_count().unwrap_or(0) as i64, - followers_count: object.followers_count().unwrap_or(0) as i64, - statuses_count: object.statuses_count().unwrap_or(0) as i64, - public_key: object.public_key().get().ok_or(super::FieldError("publicKey"))?.public_key_pem().to_string(), - private_key: None, // there's no way to transport privkey over AP json, must come from DB - }) - } - pub fn ap(self) -> serde_json::Value { serde_json::Value::new_object() .set_id(Some(&self.id)) @@ -106,65 +203,6 @@ impl Model { } } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::activity::Entity")] - Activity, - - #[sea_orm(has_many = "super::object::Entity")] - Object, - - #[sea_orm(has_one = "super::config::Entity")] - Config, - - #[sea_orm(has_one = "super::credential::Entity")] - Credential, - - #[sea_orm(has_many = "super::session::Entity")] - Session, - - #[sea_orm(has_many = "super::addressing::Entity")] - Addressing, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Activity.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Object.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Config.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Credential.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Session.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Addressing.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} - fn split_user_id(id: &str) -> (String, String) { let clean = id .replace("http://", "") diff --git a/src/model/addressing.rs b/src/model/addressing.rs index fe85556..1e308d4 100644 --- a/src/model/addressing.rs +++ b/src/model/addressing.rs @@ -7,53 +7,71 @@ use crate::routes::activitypub::jsonld::LD; #[sea_orm(table_name = "addressing")] pub struct Model { #[sea_orm(primary_key)] - pub id: i64, - pub actor: String, - pub server: String, - pub activity: Option, - pub object: Option, + pub id: i32, + pub actor: i32, + pub instance: i32, + pub activity: Option, + pub object: Option, pub published: ChronoDateTimeUtc, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm( - belongs_to = "super::user::Entity", - from = "Column::Actor", - to = "super::user::Column::Id" - )] - User, - #[sea_orm( belongs_to = "super::activity::Entity", from = "Column::Activity", - to = "super::activity::Column::Id" + to = "super::activity::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" )] - Activity, - + Activities, + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Actors, + #[sea_orm( + belongs_to = "super::instance::Entity", + from = "Column::Instance", + to = "super::instance::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Instances, #[sea_orm( belongs_to = "super::object::Entity", from = "Column::Object", - to = "super::object::Column::Id" + to = "super::object::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" )] - Object, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::User.def() - } + Objects, } impl Related for Entity { fn to() -> RelationDef { - Relation::Activity.def() + Relation::Activities.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Instances.def() } } impl Related for Entity { fn to() -> RelationDef { - Relation::Object.def() + Relation::Objects.def() } } diff --git a/src/model/announce.rs b/src/model/announce.rs new file mode 100644 index 0000000..fc7a404 --- /dev/null +++ b/src/model/announce.rs @@ -0,0 +1,45 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "announces")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub actor: i32, + pub announces: i32, + pub published: ChronoDateTimeUtc, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Actors, + #[sea_orm( + belongs_to = "super::object::Entity", + from = "Column::Announces", + to = "super::object::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Objects, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Objects.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/application.rs b/src/model/application.rs deleted file mode 100644 index cde98aa..0000000 --- a/src/model/application.rs +++ /dev/null @@ -1,18 +0,0 @@ -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "application")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i64, - - pub private_key: String, - pub public_key: String, - - pub created: ChronoDateTimeUtc, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/attachment.rs b/src/model/attachment.rs index 1a11796..c7c8914 100644 --- a/src/model/attachment.rs +++ b/src/model/attachment.rs @@ -9,16 +9,36 @@ use super::addressing::Event; #[sea_orm(table_name = "attachments")] pub struct Model { #[sea_orm(primary_key)] - pub id: i64, - + pub id: i32, + #[sea_orm(unique)] pub url: String, - pub object: String, - pub document_type: apb::DocumentType, + pub object: i32, + pub document_type: String, pub name: Option, pub media_type: String, pub created: ChronoDateTimeUtc, } +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::object::Entity", + from = "Column::Object", + to = "super::object::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Objects, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Objects.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + impl Model { pub fn ap(self) -> serde_json::Value { serde_json::Value::new_object() @@ -30,25 +50,6 @@ impl Model { } } -#[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 {} - - #[axum::async_trait] pub trait BatchFillable { async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr>; diff --git a/src/model/config.rs b/src/model/config.rs index b34df1d..f95bf43 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -4,7 +4,8 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "configs")] pub struct Model { #[sea_orm(primary_key)] - pub id: String, + pub id: i32, + pub actor: i32, pub accept_follow_requests: bool, pub show_followers_count: bool, pub show_following_count: bool, @@ -15,7 +16,7 @@ pub struct Model { impl Default for Model { fn default() -> Self { Model { - id: "".to_string(), + id: 0, actor: 0, accept_follow_requests: true, show_following_count: true, show_following: true, @@ -28,16 +29,18 @@ impl Default for Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::user::Entity", - from = "Column::Id", - to = "super::user::Column::Id" + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" )] - User, + Actors, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::User.def() + Relation::Actors.def() } } diff --git a/src/model/credential.rs b/src/model/credential.rs index f3cdc5f..5255ead 100644 --- a/src/model/credential.rs +++ b/src/model/credential.rs @@ -4,24 +4,27 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "credentials")] pub struct Model { #[sea_orm(primary_key)] - pub id: String, - pub email: String, + pub id: i32, + pub actor: i32, + pub login: String, pub password: String, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::user::Entity", - from = "Column::Id", - to = "super::user::Column::Id" + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" )] - User, + Actors, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::User.def() + Relation::Actors.def() } } diff --git a/src/model/delivery.rs b/src/model/delivery.rs index ef71261..6835f4d 100644 --- a/src/model/delivery.rs +++ b/src/model/delivery.rs @@ -4,10 +4,10 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "deliveries")] pub struct Model { #[sea_orm(primary_key)] - pub id: i64, - pub actor: String, + pub id: i32, + pub actor: i32, pub target: String, - pub activity: String, + pub activity: i32, pub created: ChronoDateTimeUtc, pub not_before: ChronoDateTimeUtc, pub attempt: i32, @@ -18,14 +18,30 @@ pub enum Relation { #[sea_orm( belongs_to = "super::activity::Entity", from = "Column::Activity", - to = "super::activity::Column::Id" + to = "super::activity::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" )] - Activity, + Activities, + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Actors, } impl Related for Entity { fn to() -> RelationDef { - Relation::Activity.def() + Relation::Activities.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() } } diff --git a/src/model/share.rs b/src/model/hashtag.rs similarity index 65% rename from src/model/share.rs rename to src/model/hashtag.rs index e677cf9..d17b1d3 100644 --- a/src/model/share.rs +++ b/src/model/hashtag.rs @@ -1,28 +1,30 @@ use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "shares")] +#[sea_orm(table_name = "hashtags")] pub struct Model { #[sea_orm(primary_key)] - pub id: i64, - pub actor: String, - pub shares: String, - pub date: ChronoDateTimeUtc, + pub id: i32, + pub object: i32, + pub name: String, + pub published: ChronoDateTimeUtc, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( belongs_to = "super::object::Entity", - from = "Column::Shares", + from = "Column::Object", to = "super::object::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" )] - Object + Objects, } impl Related for Entity { fn to() -> RelationDef { - Relation::Object.def() + Relation::Objects.def() } } diff --git a/src/model/instance.rs b/src/model/instance.rs new file mode 100644 index 0000000..125fc1f --- /dev/null +++ b/src/model/instance.rs @@ -0,0 +1,39 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "instances")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: Option, + pub domain: String, + pub software: Option, + pub version: Option, + pub down_since: Option, + pub users: Option, + pub posts: Option, + pub published: ChronoDateTimeUtc, + pub updated: ChronoDateTimeUtc, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::actor::Entity")] + Actors, + #[sea_orm(has_many = "super::addressing::Entity")] + Addressing, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Addressing.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/like.rs b/src/model/like.rs index 2e072f6..66456fc 100644 --- a/src/model/like.rs +++ b/src/model/like.rs @@ -4,25 +4,41 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "likes")] pub struct Model { #[sea_orm(primary_key)] - pub id: i64, - pub actor: String, - pub likes: String, - pub date: ChronoDateTimeUtc, + pub id: i32, + pub actor: i32, + pub likes: i32, + pub published: ChronoDateTimeUtc, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Actors, #[sea_orm( belongs_to = "super::object::Entity", from = "Column::Likes", to = "super::object::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" )] - Object + Objects, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() + } } impl Related for Entity { fn to() -> RelationDef { - Relation::Object.def() + Relation::Objects.def() } } diff --git a/src/model/mention.rs b/src/model/mention.rs new file mode 100644 index 0000000..139d00c --- /dev/null +++ b/src/model/mention.rs @@ -0,0 +1,45 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "mentions")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub object: i32, + pub actor: i32, + pub published: ChronoDateTimeUtc, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Actor", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Actors, + #[sea_orm( + belongs_to = "super::object::Entity", + from = "Column::Object", + to = "super::object::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Objects, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Objects.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/mod.rs b/src/model/mod.rs index 04bdb6d..dc5fcf8 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -1,18 +1,23 @@ +pub mod actor; pub mod object; pub mod activity; -pub mod user; -pub mod config; -pub mod relay; -pub mod relation; -pub mod addressing; -pub mod share; -pub mod like; +pub mod config; pub mod credential; pub mod session; + +pub mod instance; pub mod delivery; + +pub mod relation; +pub mod announce; +pub mod like; + +pub mod hashtag; +pub mod mention; pub mod attachment; -pub mod application; + +pub mod addressing; #[derive(Debug, Clone, thiserror::Error)] #[error("missing required field: '{0}'")] diff --git a/src/model/object.rs b/src/model/object.rs index 82387a8..a56257b 100644 --- a/src/model/object.rs +++ b/src/model/object.rs @@ -9,57 +9,137 @@ use super::Audience; #[sea_orm(table_name = "objects")] pub struct Model { #[sea_orm(primary_key)] - pub id: String, - pub object_type: apb::ObjectType, - pub attributed_to: Option, + pub id: i32, + #[sea_orm(unique)] + pub ap_id: String, + pub object_type: String, + pub attributed_to: Option, pub name: Option, pub summary: Option, pub content: Option, - pub likes: i64, - pub shares: i64, - pub comments: i64, - pub context: Option, - pub in_reply_to: Option, - pub cc: Audience, - pub bcc: Audience, - pub to: Audience, - pub bto: Audience, - pub url: Option, - pub published: ChronoDateTimeUtc, - pub updated: Option, - pub sensitive: bool, + pub in_reply_to: Option, + pub url: Option, + pub likes: i32, + pub announces: i32, + pub replies: i32, + pub context: Option, + pub to: Option, + pub bto: Option, + pub cc: Option, + pub bcc: Option, + pub published: ChronoDateTimeUtc, + pub updated: ChronoDateTimeUtc, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::activity::Entity")] + Activities, + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::AttributedTo", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Actors, + #[sea_orm(has_many = "super::addressing::Entity")] + Addressing, + #[sea_orm(has_many = "super::announce::Entity")] + Announces, + #[sea_orm(has_many = "super::attachment::Entity")] + Attachments, + #[sea_orm(has_many = "super::hashtag::Entity")] + Hashtags, + #[sea_orm(has_many = "super::like::Entity")] + Likes, + #[sea_orm(has_many = "super::mention::Entity")] + Mentions, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Activities.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Actors.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Addressing.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Announces.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Attachments.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Hashtags.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Likes.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Mentions.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} + +impl ActiveModel { + pub fn new(object: &impl apb::Object) -> Result { + Ok(ActiveModel { + id: sea_orm::ActiveValue::NotSet, + ap_id: sea_orm::ActiveValue::Set(object.id().ok_or(super::FieldError("id"))?.to_string()), + object_type: sea_orm::ActiveValue::Set(object.object_type().ok_or(super::FieldError("type"))?), + attributed_to: sea_orm::ActiveValue::Set(object.attributed_to().id()), + name: sea_orm::ActiveValue::Set(object.name().map(|x| x.to_string())), + summary: sea_orm::ActiveValue::Set(object.summary().map(|x| x.to_string())), + content: sea_orm::ActiveValue::Set(object.content().map(|x| x.to_string())), + context: sea_orm::ActiveValue::Set(object.context().id()), + in_reply_to: sea_orm::ActiveValue::Set(object.in_reply_to().id()), + published: sea_orm::ActiveValue::Set(object.published().ok_or(super::FieldError("published"))?), + updated: sea_orm::ActiveValue::Set(object.updated()), + url: sea_orm::ActiveValue::Set(object.url().id()), + replies: sea_orm::ActiveValue::Set(object.replies().get() + .map_or(0, |x| x.total_items().unwrap_or(0)) as i64), + likes: sea_orm::ActiveValue::Set(object.likes().get() + .map_or(0, |x| x.total_items().unwrap_or(0)) as i64), + announces: sea_orm::ActiveValue::Set(object.shares().get() + .map_or(0, |x| x.total_items().unwrap_or(0)) as i64), + to: sea_orm::ActiveValue::Set(object.to().into()), + bto: sea_orm::ActiveValue::Set(object.bto().into()), + cc: sea_orm::ActiveValue::Set(object.cc().into()), + bcc: sea_orm::ActiveValue::Set(object.bcc().into()), + + sensitive: sea_orm::ActiveValue::Set(object.sensitive().unwrap_or(false)), + }) + } } impl Model { - pub fn new(object: &impl apb::Object) -> Result { - Ok(Model { - id: object.id().ok_or(super::FieldError("id"))?.to_string(), - object_type: object.object_type().ok_or(super::FieldError("type"))?, - attributed_to: object.attributed_to().id(), - name: object.name().map(|x| x.to_string()), - summary: object.summary().map(|x| x.to_string()), - content: object.content().map(|x| x.to_string()), - context: object.context().id(), - in_reply_to: object.in_reply_to().id(), - published: object.published().ok_or(super::FieldError("published"))?, - updated: object.updated(), - url: object.url().id(), - comments: object.replies().get() - .map_or(0, |x| x.total_items().unwrap_or(0)) as i64, - likes: object.likes().get() - .map_or(0, |x| x.total_items().unwrap_or(0)) as i64, - shares: object.shares().get() - .map_or(0, |x| x.total_items().unwrap_or(0)) as i64, - to: object.to().into(), - bto: object.bto().into(), - cc: object.cc().into(), - bcc: object.bcc().into(), - - sensitive: object.sensitive().unwrap_or(false), - }) - } - pub fn ap(self) -> serde_json::Value { serde_json::Value::new_object() .set_id(Some(&self.id)) @@ -72,7 +152,7 @@ impl Model { .set_conversation(apb::Node::maybe_link(self.context.clone())) // duplicate context for mastodon .set_in_reply_to(apb::Node::maybe_link(self.in_reply_to.clone())) .set_published(Some(self.published)) - .set_updated(self.updated) + .set_updated(Some(self.updated)) .set_to(apb::Node::links(self.to.0.clone())) .set_bto(apb::Node::Empty) .set_cc(apb::Node::links(self.cc.0.clone())) @@ -97,69 +177,6 @@ impl Model { } } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::activity::Entity")] - Activity, - - #[sea_orm( - belongs_to = "super::user::Entity", - from = "Column::AttributedTo", - to = "super::user::Column::Id", - )] - User, - - #[sea_orm(has_many = "super::addressing::Entity")] - Addressing, - - #[sea_orm(has_many = "super::attachment::Entity")] - Attachment, - - #[sea_orm(has_many = "super::like::Entity")] - Like, - - #[sea_orm(has_many = "super::share::Entity")] - Share, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Activity.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::User.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Addressing.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Attachment.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Like.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Share.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} - impl apb::target::Addressed for Model { fn addressed(&self) -> Vec { let mut to : Vec = self.to.0.clone(); diff --git a/src/model/relation.rs b/src/model/relation.rs index 41bcc27..44d01df 100644 --- a/src/model/relation.rs +++ b/src/model/relation.rs @@ -4,13 +4,47 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "relations")] pub struct Model { #[sea_orm(primary_key)] - pub id: i64, - pub follower: String, - pub following: String, + pub id: i32, + pub follower: i32, + pub following: i32, + pub accept: Option, + pub activity: i32, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} -// TODO how to represent this User-to-User relation in sea orm?? +pub enum Relation { + #[sea_orm( + belongs_to = "super::activity::Entity", + from = "Column::Accept", + to = "super::activity::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Activities2, + #[sea_orm( + belongs_to = "super::activity::Entity", + from = "Column::Activity", + to = "super::activity::Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Activities1, + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Follower", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Actors2, + #[sea_orm( + belongs_to = "super::actor::Entity", + from = "Column::Following", + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Actors1, +} impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/relay.rs b/src/model/relay.rs deleted file mode 100644 index 0ea3b59..0000000 --- a/src/model/relay.rs +++ /dev/null @@ -1,16 +0,0 @@ -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "relays")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: String, - pub accepted: bool, - pub forwarding: bool, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} -// TODO how to represent this User-to-User relation in sea orm?? - -impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/session.rs b/src/model/session.rs index a2216e2..0e7dfea 100644 --- a/src/model/session.rs +++ b/src/model/session.rs @@ -4,24 +4,27 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "sessions")] pub struct Model { #[sea_orm(primary_key)] - pub id: String, - pub actor: String, + pub id: i32, + pub actor: i32, + pub secret: String, pub expires: ChronoDateTimeUtc, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::user::Entity", + belongs_to = "super::actor::Entity", from = "Column::Actor", - to = "super::user::Column::Id" + to = "super::actor::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" )] - User, + Actors, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::User.def() + Relation::Actors.def() } } From 94ec7d0d375878d986b2d72c6d30d5c70c9775a8 Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 25 May 2024 04:37:17 +0200 Subject: [PATCH 06/90] chore: better id, mix strings and numbers in joins "hot" joins will use internal ids (relations, like/share, addressing) while "slow" relations will use full ap ids (attributed to, context, user configs) --- ...001_create_actor_activity_object_tables.rs | 123 ++++++++++-------- ...24_000002_create_relations_likes_shares.rs | 78 +++++------ ...524_000003_create_users_auth_and_config.rs | 30 +++-- ...524_000004_create_addressing_deliveries.rs | 32 ++--- ...000005_create_attachments_tags_mentions.rs | 50 +++---- src/model/activity.rs | 8 +- src/model/actor.rs | 10 +- src/model/addressing.rs | 18 +-- src/model/announce.rs | 12 +- src/model/attachment.rs | 6 +- src/model/config.rs | 7 +- src/model/credential.rs | 5 +- src/model/delivery.rs | 6 +- src/model/hashtag.rs | 6 +- src/model/instance.rs | 6 +- src/model/like.rs | 12 +- src/model/mention.rs | 8 +- src/model/object.rs | 20 ++- src/model/relation.rs | 26 ++-- src/model/session.rs | 4 +- 20 files changed, 249 insertions(+), 218 deletions(-) diff --git a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs index 7b2a791..7c3439f 100644 --- a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs +++ b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs @@ -2,9 +2,9 @@ use sea_orm_migration::prelude::*; #[derive(DeriveIden)] pub enum Actors { Table, + Internal, Id, - ApId, - Instance, + Domain, ActorType, Name, Summary, @@ -28,8 +28,8 @@ pub enum Actors { #[derive(DeriveIden)] pub enum Activities { Table, + Internal, Id, - ApId, ActivityType, Actor, Object, @@ -44,8 +44,8 @@ pub enum Activities { #[derive(DeriveIden)] pub enum Objects { Table, + Internal, Id, - ApId, ObjectType, AttributedTo, Name, @@ -69,11 +69,12 @@ pub enum Objects { #[derive(DeriveIden)] pub enum Instances { Table, - Id, - Name, + Internal, Domain, + Name, Software, Version, + Icon, DownSince, Users, Posts, @@ -95,16 +96,17 @@ impl MigrationTrait for Migration { .table(Instances::Table) .comment("known other instances in the fediverse") .col( - ColumnDef::new(Instances::Id) - .integer() + ColumnDef::new(Instances::Internal) + .big_integer() .not_null() .auto_increment() .primary_key() ) + .col(ColumnDef::new(Instances::Domain).string().not_null().unique_key()) .col(ColumnDef::new(Instances::Name).string().null()) - .col(ColumnDef::new(Instances::Domain).string().not_null()) .col(ColumnDef::new(Instances::Software).string().null()) .col(ColumnDef::new(Instances::Version).string().null()) + .col(ColumnDef::new(Instances::Icon).string().null()) .col(ColumnDef::new(Instances::DownSince).date_time().null()) .col(ColumnDef::new(Instances::Users).integer().null()) .col(ColumnDef::new(Instances::Posts).integer().null()) @@ -115,7 +117,7 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().name("index-instances-domain").table(Instances::Table).col(Instances::Domain).to_owned()) + .create_index(Index::create().unique().name("index-instances-domain").table(Instances::Table).col(Instances::Domain).to_owned()) .await?; @@ -126,22 +128,22 @@ impl MigrationTrait for Migration { .table(Actors::Table) .comment("main actors table, with users and applications") .col( - ColumnDef::new(Actors::Id) - .integer() + ColumnDef::new(Actors::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Actors::ApId).string().not_null().unique_key()) + .col(ColumnDef::new(Actors::Id).string().not_null().unique_key()) .col(ColumnDef::new(Actors::ActorType).string().not_null()) - .col(ColumnDef::new(Actors::Instance).integer().not_null()) - .foreign_key( - ForeignKey::create() - .name("fkey-actors-instances") - .from(Actors::Table, Actors::Instance) - .to(Instances::Table, Instances::Id) - .on_update(ForeignKeyAction::Cascade) - ) + .col(ColumnDef::new(Actors::Domain).string().not_null()) + // .foreign_key( + // ForeignKey::create() + // .name("fkey-actors-instances") + // .from(Actors::Table, Actors::Domain) + // .to(Instances::Table, Instances::Domain) + // .on_update(ForeignKeyAction::Cascade) + // ) .col(ColumnDef::new(Actors::Name).string().null()) .col(ColumnDef::new(Actors::Summary).string().null()) .col(ColumnDef::new(Actors::Image).string().null()) @@ -164,7 +166,7 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().unique().name("index-actors-ap-id").table(Actors::Table).col(Actors::ApId).to_owned()) + .create_index(Index::create().unique().name("index-actors-id").table(Actors::Table).col(Actors::Id).to_owned()) .await?; manager @@ -183,27 +185,34 @@ impl MigrationTrait for Migration { .table(Objects::Table) .comment("objects are all AP documents which are neither actors nor activities") .col( - ColumnDef::new(Objects::Id) - .integer() + ColumnDef::new(Objects::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Objects::ApId).string().not_null().unique_key()) + .col(ColumnDef::new(Objects::Id).string().not_null().unique_key()) .col(ColumnDef::new(Objects::ObjectType).string().not_null()) - .col(ColumnDef::new(Objects::AttributedTo).integer().null()) - .foreign_key( - ForeignKey::create() - .name("fkey-objects-attributed-to") - .from(Objects::Table, Objects::AttributedTo) - .to(Actors::Table, Actors::Id) - .on_update(ForeignKeyAction::Cascade) - ) + .col(ColumnDef::new(Objects::AttributedTo).string().null()) + // .foreign_key( + // ForeignKey::create() + // .name("fkey-objects-attributed-to") + // .from(Objects::Table, Objects::AttributedTo) + // .to(Actors::Table, Actors::Internal) + // .on_update(ForeignKeyAction::Cascade) + // ) .col(ColumnDef::new(Objects::Name).string().null()) .col(ColumnDef::new(Objects::Summary).string().null()) .col(ColumnDef::new(Objects::Content).string().null()) .col(ColumnDef::new(Objects::Sensitive).boolean().not_null().default(false)) .col(ColumnDef::new(Objects::InReplyTo).string().null()) + // .foreign_key( + // ForeignKey::create() + // .name("fkey-objects-in-reply-to") + // .from(Objects::Table, Objects::InReplyTo) + // .to(Objects::Table, Objects::Id) + // .on_update(ForeignKeyAction::Cascade) + // ) .col(ColumnDef::new(Objects::Url).string().null()) .col(ColumnDef::new(Objects::Likes).integer().not_null().default(0)) .col(ColumnDef::new(Objects::Announces).integer().not_null().default(0)) @@ -219,7 +228,7 @@ impl MigrationTrait for Migration { ).await?; manager - .create_index(Index::create().unique().name("index-objects-ap-id").table(Objects::Table).col(Objects::ApId).to_owned()) + .create_index(Index::create().unique().name("index-objects-id").table(Objects::Table).col(Objects::Id).to_owned()) .await?; manager @@ -246,30 +255,30 @@ impl MigrationTrait for Migration { .table(Activities::Table) .comment("all activities this instance ever received or generated") .col( - ColumnDef::new(Activities::Id) - .integer() + ColumnDef::new(Activities::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Activities::ApId).string().not_null().unique_key()) + .col(ColumnDef::new(Activities::Id).string().not_null().unique_key()) .col(ColumnDef::new(Activities::ActivityType).string().not_null()) - .col(ColumnDef::new(Activities::Actor).integer().not_null()) - .foreign_key( - ForeignKey::create() - .name("fkey-activities-actors") - .from(Activities::Table, Activities::Actor) - .to(Actors::Table, Actors::Id) - .on_update(ForeignKeyAction::Cascade) - ) - .col(ColumnDef::new(Activities::Object).integer().null()) - .foreign_key( - ForeignKey::create() - .name("fkey-activities-objects") - .from(Activities::Table, Activities::Object) - .to(Objects::Table, Objects::Id) - .on_update(ForeignKeyAction::Cascade) - ) + .col(ColumnDef::new(Activities::Actor).string().not_null()) + // .foreign_key( + // ForeignKey::create() + // .name("fkey-activities-actors") + // .from(Activities::Table, Activities::Actor) + // .to(Actors::Table, Actors::Id) + // .on_update(ForeignKeyAction::Cascade) + // ) + .col(ColumnDef::new(Activities::Object).string().null()) + // .foreign_key( + // ForeignKey::create() + // .name("fkey-activities-objects") + // .from(Activities::Table, Activities::Object) + // .to(Objects::Table, Objects::Internal) + // .on_update(ForeignKeyAction::Cascade) + // ) .col(ColumnDef::new(Activities::Target).string().null()) .col(ColumnDef::new(Activities::To).json().null()) .col(ColumnDef::new(Activities::Bto).json().null()) @@ -280,7 +289,7 @@ impl MigrationTrait for Migration { ).await?; manager - .create_index(Index::create().unique().name("index-activities-ap-id").table(Activities::Table).col(Activities::ApId).to_owned()) + .create_index(Index::create().unique().name("index-activities-id").table(Activities::Table).col(Activities::Id).to_owned()) .await?; manager @@ -304,7 +313,7 @@ impl MigrationTrait for Migration { .await?; manager - .drop_index(Index::drop().name("index-actors-ap-id").table(Actors::Table).to_owned()) + .drop_index(Index::drop().name("index-actors-id").table(Actors::Table).to_owned()) .await?; manager @@ -321,7 +330,7 @@ impl MigrationTrait for Migration { .await?; manager - .drop_index(Index::drop().name("index-activities-ap-id").table(Activities::Table).to_owned()) + .drop_index(Index::drop().name("index-activities-id").table(Activities::Table).to_owned()) .await?; manager @@ -342,7 +351,7 @@ impl MigrationTrait for Migration { .await?; manager - .drop_index(Index::drop().name("index-objects-ap-id").table(Objects::Table).to_owned()) + .drop_index(Index::drop().name("index-objects-id").table(Objects::Table).to_owned()) .await?; manager diff --git a/src/migrations/m20240524_000002_create_relations_likes_shares.rs b/src/migrations/m20240524_000002_create_relations_likes_shares.rs index 1893ba9..cceddd9 100644 --- a/src/migrations/m20240524_000002_create_relations_likes_shares.rs +++ b/src/migrations/m20240524_000002_create_relations_likes_shares.rs @@ -5,7 +5,7 @@ use super::m20240524_000001_create_actor_activity_object_tables::{Activities, Ac #[derive(DeriveIden)] pub enum Relations { Table, - Id, + Internal, Follower, Following, Activity, @@ -16,9 +16,9 @@ pub enum Relations { #[allow(clippy::enum_variant_names)] pub enum Likes { Table, - Id, + Internal, Actor, - Likes, + Object, Published, } @@ -26,9 +26,9 @@ pub enum Likes { #[allow(clippy::enum_variant_names)] pub enum Announces { Table, - Id, + Internal, Actor, - Announces, + Object, Published, } @@ -44,44 +44,44 @@ impl MigrationTrait for Migration { .table(Relations::Table) .comment("follow relations between actors (applications too! for relays)") .col( - ColumnDef::new(Relations::Id) - .integer() + ColumnDef::new(Relations::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Relations::Follower).integer().not_null()) + .col(ColumnDef::new(Relations::Follower).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-relations-follower") .from(Relations::Table, Relations::Follower) - .to(Actors::Table, Actors::Id) + .to(Actors::Table, Actors::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Relations::Following).integer().not_null()) + .col(ColumnDef::new(Relations::Following).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-relations-following") .from(Relations::Table, Relations::Following) - .to(Actors::Table, Actors::Id) + .to(Actors::Table, Actors::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Relations::Accept).integer().null()) + .col(ColumnDef::new(Relations::Accept).big_integer().null()) .foreign_key( ForeignKey::create() .name("fkey-relations-accept") .from(Relations::Table, Relations::Accept) - .to(Activities::Table, Activities::Id) + .to(Activities::Table, Activities::Internal) .on_update(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Relations::Activity).integer().not_null()) + .col(ColumnDef::new(Relations::Activity).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-relations-activity") .from(Relations::Table, Relations::Activity) - .to(Activities::Table, Activities::Id) + .to(Activities::Table, Activities::Internal) .on_update(ForeignKeyAction::Cascade) ) .to_owned() @@ -102,27 +102,27 @@ impl MigrationTrait for Migration { .table(Likes::Table) .comment("all like events, joining actor to object") .col( - ColumnDef::new(Likes::Id) - .integer() + ColumnDef::new(Likes::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Likes::Actor).integer().not_null()) + .col(ColumnDef::new(Likes::Actor).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-likes-actor") .from(Likes::Table, Likes::Actor) - .to(Actors::Table, Actors::Id) + .to(Actors::Table, Actors::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Likes::Likes).integer().not_null()) + .col(ColumnDef::new(Likes::Object).big_integer().not_null()) .foreign_key( ForeignKey::create() - .name("fkey-likes-likes") - .from(Likes::Table, Likes::Likes) - .to(Objects::Table, Objects::Id) + .name("fkey-likes-object") + .from(Likes::Table, Likes::Object) + .to(Objects::Table, Objects::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) @@ -136,17 +136,17 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().name("index-likes-likes").table(Likes::Table).col(Likes::Likes).to_owned()) + .create_index(Index::create().name("index-likes-object").table(Likes::Table).col(Likes::Likes).to_owned()) .await?; manager .create_index( Index::create() .unique() - .name("index-likes-actor-likes") + .name("index-likes-actor-object") .table(Likes::Table) .col(Likes::Actor) - .col(Likes::Likes) + .col(Likes::Object) .to_owned() ).await?; @@ -156,27 +156,27 @@ impl MigrationTrait for Migration { .table(Announces::Table) .comment("all share/boost/reblog events, joining actor to object") .col( - ColumnDef::new(Announces::Id) - .integer() + ColumnDef::new(Announces::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Announces::Actor).integer().not_null()) + .col(ColumnDef::new(Announces::Actor).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-announces-actor") .from(Announces::Table, Announces::Actor) - .to(Actors::Table, Actors::Id) + .to(Actors::Table, Actors::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Announces::Announces).integer().not_null()) + .col(ColumnDef::new(Announces::Object).big_integer().not_null()) .foreign_key( ForeignKey::create() - .name("fkey-announces-announces") - .from(Announces::Table, Announces::Announces) - .to(Objects::Table, Objects::Id) + .name("fkey-announces-object") + .from(Announces::Table, Announces::Object) + .to(Objects::Table, Objects::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) @@ -190,7 +190,7 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().name("index-announces-announces").table(Announces::Table).col(Announces::Announces).to_owned()) + .create_index(Index::create().name("index-announces-object").table(Announces::Table).col(Announces::Announces).to_owned()) .await?; Ok(()) @@ -217,10 +217,10 @@ impl MigrationTrait for Migration { .drop_index(Index::drop().name("index-likes-actor").table(Likes::Table).to_owned()) .await?; manager - .drop_index(Index::drop().name("index-likes-likes").table(Likes::Table).to_owned()) + .drop_index(Index::drop().name("index-likes-object").table(Likes::Table).to_owned()) .await?; manager - .drop_index(Index::drop().name("index-likes-actor-likes").table(Likes::Table).to_owned()) + .drop_index(Index::drop().name("index-likes-actor-object").table(Likes::Table).to_owned()) .await?; manager @@ -228,10 +228,10 @@ impl MigrationTrait for Migration { .await?; manager - .drop_index(Index::drop().name("shares-actor-index").table(Announces::Table).to_owned()) + .drop_index(Index::drop().name("index-announces-actor").table(Announces::Table).to_owned()) .await?; manager - .drop_index(Index::drop().name("shares-shares-index").table(Announces::Table).to_owned()) + .drop_index(Index::drop().name("index-announces-object").table(Announces::Table).to_owned()) .await?; Ok(()) diff --git a/src/migrations/m20240524_000003_create_users_auth_and_config.rs b/src/migrations/m20240524_000003_create_users_auth_and_config.rs index bd83a33..aeb4834 100644 --- a/src/migrations/m20240524_000003_create_users_auth_and_config.rs +++ b/src/migrations/m20240524_000003_create_users_auth_and_config.rs @@ -5,7 +5,7 @@ use super::m20240524_000001_create_actor_activity_object_tables::Actors; #[derive(DeriveIden)] pub enum Configs { Table, - Id, + Internal, Actor, AcceptFollowRequests, ShowFollowersCount, @@ -17,7 +17,7 @@ pub enum Configs { #[derive(DeriveIden)] pub enum Credentials { Table, - Id, + Internal, Actor, Login, Password, @@ -26,7 +26,7 @@ pub enum Credentials { #[derive(DeriveIden)] pub enum Sessions { Table, - Id, + Internal, Actor, Secret, Expires, @@ -44,13 +44,13 @@ impl MigrationTrait for Migration { .table(Configs::Table) .comment("configuration for each local user") .col( - ColumnDef::new(Configs::Id) - .integer() + ColumnDef::new(Configs::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Configs::Actor).integer().not_null()) + .col(ColumnDef::new(Configs::Actor).string().not_null().unique_key()) .foreign_key( ForeignKey::create() .name("fkey-config-actor") @@ -69,7 +69,7 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().name("index-configs-actor").table(Configs::Table).col(Configs::Actor).to_owned()) + .create_index(Index::create().unique().name("index-configs-actor").table(Configs::Table).col(Configs::Actor).to_owned()) .await?; manager @@ -78,13 +78,13 @@ impl MigrationTrait for Migration { .table(Credentials::Table) .comment("simple login credentials to authenticate local users") .col( - ColumnDef::new(Credentials::Id) - .integer() + ColumnDef::new(Credentials::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Credentials::Actor).integer().not_null()) + .col(ColumnDef::new(Credentials::Actor).string().not_null().unique_key()) .foreign_key( ForeignKey::create() .name("fkey-credentials-actor") @@ -99,6 +99,10 @@ impl MigrationTrait for Migration { ) .await?; + manager + .create_index(Index::create().unique().name("index-credentials-actor").table(Credentials::Table).col(Credentials::Actor).to_owned()) + .await?; + manager .create_index(Index::create().name("index-credentials-login").table(Credentials::Table).col(Credentials::Login).to_owned()) .await?; @@ -109,13 +113,13 @@ impl MigrationTrait for Migration { .table(Sessions::Table) .comment("authenticated sessions from local users") .col( - ColumnDef::new(Sessions::Id) - .integer() + ColumnDef::new(Sessions::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Sessions::Actor).integer().not_null()) + .col(ColumnDef::new(Sessions::Actor).string().not_null()) .foreign_key( ForeignKey::create() .name("fkey-sessions-actor") diff --git a/src/migrations/m20240524_000004_create_addressing_deliveries.rs b/src/migrations/m20240524_000004_create_addressing_deliveries.rs index 6d7e330..2c82461 100644 --- a/src/migrations/m20240524_000004_create_addressing_deliveries.rs +++ b/src/migrations/m20240524_000004_create_addressing_deliveries.rs @@ -5,7 +5,7 @@ use super::m20240524_000001_create_actor_activity_object_tables::{Activities, Ac #[derive(DeriveIden)] pub enum Addressing { Table, - Id, + Internal, Actor, Instance, Activity, @@ -16,7 +16,7 @@ pub enum Addressing { #[derive(DeriveIden)] pub enum Deliveries { Table, - Id, + Internal, Actor, Target, Activity, @@ -37,42 +37,42 @@ impl MigrationTrait for Migration { .table(Addressing::Table) .comment("this join table contains all addressing relations, serving effectively as permissions truth table") .col( - ColumnDef::new(Addressing::Id) - .integer() + ColumnDef::new(Addressing::Internal) + .big_integer() .not_null() .auto_increment() .primary_key() ) - .col(ColumnDef::new(Addressing::Actor).integer().not_null()) + .col(ColumnDef::new(Addressing::Actor).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-addressing-actor") .from(Addressing::Table, Addressing::Actor) - .to(Actors::Table, Actors::Id) + .to(Actors::Table, Actors::Internal) .on_update(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Addressing::Instance).integer().not_null()) + .col(ColumnDef::new(Addressing::Instance).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-addressing-instance") .from(Addressing::Table, Addressing::Instance) - .to(Instances::Table, Instances::Id) + .to(Instances::Table, Instances::Internal) .on_update(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Addressing::Activity).integer().null()) + .col(ColumnDef::new(Addressing::Activity).big_integer().null()) .foreign_key( ForeignKey::create() .name("fkey-addressing-activity") .from(Addressing::Table, Addressing::Activity) - .to(Activities::Table, Activities::Id) + .to(Activities::Table, Activities::Internal) .on_update(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Addressing::Object).integer().null()) + .col(ColumnDef::new(Addressing::Object).big_integer().null()) .foreign_key( ForeignKey::create() .name("fkey-addressing-object") .from(Addressing::Table, Addressing::Object) - .to(Objects::Table, Objects::Id) + .to(Objects::Table, Objects::Internal) .on_update(ForeignKeyAction::Cascade) ) .col(ColumnDef::new(Addressing::Published).date_time().not_null().default(Expr::current_timestamp())) @@ -116,13 +116,13 @@ impl MigrationTrait for Migration { .table(Deliveries::Table) .comment("this table contains all enqueued outgoing delivery jobs") .col( - ColumnDef::new(Deliveries::Id) - .integer() + ColumnDef::new(Deliveries::Internal) + .big_integer() .not_null() .auto_increment() .primary_key() ) - .col(ColumnDef::new(Deliveries::Actor).integer().not_null()) + .col(ColumnDef::new(Deliveries::Actor).string().not_null()) .foreign_key( ForeignKey::create() .name("fkey-deliveries-actor") @@ -132,7 +132,7 @@ impl MigrationTrait for Migration { .on_delete(ForeignKeyAction::Cascade) ) .col(ColumnDef::new(Deliveries::Target).string().not_null()) - .col(ColumnDef::new(Deliveries::Activity).integer().not_null()) + .col(ColumnDef::new(Deliveries::Activity).string().not_null()) .foreign_key( ForeignKey::create() .name("fkey-deliveries-activity") diff --git a/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs b/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs index 1e6ed27..bf0c2c5 100644 --- a/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs +++ b/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs @@ -1,11 +1,11 @@ use sea_orm_migration::prelude::*; -use super::m20240524_000001_create_actor_activity_object_tables::{Actors, Objects}; +use super::m20240524_000001_create_actor_activity_object_tables::Objects; #[derive(DeriveIden)] pub enum Attachments { Table, - Id, + Internal, DocumentType, Url, Object, @@ -17,7 +17,7 @@ pub enum Attachments { #[derive(DeriveIden)] pub enum Mentions { Table, - Id, + Internal, Object, Actor, Published, @@ -26,7 +26,7 @@ pub enum Mentions { #[derive(DeriveIden)] pub enum Hashtags { Table, - Id, + Internal, Object, Name, Published, @@ -44,19 +44,19 @@ impl MigrationTrait for Migration { .table(Attachments::Table) .comment("media attachments related to objects") .col( - ColumnDef::new(Attachments::Id) - .integer() + ColumnDef::new(Attachments::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) .col(ColumnDef::new(Attachments::Url).string().not_null().unique_key()) - .col(ColumnDef::new(Attachments::Object).integer().not_null()) + .col(ColumnDef::new(Attachments::Object).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-attachments-object") .from(Attachments::Table, Attachments::Object) - .to(Objects::Table, Objects::Id) + .to(Objects::Table, Objects::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) @@ -78,30 +78,30 @@ impl MigrationTrait for Migration { .table(Mentions::Table) .comment("join table relating posts to mentioned users") .col( - ColumnDef::new(Mentions::Id) - .integer() + ColumnDef::new(Mentions::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Mentions::Object).integer().not_null()) + .col(ColumnDef::new(Mentions::Object).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-mentions-object") .from(Mentions::Table, Mentions::Object) - .to(Objects::Table, Objects::Id) - .on_update(ForeignKeyAction::Cascade) - .on_delete(ForeignKeyAction::Cascade) - ) - .col(ColumnDef::new(Mentions::Actor).integer().not_null()) - .foreign_key( - ForeignKey::create() - .name("fkey-mentions-actor") - .from(Mentions::Table, Mentions::Actor) - .to(Actors::Table, Actors::Id) + .to(Objects::Table, Objects::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) + .col(ColumnDef::new(Mentions::Actor).string().not_null()) + // .foreign_key( + // ForeignKey::create() + // .name("fkey-mentions-actor") + // .from(Mentions::Table, Mentions::Actor) + // .to(Actors::Table, Actors::Internal) + // .on_update(ForeignKeyAction::Cascade) + // .on_delete(ForeignKeyAction::Cascade) + // ) .col(ColumnDef::new(Mentions::Published).date_time().not_null().default(Expr::current_timestamp())) .to_owned() ) @@ -128,18 +128,18 @@ impl MigrationTrait for Migration { .table(Hashtags::Table) .comment("join table relating posts to hashtags") .col( - ColumnDef::new(Hashtags::Id) - .integer() + ColumnDef::new(Hashtags::Internal) + .big_integer() .not_null() .primary_key() .auto_increment() ) - .col(ColumnDef::new(Hashtags::Object).integer().not_null()) + .col(ColumnDef::new(Hashtags::Object).big_integer().not_null()) .foreign_key( ForeignKey::create() .name("fkey-hashtags-object") .from(Hashtags::Table, Hashtags::Object) - .to(Objects::Table, Objects::Id) + .to(Objects::Table, Objects::Internal) .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) diff --git a/src/model/activity.rs b/src/model/activity.rs index 351d2f4..df312cd 100644 --- a/src/model/activity.rs +++ b/src/model/activity.rs @@ -7,12 +7,12 @@ use crate::routes::activitypub::jsonld::LD; #[sea_orm(table_name = "activities")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, + pub internal: i64, #[sea_orm(unique)] - pub ap_id: String, + pub id: String, pub activity_type: ActivityType, - pub actor: i32, - pub object: Option, + pub actor: String, + pub object: Option, pub target: Option, pub to: Option, pub bto: Option, diff --git a/src/model/actor.rs b/src/model/actor.rs index 66de0a1..32233dc 100644 --- a/src/model/actor.rs +++ b/src/model/actor.rs @@ -8,11 +8,11 @@ use crate::routes::activitypub::jsonld::LD; #[sea_orm(table_name = "actors")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, + pub internal: i64, #[sea_orm(unique)] - pub ap_id: String, + pub id: String, pub actor_type: ActorType, - pub instance: i32, + pub domain: String, pub name: Option, pub summary: Option, pub image: Option, @@ -49,8 +49,8 @@ pub enum Relation { Deliveries, #[sea_orm( belongs_to = "super::instance::Entity", - from = "Column::Instance", - to = "super::instance::Column::Id", + from = "Column::Domain", + to = "super::instance::Column::Domain", on_update = "Cascade", on_delete = "NoAction" )] diff --git a/src/model/addressing.rs b/src/model/addressing.rs index 1e308d4..26175be 100644 --- a/src/model/addressing.rs +++ b/src/model/addressing.rs @@ -7,11 +7,11 @@ use crate::routes::activitypub::jsonld::LD; #[sea_orm(table_name = "addressing")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub actor: i32, - pub instance: i32, - pub activity: Option, - pub object: Option, + pub internal: i64, + pub actor: Option, + pub instance: Option, + pub activity: Option, + pub object: Option, pub published: ChronoDateTimeUtc, } @@ -20,7 +20,7 @@ pub enum Relation { #[sea_orm( belongs_to = "super::activity::Entity", from = "Column::Activity", - to = "super::activity::Column::Id", + to = "super::activity::Column::Internal", on_update = "Cascade", on_delete = "NoAction" )] @@ -28,7 +28,7 @@ pub enum Relation { #[sea_orm( belongs_to = "super::actor::Entity", from = "Column::Actor", - to = "super::actor::Column::Id", + to = "super::actor::Column::Internal", on_update = "Cascade", on_delete = "NoAction" )] @@ -36,7 +36,7 @@ pub enum Relation { #[sea_orm( belongs_to = "super::instance::Entity", from = "Column::Instance", - to = "super::instance::Column::Id", + to = "super::instance::Column::Internal", on_update = "Cascade", on_delete = "NoAction" )] @@ -44,7 +44,7 @@ pub enum Relation { #[sea_orm( belongs_to = "super::object::Entity", from = "Column::Object", - to = "super::object::Column::Id", + to = "super::object::Column::Internal", on_update = "Cascade", on_delete = "NoAction" )] diff --git a/src/model/announce.rs b/src/model/announce.rs index fc7a404..5a800b4 100644 --- a/src/model/announce.rs +++ b/src/model/announce.rs @@ -4,9 +4,9 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "announces")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub actor: i32, - pub announces: i32, + pub internal: i64, + pub actor: i64, + pub object: i64, pub published: ChronoDateTimeUtc, } @@ -15,15 +15,15 @@ pub enum Relation { #[sea_orm( belongs_to = "super::actor::Entity", from = "Column::Actor", - to = "super::actor::Column::Id", + to = "super::actor::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] Actors, #[sea_orm( belongs_to = "super::object::Entity", - from = "Column::Announces", - to = "super::object::Column::Id", + from = "Column::Object", + to = "super::object::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] diff --git a/src/model/attachment.rs b/src/model/attachment.rs index c7c8914..30930f4 100644 --- a/src/model/attachment.rs +++ b/src/model/attachment.rs @@ -9,10 +9,10 @@ use super::addressing::Event; #[sea_orm(table_name = "attachments")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, + pub internal: i64, #[sea_orm(unique)] pub url: String, - pub object: i32, + pub object: i64, pub document_type: String, pub name: Option, pub media_type: String, @@ -24,7 +24,7 @@ pub enum Relation { #[sea_orm( belongs_to = "super::object::Entity", from = "Column::Object", - to = "super::object::Column::Id", + to = "super::object::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] diff --git a/src/model/config.rs b/src/model/config.rs index f95bf43..5335dec 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -4,8 +4,9 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "configs")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub actor: i32, + pub internal: i64, + #[sea_orm(unique)] + pub actor: String, pub accept_follow_requests: bool, pub show_followers_count: bool, pub show_following_count: bool, @@ -16,7 +17,7 @@ pub struct Model { impl Default for Model { fn default() -> Self { Model { - id: 0, actor: 0, + internal: 0, actor: "".into(), accept_follow_requests: true, show_following_count: true, show_following: true, diff --git a/src/model/credential.rs b/src/model/credential.rs index 5255ead..be2d2c6 100644 --- a/src/model/credential.rs +++ b/src/model/credential.rs @@ -4,8 +4,9 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "credentials")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub actor: i32, + pub internal: i64, + #[sea_orm(unique)] + pub actor: String, pub login: String, pub password: String, } diff --git a/src/model/delivery.rs b/src/model/delivery.rs index 6835f4d..eac7aa0 100644 --- a/src/model/delivery.rs +++ b/src/model/delivery.rs @@ -4,10 +4,10 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "deliveries")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub actor: i32, + pub internal: i64, + pub actor: String, pub target: String, - pub activity: i32, + pub activity: String, pub created: ChronoDateTimeUtc, pub not_before: ChronoDateTimeUtc, pub attempt: i32, diff --git a/src/model/hashtag.rs b/src/model/hashtag.rs index d17b1d3..bcd603b 100644 --- a/src/model/hashtag.rs +++ b/src/model/hashtag.rs @@ -4,8 +4,8 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "hashtags")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub object: i32, + pub internal: i64, + pub object: i64, pub name: String, pub published: ChronoDateTimeUtc, } @@ -15,7 +15,7 @@ pub enum Relation { #[sea_orm( belongs_to = "super::object::Entity", from = "Column::Object", - to = "super::object::Column::Id", + to = "super::object::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] diff --git a/src/model/instance.rs b/src/model/instance.rs index 125fc1f..fe63ac4 100644 --- a/src/model/instance.rs +++ b/src/model/instance.rs @@ -4,11 +4,13 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "instances")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub name: Option, + pub internal: i64, + #[sea_orm(unique)] pub domain: String, + pub name: Option, pub software: Option, pub version: Option, + pub icon: Option, pub down_since: Option, pub users: Option, pub posts: Option, diff --git a/src/model/like.rs b/src/model/like.rs index 66456fc..1ca919f 100644 --- a/src/model/like.rs +++ b/src/model/like.rs @@ -4,9 +4,9 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "likes")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub actor: i32, - pub likes: i32, + pub internal: i64, + pub actor: i64, + pub object: i64, pub published: ChronoDateTimeUtc, } @@ -15,15 +15,15 @@ pub enum Relation { #[sea_orm( belongs_to = "super::actor::Entity", from = "Column::Actor", - to = "super::actor::Column::Id", + to = "super::actor::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] Actors, #[sea_orm( belongs_to = "super::object::Entity", - from = "Column::Likes", - to = "super::object::Column::Id", + from = "Column::Object", + to = "super::object::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] diff --git a/src/model/mention.rs b/src/model/mention.rs index 139d00c..f08cb04 100644 --- a/src/model/mention.rs +++ b/src/model/mention.rs @@ -4,9 +4,9 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "mentions")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub object: i32, - pub actor: i32, + pub internal: i64, + pub object: i64, + pub actor: String, pub published: ChronoDateTimeUtc, } @@ -23,7 +23,7 @@ pub enum Relation { #[sea_orm( belongs_to = "super::object::Entity", from = "Column::Object", - to = "super::object::Column::Id", + to = "super::object::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] diff --git a/src/model/object.rs b/src/model/object.rs index a56257b..bb1c9f6 100644 --- a/src/model/object.rs +++ b/src/model/object.rs @@ -9,11 +9,11 @@ use super::Audience; #[sea_orm(table_name = "objects")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, + pub internal: i64, #[sea_orm(unique)] - pub ap_id: String, + pub id: String, pub object_type: String, - pub attributed_to: Option, + pub attributed_to: Option, pub name: Option, pub summary: Option, pub content: Option, @@ -56,6 +56,14 @@ pub enum Relation { Likes, #[sea_orm(has_many = "super::mention::Entity")] Mentions, + #[sea_orm( + belongs_to = "Entity", + from = "Column::InReplyTo", + to = "Column::Id", + on_update = "Cascade", + on_delete = "NoAction" + )] + Objects, } impl Related for Entity { @@ -106,6 +114,12 @@ impl Related for Entity { } } +impl Related for Entity { + fn to() -> RelationDef { + Relation::Objects.def() + } +} + impl ActiveModelBehavior for ActiveModel {} impl ActiveModel { diff --git a/src/model/relation.rs b/src/model/relation.rs index 44d01df..2a91741 100644 --- a/src/model/relation.rs +++ b/src/model/relation.rs @@ -4,11 +4,11 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "relations")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub follower: i32, - pub following: i32, - pub accept: Option, - pub activity: i32, + pub internal: i64, + pub follower: i64, + pub following: i64, + pub accept: Option, + pub activity: i64, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -16,35 +16,35 @@ pub enum Relation { #[sea_orm( belongs_to = "super::activity::Entity", from = "Column::Accept", - to = "super::activity::Column::Id", + to = "super::activity::Column::Internal", on_update = "Cascade", on_delete = "NoAction" )] - Activities2, + ActivitiesAccept, #[sea_orm( belongs_to = "super::activity::Entity", from = "Column::Activity", - to = "super::activity::Column::Id", + to = "super::activity::Column::Internal", on_update = "Cascade", on_delete = "NoAction" )] - Activities1, + ActivitiesFollow, #[sea_orm( belongs_to = "super::actor::Entity", from = "Column::Follower", - to = "super::actor::Column::Id", + to = "super::actor::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] - Actors2, + ActorsFollower, #[sea_orm( belongs_to = "super::actor::Entity", from = "Column::Following", - to = "super::actor::Column::Id", + to = "super::actor::Column::Internal", on_update = "Cascade", on_delete = "Cascade" )] - Actors1, + ActorsFollowing, } impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/session.rs b/src/model/session.rs index 0e7dfea..f8ba460 100644 --- a/src/model/session.rs +++ b/src/model/session.rs @@ -4,8 +4,8 @@ use sea_orm::entity::prelude::*; #[sea_orm(table_name = "sessions")] pub struct Model { #[sea_orm(primary_key)] - pub id: i32, - pub actor: i32, + pub internal: i64, + pub actor: String, pub secret: String, pub expires: ChronoDateTimeUtc, } From b09cfd05264787d8b69551c9c46dc5ec52f68ce1 Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 25 May 2024 05:31:10 +0200 Subject: [PATCH 07/90] chore: updated models and some server components --- src/model/activity.rs | 33 +++-- src/model/actor.rs | 37 +++-- src/model/addressing.rs | 11 +- src/model/attachment.rs | 16 +-- src/model/instance.rs | 21 ++- src/model/object.rs | 49 ++++--- src/routes/activitypub/auth.rs | 9 +- src/routes/activitypub/object/mod.rs | 4 +- src/routes/activitypub/user/mod.rs | 4 +- src/routes/activitypub/well_known.rs | 71 ++++------ src/routes/mastodon/accounts.rs | 3 +- src/server/admin.rs | 74 +++++----- src/server/dispatcher.rs | 33 ++--- src/server/fetcher.rs | 200 ++++++++++++++------------- src/server/normalizer.rs | 51 +++---- 15 files changed, 338 insertions(+), 278 deletions(-) diff --git a/src/model/activity.rs b/src/model/activity.rs index df312cd..eddc86d 100644 --- a/src/model/activity.rs +++ b/src/model/activity.rs @@ -1,7 +1,7 @@ use apb::{ActivityMut, ActivityType, BaseMut, ObjectMut}; -use sea_orm::entity::prelude::*; +use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns}; -use crate::routes::activitypub::jsonld::LD; +use crate::{model::Audience, errors::UpubError, routes::activitypub::jsonld::LD}; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "activities")] @@ -14,10 +14,10 @@ pub struct Model { pub actor: String, pub object: Option, pub target: Option, - pub to: Option, - pub bto: Option, - pub cc: Option, - pub bcc: Option, + pub to: Audience, + pub bto: Audience, + pub cc: Audience, + pub bcc: Audience, pub published: ChronoDateTimeUtc, } @@ -71,11 +71,28 @@ impl Related for Entity { impl ActiveModelBehavior for ActiveModel {} +impl Entity { + pub fn find_by_ap_id(id: &str) -> Select { + Entity::find().filter(Column::Id.eq(id)) + } + + pub async fn ap_to_internal(id: &str, db: &DatabaseConnection) -> crate::Result { + Entity::find() + .filter(Column::Id.eq(id)) + .select_only() + .select_column(Column::Internal) + .into_tuple::() + .one(db) + .await? + .ok_or_else(UpubError::not_found) + } +} + impl ActiveModel { pub fn new(activity: &impl apb::Activity) -> Result { Ok(ActiveModel { - id: sea_orm::ActiveValue::NotSet, - ap_id: sea_orm::ActiveValue::Set(activity.id().ok_or(super::FieldError("id"))?.to_string()), + internal: sea_orm::ActiveValue::NotSet, + id: sea_orm::ActiveValue::Set(activity.id().ok_or(super::FieldError("id"))?.to_string()), activity_type: sea_orm::ActiveValue::Set(activity.activity_type().ok_or(super::FieldError("type"))?), actor: sea_orm::ActiveValue::Set(activity.actor().id().ok_or(super::FieldError("actor"))?), object: sea_orm::ActiveValue::Set(activity.object().id()), diff --git a/src/model/actor.rs b/src/model/actor.rs index 32233dc..cd41d08 100644 --- a/src/model/actor.rs +++ b/src/model/actor.rs @@ -1,8 +1,8 @@ -use sea_orm::entity::prelude::*; +use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns}; use apb::{Actor, ActorMut, ActorType, BaseMut, DocumentMut, Endpoints, EndpointsMut, Object, ObjectMut, PublicKey, PublicKeyMut}; -use crate::routes::activitypub::jsonld::LD; +use crate::{errors::UpubError, routes::activitypub::jsonld::LD}; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "actors")] @@ -133,14 +133,31 @@ impl Related for Entity { impl ActiveModelBehavior for ActiveModel {} +impl Entity { + pub fn find_by_ap_id(id: &str) -> Select { + Entity::find().filter(Column::Id.eq(id)) + } + + pub async fn ap_to_internal(id: &str, db: &DatabaseConnection) -> crate::Result { + Entity::find() + .filter(Column::Id.eq(id)) + .select_only() + .select_column(Column::Internal) + .into_tuple::() + .one(db) + .await? + .ok_or_else(UpubError::not_found) + } +} + impl ActiveModel { - pub fn new(object: &impl Actor, instance: i32) -> Result { + pub fn new(object: &impl Actor) -> Result { let ap_id = object.id().ok_or(super::FieldError("id"))?.to_string(); - let (_domain, fallback_preferred_username) = split_user_id(&ap_id); + let (domain, fallback_preferred_username) = split_user_id(&ap_id); Ok(ActiveModel { - instance: sea_orm::ActiveValue::Set(instance), // TODO receiving it from outside is cheap - id: sea_orm::ActiveValue::NotSet, - ap_id: sea_orm::ActiveValue::Set(ap_id), + internal: sea_orm::ActiveValue::NotSet, + domain: sea_orm::ActiveValue::Set(domain), + id: sea_orm::ActiveValue::Set(ap_id), preferred_username: sea_orm::ActiveValue::Set(object.preferred_username().unwrap_or(&fallback_preferred_username).to_string()), actor_type: sea_orm::ActiveValue::Set(object.actor_type().ok_or(super::FieldError("type"))?), name: sea_orm::ActiveValue::Set(object.name().map(|x| x.to_string())), @@ -154,9 +171,9 @@ impl ActiveModel { following: sea_orm::ActiveValue::Set(object.following().id()), created: sea_orm::ActiveValue::Set(object.published().unwrap_or(chrono::Utc::now())), updated: sea_orm::ActiveValue::Set(chrono::Utc::now()), - following_count: sea_orm::ActiveValue::Set(object.following_count().unwrap_or(0) as i64), - followers_count: sea_orm::ActiveValue::Set(object.followers_count().unwrap_or(0) as i64), - statuses_count: sea_orm::ActiveValue::Set(object.statuses_count().unwrap_or(0) as i64), + following_count: sea_orm::ActiveValue::Set(object.following_count().unwrap_or(0) as i32), + followers_count: sea_orm::ActiveValue::Set(object.followers_count().unwrap_or(0) as i32), + statuses_count: sea_orm::ActiveValue::Set(object.statuses_count().unwrap_or(0) as i32), public_key: sea_orm::ActiveValue::Set(object.public_key().get().ok_or(super::FieldError("publicKey"))?.public_key_pem().to_string()), private_key: sea_orm::ActiveValue::Set(None), // there's no way to transport privkey over AP json, must come from DB }) diff --git a/src/model/addressing.rs b/src/model/addressing.rs index 26175be..2eb6ecf 100644 --- a/src/model/addressing.rs +++ b/src/model/addressing.rs @@ -154,12 +154,12 @@ impl FromQueryResult for Event { impl Entity { - pub fn find_addressed(uid: Option<&str>) -> Select { + pub fn find_addressed(uid: Option) -> Select { let mut select = Entity::find() .distinct() .select_only() - .join(sea_orm::JoinType::LeftJoin, Relation::Object.def()) - .join(sea_orm::JoinType::LeftJoin, Relation::Activity.def()) + .join(sea_orm::JoinType::LeftJoin, Relation::Objects.def()) + .join(sea_orm::JoinType::LeftJoin, Relation::Activities.def()) .filter( // TODO ghetto double inner join because i want to filter out tombstones Condition::any() @@ -169,12 +169,11 @@ impl Entity { .order_by(Column::Published, Order::Desc); if let Some(uid) = uid { - let uid = uid.to_string(); select = select .join( sea_orm::JoinType::LeftJoin, - crate::model::object::Relation::Like.def() - .on_condition(move |_l, _r| crate::model::like::Column::Actor.eq(uid.clone()).into_condition()), + crate::model::object::Relation::Likes.def() + .on_condition(move |_l, _r| crate::model::like::Column::Actor.eq(uid).into_condition()), ) .select_column_as(crate::model::like::Column::Actor, format!("{}{}", crate::model::like::Entity.table_name(), crate::model::like::Column::Actor.to_string())); } diff --git a/src/model/attachment.rs b/src/model/attachment.rs index 30930f4..93c6ea4 100644 --- a/src/model/attachment.rs +++ b/src/model/attachment.rs @@ -1,4 +1,4 @@ -use apb::{DocumentMut, ObjectMut}; +use apb::{DocumentMut, DocumentType, ObjectMut}; use sea_orm::entity::prelude::*; use crate::routes::activitypub::jsonld::LD; @@ -13,7 +13,7 @@ pub struct Model { #[sea_orm(unique)] pub url: String, pub object: i64, - pub document_type: String, + pub document_type: DocumentType, pub name: Option, pub media_type: String, pub created: ChronoDateTimeUtc, @@ -52,12 +52,12 @@ impl Model { #[axum::async_trait] pub trait BatchFillable { - async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr>; + async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr>; } #[axum::async_trait] impl BatchFillable for &[Event] { - async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr> { + async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr> { let objects : Vec = self .iter() .filter_map(|x| match x { @@ -70,12 +70,12 @@ impl BatchFillable for &[Event] { let attachments = objects.load_many(Entity, db).await?; - let mut out : std::collections::BTreeMap> = std::collections::BTreeMap::new(); + let mut out : std::collections::BTreeMap> = std::collections::BTreeMap::new(); for attach in attachments.into_iter().flatten() { if out.contains_key(&attach.object) { out.get_mut(&attach.object).expect("contains but get failed?").push(attach); } else { - out.insert(attach.object.clone(), vec![attach]); + out.insert(attach.object, vec![attach]); } } @@ -85,14 +85,14 @@ impl BatchFillable for &[Event] { #[axum::async_trait] impl BatchFillable for Vec { - async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr> { + async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr> { self.as_slice().load_attachments_batch(db).await } } #[axum::async_trait] impl BatchFillable for Event { - async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr> { + async fn load_attachments_batch(&self, db: &DatabaseConnection) -> Result>, DbErr> { let x = vec![self.clone()]; // TODO wasteful clone and vec![] but ehhh convenient x.load_attachments_batch(db).await } diff --git a/src/model/instance.rs b/src/model/instance.rs index fe63ac4..83c8c6e 100644 --- a/src/model/instance.rs +++ b/src/model/instance.rs @@ -1,4 +1,6 @@ -use sea_orm::entity::prelude::*; +use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns}; + +use crate::errors::UpubError; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "instances")] @@ -39,3 +41,20 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +impl Entity { + pub fn find_by_domain(domain: &str) -> Select { + Entity::find().filter(Column::Domain.eq(domain)) + } + + pub async fn domain_to_internal(domain: &str, db: &DatabaseConnection) -> crate::Result { + Entity::find() + .filter(Column::Domain.eq(domain)) + .select_only() + .select_column(Column::Internal) + .into_tuple::() + .one(db) + .await? + .ok_or_else(UpubError::not_found) + } +} diff --git a/src/model/object.rs b/src/model/object.rs index bb1c9f6..bbaab0e 100644 --- a/src/model/object.rs +++ b/src/model/object.rs @@ -1,7 +1,7 @@ -use apb::{BaseMut, Collection, CollectionMut, ObjectMut}; -use sea_orm::entity::prelude::*; +use apb::{BaseMut, Collection, CollectionMut, ObjectMut, ObjectType}; +use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns}; -use crate::routes::activitypub::jsonld::LD; +use crate::{errors::UpubError, routes::activitypub::jsonld::LD}; use super::Audience; @@ -12,7 +12,7 @@ pub struct Model { pub internal: i64, #[sea_orm(unique)] pub id: String, - pub object_type: String, + pub object_type: ObjectType, pub attributed_to: Option, pub name: Option, pub summary: Option, @@ -24,10 +24,10 @@ pub struct Model { pub announces: i32, pub replies: i32, pub context: Option, - pub to: Option, - pub bto: Option, - pub cc: Option, - pub bcc: Option, + pub to: Audience, + pub bto: Audience, + pub cc: Audience, + pub bcc: Audience, pub published: ChronoDateTimeUtc, pub updated: ChronoDateTimeUtc, } @@ -122,11 +122,28 @@ impl Related for Entity { impl ActiveModelBehavior for ActiveModel {} +impl Entity { + pub fn find_by_ap_id(id: &str) -> Select { + Entity::find().filter(Column::Id.eq(id)) + } + + pub async fn ap_to_internal(id: &str, db: &DatabaseConnection) -> crate::Result { + Entity::find() + .filter(Column::Id.eq(id)) + .select_only() + .select_column(Column::Internal) + .into_tuple::() + .one(db) + .await? + .ok_or_else(UpubError::not_found) + } +} + impl ActiveModel { pub fn new(object: &impl apb::Object) -> Result { Ok(ActiveModel { - id: sea_orm::ActiveValue::NotSet, - ap_id: sea_orm::ActiveValue::Set(object.id().ok_or(super::FieldError("id"))?.to_string()), + internal: sea_orm::ActiveValue::NotSet, + id: sea_orm::ActiveValue::Set(object.id().ok_or(super::FieldError("id"))?.to_string()), object_type: sea_orm::ActiveValue::Set(object.object_type().ok_or(super::FieldError("type"))?), attributed_to: sea_orm::ActiveValue::Set(object.attributed_to().id()), name: sea_orm::ActiveValue::Set(object.name().map(|x| x.to_string())), @@ -135,14 +152,14 @@ impl ActiveModel { context: sea_orm::ActiveValue::Set(object.context().id()), in_reply_to: sea_orm::ActiveValue::Set(object.in_reply_to().id()), published: sea_orm::ActiveValue::Set(object.published().ok_or(super::FieldError("published"))?), - updated: sea_orm::ActiveValue::Set(object.updated()), + updated: sea_orm::ActiveValue::Set(object.updated().unwrap_or_else(chrono::Utc::now)), url: sea_orm::ActiveValue::Set(object.url().id()), replies: sea_orm::ActiveValue::Set(object.replies().get() - .map_or(0, |x| x.total_items().unwrap_or(0)) as i64), + .map_or(0, |x| x.total_items().unwrap_or(0)) as i32), likes: sea_orm::ActiveValue::Set(object.likes().get() - .map_or(0, |x| x.total_items().unwrap_or(0)) as i64), + .map_or(0, |x| x.total_items().unwrap_or(0)) as i32), announces: sea_orm::ActiveValue::Set(object.shares().get() - .map_or(0, |x| x.total_items().unwrap_or(0)) as i64), + .map_or(0, |x| x.total_items().unwrap_or(0)) as i32), to: sea_orm::ActiveValue::Set(object.to().into()), bto: sea_orm::ActiveValue::Set(object.bto().into()), cc: sea_orm::ActiveValue::Set(object.cc().into()), @@ -176,7 +193,7 @@ impl Model { .set_shares(apb::Node::object( serde_json::Value::new_object() .set_collection_type(Some(apb::CollectionType::OrderedCollection)) - .set_total_items(Some(self.shares as u64)) + .set_total_items(Some(self.announces as u64)) )) .set_likes(apb::Node::object( serde_json::Value::new_object() @@ -186,7 +203,7 @@ impl Model { .set_replies(apb::Node::object( serde_json::Value::new_object() .set_collection_type(Some(apb::CollectionType::OrderedCollection)) - .set_total_items(Some(self.comments as u64)) + .set_total_items(Some(self.replies as u64)) )) } } diff --git a/src/routes/activitypub/auth.rs b/src/routes/activitypub/auth.rs index acb01b5..c1928f0 100644 --- a/src/routes/activitypub/auth.rs +++ b/src/routes/activitypub/auth.rs @@ -25,7 +25,7 @@ pub async fn login( // TODO salt the pwd match model::credential::Entity::find() .filter(Condition::all() - .add(model::credential::Column::Email.eq(login.email)) + .add(model::credential::Column::Login.eq(login.email)) .add(model::credential::Column::Password.eq(sha256::digest(login.password))) ) .one(ctx.db()) @@ -41,8 +41,9 @@ pub async fn login( let expires = chrono::Utc::now() + std::time::Duration::from_secs(3600 * 6); model::session::Entity::insert( model::session::ActiveModel { - id: sea_orm::ActiveValue::Set(token.clone()), - actor: sea_orm::ActiveValue::Set(x.id.clone()), + internal: sea_orm::ActiveValue::NotSet, + secret: sea_orm::ActiveValue::Set(token.clone()), + actor: sea_orm::ActiveValue::Set(x.actor.clone()), expires: sea_orm::ActiveValue::Set(expires), } ) @@ -50,7 +51,7 @@ pub async fn login( .await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(Json(AuthSuccess { token, expires, - user: x.id + user: x.actor })) }, None => Err(UpubError::unauthorized()), diff --git a/src/routes/activitypub/object/mod.rs b/src/routes/activitypub/object/mod.rs index 59a4fc9..445dd29 100644 --- a/src/routes/activitypub/object/mod.rs +++ b/src/routes/activitypub/object/mod.rs @@ -2,7 +2,7 @@ pub mod replies; use apb::{CollectionMut, ObjectMut}; use axum::extract::{Path, Query, State}; -use sea_orm::{ColumnTrait, EntityTrait, ModelTrait, QueryFilter, QuerySelect, SelectColumns}; +use sea_orm::{ColumnTrait, ModelTrait, QueryFilter, QuerySelect, SelectColumns}; use crate::{errors::UpubError, model::{self, addressing::Event}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}}; @@ -62,7 +62,7 @@ pub async fn view( // .set_id(Some(&crate::url!(ctx, "/objects/{id}/replies"))) // .set_first(apb::Node::link(crate::url!(ctx, "/objects/{id}/replies/page"))) .set_collection_type(Some(apb::CollectionType::Collection)) - .set_total_items(Some(object.comments as u64)) + .set_total_items(Some(object.replies as u64)) .set_items(apb::Node::links(replies_ids)) ); } diff --git a/src/routes/activitypub/user/mod.rs b/src/routes/activitypub/user/mod.rs index 5819f24..a8076bf 100644 --- a/src/routes/activitypub/user/mod.rs +++ b/src/routes/activitypub/user/mod.rs @@ -8,7 +8,7 @@ use axum::extract::{Path, Query, State}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QuerySelect, SelectColumns}; use apb::{ActorMut, EndpointsMut, Node}; -use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url}; +use crate::{errors::UpubError, model, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url}; use super::{jsonld::LD, JsonLD, TryFetch}; @@ -61,7 +61,7 @@ pub async fn view( }, }; - match user::Entity::find_by_id(&uid) + match model::actor::Entity::find_by_ap_id(&uid) .find_also_related(model::config::Entity) .one(ctx.db()).await? { diff --git a/src/routes/activitypub/well_known.rs b/src/routes/activitypub/well_known.rs index 732f690..5b72509 100644 --- a/src/routes/activitypub/well_known.rs +++ b/src/routes/activitypub/well_known.rs @@ -35,7 +35,7 @@ pub async fn nodeinfo_discovery(State(ctx): State) -> Json, Path(version): Path) -> Result, StatusCode> { // TODO it's unsustainable to count these every time, especially comments since it's a complex // filter! keep these numbers caches somewhere, maybe db, so that we can just look them up - let total_users = model::user::Entity::find().count(ctx.db()).await.ok(); + let total_users = model::actor::Entity::find().count(ctx.db()).await.ok(); let total_posts = None; let total_comments = None; let (software, version) = match version.as_str() { @@ -103,54 +103,33 @@ pub async fn webfinger(State(ctx): State, Query(query): Query ) -> Result, StatusCode> { - match model::user::Entity::find_by_id(ctx.uid(&id)) + match model::actor::Entity::find_by_ap_id(&ctx.uid(&id)) .find_also_related(model::config::Entity) .one(ctx.db()) .await diff --git a/src/server/admin.rs b/src/server/admin.rs index 0ee1dd7..c5f70e9 100644 --- a/src/server/admin.rs +++ b/src/server/admin.rs @@ -1,4 +1,4 @@ -use sea_orm::{EntityTrait, IntoActiveModel}; +use sea_orm::{ActiveValue::{Set, NotSet}, EntityTrait}; #[axum::async_trait] pub trait Administrable { @@ -28,52 +28,56 @@ impl Administrable for super::Context { let ap_id = self.uid(&username); let db = self.db(); let domain = self.domain().to_string(); - let user_model = crate::model::user::Model { - id: ap_id.clone(), - name: display_name, - domain, summary, - preferred_username: username.clone(), - following: None, - following_count: 0, - followers: None, - followers_count: 0, - statuses_count: 0, - icon: avatar_url, - image: banner_url, - inbox: None, - shared_inbox: None, - outbox: None, - actor_type: apb::ActorType::Person, - created: chrono::Utc::now(), - updated: chrono::Utc::now(), - private_key: Some(std::str::from_utf8(&key.private_key_to_pem().unwrap()).unwrap().to_string()), - public_key: std::str::from_utf8(&key.public_key_to_pem().unwrap()).unwrap().to_string(), + let user_model = crate::model::actor::Model { + internal: NotSet, + id: Set(ap_id.clone()), + name: Set(display_name), + domain: Set(domain), + summary: Set(summary), + preferred_username: Set(username.clone()), + following: Set(None), + following_count: Set(0), + followers: Set(None), + followers_count: Set(0), + statuses_count: Set(0), + icon: Set(avatar_url), + image: Set(banner_url), + inbox: Set(None), + shared_inbox: Set(None), + outbox: Set(None), + actor_type: Set(apb::ActorType::Person), + created: Set(chrono::Utc::now()), + updated: Set(chrono::Utc::now()), + private_key: Set(Some(std::str::from_utf8(&key.private_key_to_pem().unwrap()).unwrap().to_string())), + public_key: Set(std::str::from_utf8(&key.public_key_to_pem().unwrap()).unwrap().to_string()), }; - crate::model::user::Entity::insert(user_model.into_active_model()) + crate::model::actor::Entity::insert(user_model) .exec(db) .await?; - let config_model = crate::model::config::Model { - id: ap_id.clone(), - accept_follow_requests: true, - show_followers_count: true, - show_following_count: true, - show_followers: false, - show_following: false, + let config_model = crate::model::config::ActiveModel { + internal: NotSet, + actor: Set(ap_id.clone()), + accept_follow_requests: Set(true), + show_followers_count: Set(true), + show_following_count: Set(true), + show_followers: Set(false), + show_following: Set(false), }; - crate::model::config::Entity::insert(config_model.into_active_model()) + crate::model::config::Entity::insert(config_model) .exec(db) .await?; - let credentials_model = crate::model::credential::Model { - id: ap_id, - email: username, - password, + let credentials_model = crate::model::credential::ActiveModel { + internal: NotSet, + actor: Set(ap_id), + login: Set(username), + password: Set(password), }; - crate::model::credential::Entity::insert(credentials_model.into_active_model()) + crate::model::credential::Entity::insert(credentials_model) .exec(db) .await?; diff --git a/src/server/dispatcher.rs b/src/server/dispatcher.rs index 4a54550..f48f5a6 100644 --- a/src/server/dispatcher.rs +++ b/src/server/dispatcher.rs @@ -54,7 +54,7 @@ async fn worker(db: &DatabaseConnection, domain: &str, poll_interval: u64, waker }; let del_row = model::delivery::ActiveModel { - id: sea_orm::ActiveValue::Set(delivery.id), + internal: sea_orm::ActiveValue::Set(delivery.internal), ..Default::default() }; let del = model::delivery::Entity::delete(del_row) @@ -72,7 +72,7 @@ async fn worker(db: &DatabaseConnection, domain: &str, poll_interval: u64, waker tracing::info!("delivering {} to {}", delivery.activity, delivery.target); - let payload = match model::activity::Entity::find_by_id(&delivery.activity) + let payload = match model::activity::Entity::find_by_ap_id(&delivery.activity) .find_also_related(model::object::Entity) .one(db) .await? // TODO probably should not fail here and at least re-insert the delivery @@ -99,24 +99,19 @@ async fn worker(db: &DatabaseConnection, domain: &str, poll_interval: u64, waker }, }; - let key = if delivery.actor == format!("https://{domain}") { - let Some(model::application::Model { private_key: key, .. }) = model::application::Entity::find() - .one(db).await? - else { - tracing::error!("no private key configured for application"); - continue; - }; - key - } else { - let Some(model::user::Model{ private_key: Some(key), .. }) = model::user::Entity::find_by_id(&delivery.actor) - .one(db).await? - else { - tracing::error!("can not dispatch activity for user without private key: {}", delivery.actor); - continue; - }; - key + let Some(actor) = model::actor::Entity::find_by_ap_id(&delivery.actor) + .one(db) + .await? + else { + tracing::error!("abandoning delivery of {} from non existant actor: {}", delivery.activity, delivery.actor); + continue; }; + let Some(key) = actor.private_key + else { + tracing::error!("abandoning delivery of {} from actor without private key: {}", delivery.activity, delivery.actor); + continue; + }; if let Err(e) = Context::request( Method::POST, &delivery.target, @@ -125,7 +120,7 @@ async fn worker(db: &DatabaseConnection, domain: &str, poll_interval: u64, waker ).await { tracing::warn!("failed delivery of {} to {} : {e}", delivery.activity, delivery.target); let new_delivery = model::delivery::ActiveModel { - id: sea_orm::ActiveValue::NotSet, + internal: sea_orm::ActiveValue::NotSet, not_before: sea_orm::ActiveValue::Set(delivery.next_delivery()), actor: sea_orm::ActiveValue::Set(delivery.actor), target: sea_orm::ActiveValue::Set(delivery.target), diff --git a/src/server/fetcher.rs b/src/server/fetcher.rs index 44830ff..9abeb88 100644 --- a/src/server/fetcher.rs +++ b/src/server/fetcher.rs @@ -1,9 +1,9 @@ use std::collections::BTreeMap; -use apb::{target::Addressed, Activity, Base, Collection, CollectionPage, Link, Object}; +use apb::{target::Addressed, Activity, Actor, ActorMut, Base, Collection, Link, Object}; use base64::Engine; use reqwest::{header::{ACCEPT, CONTENT_TYPE, USER_AGENT}, Method, Response}; -use sea_orm::{sea_query::Expr, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; +use sea_orm::EntityTrait; use crate::{errors::UpubError, model, VERSION}; @@ -13,14 +13,14 @@ use super::{httpsign::HttpSignature, normalizer::Normalizer, Context}; pub trait Fetcher { async fn webfinger(&self, user: &str, host: &str) -> crate::Result; - async fn fetch_user(&self, id: &str) -> crate::Result; - async fn pull_user(&self, id: &str) -> crate::Result; + async fn fetch_user(&self, id: &str) -> crate::Result; + async fn pull_user(&self, id: &str) -> crate::Result; async fn fetch_object(&self, id: &str) -> crate::Result; - async fn pull_object(&self, id: &str) -> crate::Result; + async fn pull_object(&self, id: &str) -> crate::Result; async fn fetch_activity(&self, id: &str) -> crate::Result; - async fn pull_activity(&self, id: &str) -> crate::Result; + async fn pull_activity(&self, id: &str) -> crate::Result; async fn fetch_thread(&self, id: &str) -> crate::Result<()>; @@ -115,30 +115,35 @@ impl Fetcher for Context { } - async fn fetch_user(&self, id: &str) -> crate::Result { - if let Some(x) = model::user::Entity::find_by_id(id).one(self.db()).await? { + async fn fetch_user(&self, id: &str) -> crate::Result { + if let Some(x) = model::actor::Entity::find_by_ap_id(id).one(self.db()).await? { return Ok(x); // already in db, easy } - let user_model = self.pull_user(id).await?; + let user_document = self.pull_user(id).await?; + let user_model = model::actor::ActiveModel::new(&user_document)?; // TODO this may fail: while fetching, remote server may fetch our service actor. // if it does so with http signature, we will fetch that actor in background // meaning that, once we reach here, it's already inserted and returns an UNIQUE error - model::user::Entity::insert(user_model.clone().into_active_model()) - .exec(self.db()).await?; - - Ok(user_model) + model::actor::Entity::insert(user_model).exec(self.db()).await?; + + // TODO fetch it back to get the internal id + Ok( + model::actor::Entity::find_by_ap_id(id) + .one(self.db()) + .await? + .ok_or_else(UpubError::internal_server_error)? + ) } - async fn pull_user(&self, id: &str) -> crate::Result { - let user = Self::request( + async fn pull_user(&self, id: &str) -> crate::Result { + let mut user = Self::request( Method::GET, id, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), ).await?.json::().await?; - let mut user_model = model::user::Model::new(&user)?; // TODO try fetching these numbers from audience/generator fields to avoid making 2 more GETs - if let Some(followers_url) = &user_model.followers { + if let Some(followers_url) = &user.followers().id() { let req = Self::request( Method::GET, followers_url, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), @@ -146,13 +151,13 @@ impl Fetcher for Context { if let Ok(res) = req { if let Ok(user_followers) = res.json::().await { if let Some(total) = user_followers.total_items() { - user_model.followers_count = total as i64; + user = user.set_followers_count(Some(total)); } } } } - if let Some(following_url) = &user_model.following { + if let Some(following_url) = &user.following().id() { let req = Self::request( Method::GET, following_url, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), @@ -160,33 +165,38 @@ impl Fetcher for Context { if let Ok(res) = req { if let Ok(user_following) = res.json::().await { if let Some(total) = user_following.total_items() { - user_model.following_count = total as i64; + user = user.set_following_count(Some(total)); } } } } - Ok(user_model) + Ok(user) } async fn fetch_activity(&self, id: &str) -> crate::Result { - if let Some(x) = model::activity::Entity::find_by_id(id).one(self.db()).await? { + if let Some(x) = model::activity::Entity::find_by_ap_id(id).one(self.db()).await? { return Ok(x); // already in db, easy } - let activity_model = self.pull_activity(id).await?; + let activity_document = self.pull_activity(id).await?; + let activity_model = model::activity::ActiveModel::new(&activity_document)?; - model::activity::Entity::insert(activity_model.clone().into_active_model()) + model::activity::Entity::insert(activity_model) .exec(self.db()).await?; - let addressed = activity_model.addressed(); - let expanded_addresses = self.expand_addressing(addressed).await?; - self.address_to(Some(&activity_model.id), None, &expanded_addresses).await?; + // TODO fetch it back to get the internal id + let activity = model::activity::Entity::find_by_ap_id(id) + .one(self.db()).await?.ok_or_else(UpubError::internal_server_error)?; - Ok(activity_model) + let addressed = activity.addressed(); + let expanded_addresses = self.expand_addressing(addressed).await?; + self.address_to(Some(&activity.id), None, &expanded_addresses).await?; + + Ok(activity) } - async fn pull_activity(&self, id: &str) -> crate::Result { + async fn pull_activity(&self, id: &str) -> crate::Result { let activity = Self::request( Method::GET, id, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), ).await?.json::().await?; @@ -203,84 +213,33 @@ impl Fetcher for Context { } } - let activity_model = model::activity::Model::new(&activity)?; - - Ok(activity_model) + Ok(activity) } async fn fetch_thread(&self, id: &str) -> crate::Result<()> { - crawl_replies(self, id, 0).await + // crawl_replies(self, id, 0).await + todo!() } async fn fetch_object(&self, id: &str) -> crate::Result { fetch_object_inner(self, id, 0).await } - async fn pull_object(&self, id: &str) -> crate::Result { - let object = Context::request( - Method::GET, id, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), - ).await?.json::().await?; - - Ok(model::object::Model::new(&object)?) + async fn pull_object(&self, id: &str) -> crate::Result { + Ok( + Context::request( + Method::GET, id, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), + ) + .await? + .json::() + .await? + ) } } -#[async_recursion::async_recursion] -async fn crawl_replies(ctx: &Context, id: &str, depth: usize) -> crate::Result<()> { - tracing::info!("crawling replies of '{id}'"); - let object = Context::request( - Method::GET, id, None, &format!("https://{}", ctx.domain()), &ctx.app().private_key, ctx.domain(), - ).await?.json::().await?; - - let object_model = model::object::Model::new(&object)?; - match model::object::Entity::insert(object_model.into_active_model()) - .exec(ctx.db()).await - { - Ok(_) => {}, - Err(sea_orm::DbErr::RecordNotInserted) => {}, - Err(sea_orm::DbErr::Exec(_)) => {}, // ughhh bad fix for sqlite - Err(e) => return Err(e.into()), - } - - if depth > 16 { - tracing::warn!("stopping thread crawling: too deep!"); - return Ok(()); - } - - let mut page_url = match object.replies().get() { - Some(serde_json::Value::String(x)) => { - let replies = Context::request( - Method::GET, x, None, &format!("https://{}", ctx.domain()), &ctx.app().private_key, ctx.domain(), - ).await?.json::().await?; - replies.first().id() - }, - Some(serde_json::Value::Object(x)) => { - let obj = serde_json::Value::Object(x.clone()); // lol putting it back, TODO! - obj.first().id() - }, - _ => return Ok(()), - }; - - while let Some(ref url) = page_url { - let replies = Context::request( - Method::GET, url, None, &format!("https://{}", ctx.domain()), &ctx.app().private_key, ctx.domain(), - ).await?.json::().await?; - - for reply in replies.items() { - // TODO right now it crawls one by one, could be made in parallel but would be quite more - // abusive, so i'll keep it like this while i try it out - crawl_replies(ctx, reply.href(), depth + 1).await?; - } - - page_url = replies.next().id(); - } - - Ok(()) -} - #[async_recursion::async_recursion] async fn fetch_object_inner(ctx: &Context, id: &str, depth: usize) -> crate::Result { - if let Some(x) = model::object::Entity::find_by_id(id).one(ctx.db()).await? { + if let Some(x) = model::object::Entity::find_by_ap_id(id).one(ctx.db()).await? { return Ok(x); // already in db, easy } @@ -290,7 +249,7 @@ async fn fetch_object_inner(ctx: &Context, id: &str, depth: usize) -> crate::Res if let Some(oid) = object.id() { if oid != id { - if let Some(x) = model::object::Entity::find_by_id(oid).one(ctx.db()).await? { + if let Some(x) = model::object::Entity::find_by_ap_id(oid).one(ctx.db()).await? { return Ok(x); // already in db, but with id different that given url } } @@ -341,3 +300,56 @@ impl Fetchable for apb::Node { Ok(self) } } + +// #[async_recursion::async_recursion] +// async fn crawl_replies(ctx: &Context, id: &str, depth: usize) -> crate::Result<()> { +// tracing::info!("crawling replies of '{id}'"); +// let object = Context::request( +// Method::GET, id, None, &format!("https://{}", ctx.domain()), &ctx.app().private_key, ctx.domain(), +// ).await?.json::().await?; +// +// let object_model = model::object::Model::new(&object)?; +// match model::object::Entity::insert(object_model.into_active_model()) +// .exec(ctx.db()).await +// { +// Ok(_) => {}, +// Err(sea_orm::DbErr::RecordNotInserted) => {}, +// Err(sea_orm::DbErr::Exec(_)) => {}, // ughhh bad fix for sqlite +// Err(e) => return Err(e.into()), +// } +// +// if depth > 16 { +// tracing::warn!("stopping thread crawling: too deep!"); +// return Ok(()); +// } +// +// let mut page_url = match object.replies().get() { +// Some(serde_json::Value::String(x)) => { +// let replies = Context::request( +// Method::GET, x, None, &format!("https://{}", ctx.domain()), &ctx.app().private_key, ctx.domain(), +// ).await?.json::().await?; +// replies.first().id() +// }, +// Some(serde_json::Value::Object(x)) => { +// let obj = serde_json::Value::Object(x.clone()); // lol putting it back, TODO! +// obj.first().id() +// }, +// _ => return Ok(()), +// }; +// +// while let Some(ref url) = page_url { +// let replies = Context::request( +// Method::GET, url, None, &format!("https://{}", ctx.domain()), &ctx.app().private_key, ctx.domain(), +// ).await?.json::().await?; +// +// for reply in replies.items() { +// // TODO right now it crawls one by one, could be made in parallel but would be quite more +// // abusive, so i'll keep it like this while i try it out +// crawl_replies(ctx, reply.href(), depth + 1).await?; +// } +// +// page_url = replies.next().id(); +// } +// +// Ok(()) +// } diff --git a/src/server/normalizer.rs b/src/server/normalizer.rs index f4fe2db..a4d2fde 100644 --- a/src/server/normalizer.rs +++ b/src/server/normalizer.rs @@ -1,5 +1,5 @@ use apb::{Node, Base, Object, Document}; -use sea_orm::{sea_query::Expr, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, Set}; +use sea_orm::{sea_query::Expr, ActiveValue::Set, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; use crate::{errors::UpubError, model, server::Context}; use super::fetcher::Fetcher; @@ -12,23 +12,23 @@ pub trait Normalizer { #[axum::async_trait] impl Normalizer for super::Context { async fn insert_object(&self, object_node: impl apb::Object, server: Option) -> crate::Result { - let mut object_model = model::object::Model::new(&object_node)?; - let oid = object_model.id.clone(); - let uid = object_model.attributed_to.clone(); + let oid = object_node.id().ok_or_else(UpubError::bad_request)?.to_string(); + let uid = object_node.attributed_to().id(); + let mut object_model = model::object::ActiveModel::new(&object_node)?; if let Some(server) = server { // make sure we're allowed to create this object - if let Some(object_author) = &object_model.attributed_to { + if let Set(Some(object_author)) = &object_model.attributed_to { if server != Context::server(object_author) { return Err(UpubError::forbidden()); } - } else if server != Context::server(&object_model.id) { + } else if server != Context::server(&oid) { return Err(UpubError::forbidden()); }; } // make sure content only contains a safe subset of html - if let Some(content) = object_model.content { - object_model.content = Some(mdhtml::safe_html(&content)); + if let Set(Some(content)) = object_model.content { + object_model.content = Set(Some(mdhtml::safe_html(&content))); } // fix context for remote posts @@ -37,33 +37,34 @@ impl Normalizer for super::Context { // > btw! also if any link is broken or we get rate limited, the whole insertion fails which is // > kind of dumb. there should be a job system so this can be done in waves. or maybe there's // > some whole other way to do this?? im thinking but misskey aaaa!! TODO - if let Some(ref reply) = object_model.in_reply_to { - if let Some(o) = model::object::Entity::find_by_id(reply).one(self.db()).await? { - object_model.context = o.context; + if let Set(Some(ref reply)) = object_model.in_reply_to { + if let Some(o) = model::object::Entity::find_by_ap_id(reply).one(self.db()).await? { + object_model.context = Set(o.context); } else { - object_model.context = None; // TODO to be filled by some other task + object_model.context = Set(None); // TODO to be filled by some other task } } else { - object_model.context = Some(object_model.id.clone()); + object_model.context = Set(Some(oid.clone())); } model::object::Entity::insert(object_model.clone().into_active_model()).exec(self.db()).await?; + let object = model::object::Entity::find_by_ap_id(&oid).one(self.db()).await?.ok_or_else(UpubError::internal_server_error)?; // update replies counter - if let Some(ref in_reply_to) = object_model.in_reply_to { + if let Set(Some(ref in_reply_to)) = object_model.in_reply_to { if self.fetch_object(in_reply_to).await.is_ok() { model::object::Entity::update_many() .filter(model::object::Column::Id.eq(in_reply_to)) - .col_expr(model::object::Column::Comments, Expr::col(model::object::Column::Comments).add(1)) + .col_expr(model::object::Column::Replies, Expr::col(model::object::Column::Replies).add(1)) .exec(self.db()) .await?; } } // update statuses counter if let Some(object_author) = uid { - model::user::Entity::update_many() - .col_expr(model::user::Column::StatusesCount, Expr::col(model::user::Column::StatusesCount).add(1)) - .filter(model::user::Column::Id.eq(&object_author)) + model::actor::Entity::update_many() + .col_expr(model::actor::Column::StatusesCount, Expr::col(model::actor::Column::StatusesCount).add(1)) + .filter(model::actor::Column::Id.eq(&object_author)) .exec(self.db()) .await?; } @@ -76,18 +77,18 @@ impl Normalizer for super::Context { continue }, Node::Link(l) => model::attachment::ActiveModel { - id: sea_orm::ActiveValue::NotSet, + internal: sea_orm::ActiveValue::NotSet, url: Set(l.href().to_string()), - object: Set(oid.clone()), + object: Set(object.internal), document_type: Set(apb::DocumentType::Page), name: Set(l.link_name().map(|x| x.to_string())), media_type: Set(l.link_media_type().unwrap_or("link").to_string()), created: Set(chrono::Utc::now()), }, Node::Object(o) => model::attachment::ActiveModel { - id: sea_orm::ActiveValue::NotSet, + internal: sea_orm::ActiveValue::NotSet, url: Set(o.url().id().unwrap_or_else(|| o.id().map(|x| x.to_string()).unwrap_or_default())), - object: Set(oid.clone()), + object: Set(object.internal), document_type: Set(o.as_document().map_or(apb::DocumentType::Document, |x| x.document_type().unwrap_or(apb::DocumentType::Page))), name: Set(o.name().map(|x| x.to_string())), media_type: Set(o.media_type().unwrap_or("link").to_string()), @@ -113,9 +114,9 @@ impl Normalizer for super::Context { }; let attachment_model = model::attachment::ActiveModel { - id: sea_orm::ActiveValue::NotSet, + internal: sea_orm::ActiveValue::NotSet, url: Set(img.url().id().unwrap_or_else(|| img.id().map(|x| x.to_string()).unwrap_or_default())), - object: Set(oid.clone()), + object: Set(object.internal), document_type: Set(img.as_document().map_or(apb::DocumentType::Document, |x| x.document_type().unwrap_or(apb::DocumentType::Page))), name: Set(img.name().map(|x| x.to_string())), media_type: Set(img.media_type().unwrap_or(media_type.as_deref().unwrap_or("link")).to_string()), @@ -126,6 +127,6 @@ impl Normalizer for super::Context { .await?; } - Ok(object_model) + Ok(object) } } From 322b18e9cde3a7771fdfb38a156277165df743e6 Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 25 May 2024 07:00:03 +0200 Subject: [PATCH 08/90] chore: helpers for internal ids, fix routes and ctx basically just need to do inbox/outbox? then there's still some issues with relays relations and auth extra selects but may actually work again --- ...001_create_actor_activity_object_tables.rs | 8 +- ...24_000002_create_relations_likes_shares.rs | 4 +- ...524_000004_create_addressing_deliveries.rs | 4 +- ...000005_create_attachments_tags_mentions.rs | 4 +- src/model/actor.rs | 6 +- src/model/addressing.rs | 9 ++ src/model/attachment.rs | 4 +- src/model/delivery.rs | 4 +- src/routes/activitypub/activity.rs | 2 +- src/routes/activitypub/application.rs | 12 +- src/routes/activitypub/inbox.rs | 5 +- src/routes/activitypub/user/inbox.rs | 6 +- src/routes/activitypub/user/outbox.rs | 4 +- src/routes/mastodon/accounts.rs | 2 +- src/server/admin.rs | 4 +- src/server/auth.rs | 59 ++++---- src/server/builders.rs | 4 +- src/server/context.rs | 136 ++++++++++++------ src/server/dispatcher.rs | 2 +- src/server/fetcher.rs | 22 ++- src/server/normalizer.rs | 6 +- src/server/outbox.rs | 44 +++--- 22 files changed, 212 insertions(+), 139 deletions(-) diff --git a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs index 7c3439f..a7143af 100644 --- a/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs +++ b/src/migrations/m20240524_000001_create_actor_activity_object_tables.rs @@ -21,7 +21,7 @@ pub enum Actors { StatusesCount, PublicKey, PrivateKey, - Created, + Published, Updated, } @@ -159,7 +159,7 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Actors::StatusesCount).integer().not_null().default(0)) .col(ColumnDef::new(Actors::PublicKey).string().not_null()) .col(ColumnDef::new(Actors::PrivateKey).string().null()) - .col(ColumnDef::new(Actors::Created).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Actors::Published).date_time().not_null().default(Expr::current_timestamp())) .col(ColumnDef::new(Actors::Updated).date_time().not_null().default(Expr::current_timestamp())) .to_owned() ) @@ -174,7 +174,7 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().name("index-actors-instance").table(Actors::Table).col(Actors::Instance).to_owned()) + .create_index(Index::create().name("index-actors-domain").table(Actors::Table).col(Actors::Domain).to_owned()) .await?; @@ -321,7 +321,7 @@ impl MigrationTrait for Migration { .await?; manager - .drop_index(Index::drop().name("index-actors-instance").table(Actors::Table).to_owned()) + .drop_index(Index::drop().name("index-actors-domain").table(Actors::Table).to_owned()) .await?; diff --git a/src/migrations/m20240524_000002_create_relations_likes_shares.rs b/src/migrations/m20240524_000002_create_relations_likes_shares.rs index cceddd9..fbd8504 100644 --- a/src/migrations/m20240524_000002_create_relations_likes_shares.rs +++ b/src/migrations/m20240524_000002_create_relations_likes_shares.rs @@ -136,7 +136,7 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().name("index-likes-object").table(Likes::Table).col(Likes::Likes).to_owned()) + .create_index(Index::create().name("index-likes-object").table(Likes::Table).col(Likes::Object).to_owned()) .await?; manager @@ -190,7 +190,7 @@ impl MigrationTrait for Migration { .await?; manager - .create_index(Index::create().name("index-announces-object").table(Announces::Table).col(Announces::Announces).to_owned()) + .create_index(Index::create().name("index-announces-object").table(Announces::Table).col(Announces::Object).to_owned()) .await?; Ok(()) diff --git a/src/migrations/m20240524_000004_create_addressing_deliveries.rs b/src/migrations/m20240524_000004_create_addressing_deliveries.rs index 2c82461..0e1ecb2 100644 --- a/src/migrations/m20240524_000004_create_addressing_deliveries.rs +++ b/src/migrations/m20240524_000004_create_addressing_deliveries.rs @@ -20,7 +20,7 @@ pub enum Deliveries { Actor, Target, Activity, - Created, + Published, NotBefore, Attempt, } @@ -141,7 +141,7 @@ impl MigrationTrait for Migration { .on_update(ForeignKeyAction::Cascade) .on_delete(ForeignKeyAction::Cascade) ) - .col(ColumnDef::new(Deliveries::Created).date_time().not_null().default(Expr::current_timestamp())) + .col(ColumnDef::new(Deliveries::Published).date_time().not_null().default(Expr::current_timestamp())) .col(ColumnDef::new(Deliveries::NotBefore).date_time().not_null().default(Expr::current_timestamp())) .col(ColumnDef::new(Deliveries::Attempt).integer().not_null().default(0)) .to_owned() diff --git a/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs b/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs index bf0c2c5..863bcf5 100644 --- a/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs +++ b/src/migrations/m20240524_000005_create_attachments_tags_mentions.rs @@ -11,7 +11,7 @@ pub enum Attachments { Object, Name, MediaType, - Created, + Published, } #[derive(DeriveIden)] @@ -63,7 +63,7 @@ impl MigrationTrait for Migration { .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().default(Expr::current_timestamp())) + .col(ColumnDef::new(Attachments::Published).date_time().not_null().default(Expr::current_timestamp())) .to_owned() ) .await?; diff --git a/src/model/actor.rs b/src/model/actor.rs index cd41d08..6a8086f 100644 --- a/src/model/actor.rs +++ b/src/model/actor.rs @@ -29,7 +29,7 @@ pub struct Model { pub statuses_count: i32, pub public_key: String, pub private_key: Option, - pub created: ChronoDateTimeUtc, + pub published: ChronoDateTimeUtc, pub updated: ChronoDateTimeUtc, } @@ -169,7 +169,7 @@ impl ActiveModel { shared_inbox: sea_orm::ActiveValue::Set(object.endpoints().get().and_then(|x| Some(x.shared_inbox()?.to_string()))), followers: sea_orm::ActiveValue::Set(object.followers().id()), following: sea_orm::ActiveValue::Set(object.following().id()), - created: sea_orm::ActiveValue::Set(object.published().unwrap_or(chrono::Utc::now())), + published: sea_orm::ActiveValue::Set(object.published().unwrap_or(chrono::Utc::now())), updated: sea_orm::ActiveValue::Set(chrono::Utc::now()), following_count: sea_orm::ActiveValue::Set(object.following_count().unwrap_or(0) as i32), followers_count: sea_orm::ActiveValue::Set(object.followers_count().unwrap_or(0) as i32), @@ -197,7 +197,7 @@ impl Model { .set_document_type(Some(apb::DocumentType::Image)) .set_url(apb::Node::link(i.clone())) ))) - .set_published(Some(self.created)) + .set_published(Some(self.published)) .set_preferred_username(Some(&self.preferred_username)) .set_statuses_count(Some(self.statuses_count as u64)) .set_followers_count(Some(self.followers_count as u64)) diff --git a/src/model/addressing.rs b/src/model/addressing.rs index 2eb6ecf..72ac9b3 100644 --- a/src/model/addressing.rs +++ b/src/model/addressing.rs @@ -106,6 +106,15 @@ impl Event { } } + pub fn internal(&self) -> i64 { + match self { + Event::Tombstone => 0, + Event::Activity(x) => x.internal, + Event::StrayObject { object, liked: _ } => object.internal, + Event::DeepActivity { activity: _, liked: _, object } => object.internal, + } + } + pub fn ap(self, attachment: Option>) -> serde_json::Value { let attachment = match attachment { None => apb::Node::Empty, diff --git a/src/model/attachment.rs b/src/model/attachment.rs index 93c6ea4..ac32246 100644 --- a/src/model/attachment.rs +++ b/src/model/attachment.rs @@ -16,7 +16,7 @@ pub struct Model { pub document_type: DocumentType, pub name: Option, pub media_type: String, - pub created: ChronoDateTimeUtc, + pub published: ChronoDateTimeUtc, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -46,7 +46,7 @@ impl Model { .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)) + .set_published(Some(self.published)) } } diff --git a/src/model/delivery.rs b/src/model/delivery.rs index eac7aa0..b767f67 100644 --- a/src/model/delivery.rs +++ b/src/model/delivery.rs @@ -8,7 +8,7 @@ pub struct Model { pub actor: String, pub target: String, pub activity: String, - pub created: ChronoDateTimeUtc, + pub published: ChronoDateTimeUtc, pub not_before: ChronoDateTimeUtc, pub attempt: i32, } @@ -61,6 +61,6 @@ impl Model { } pub fn expired(&self) -> bool { - chrono::Utc::now() - self.created > chrono::Duration::days(7) + chrono::Utc::now() - self.published > chrono::Duration::days(7) } } diff --git a/src/routes/activitypub/activity.rs b/src/routes/activitypub/activity.rs index bc15a35..a7880bc 100644 --- a/src/routes/activitypub/activity.rs +++ b/src/routes/activitypub/activity.rs @@ -27,7 +27,7 @@ pub async fn view( .ok_or_else(UpubError::not_found)?; let mut attachments = row.load_attachments_batch(ctx.db()).await?; - let attach = attachments.remove(row.id()); + let attach = attachments.remove(&row.internal()); Ok(JsonLD(row.ap(attach).ld_context())) } diff --git a/src/routes/activitypub/application.rs b/src/routes/activitypub/application.rs index 7d9aa4e..594d265 100644 --- a/src/routes/activitypub/application.rs +++ b/src/routes/activitypub/application.rs @@ -2,7 +2,7 @@ use apb::{ActorMut, BaseMut, ObjectMut, PublicKeyMut}; use axum::{extract::{Query, State}, http::HeaderMap, response::{IntoResponse, Redirect, Response}, Form, Json}; use reqwest::Method; -use crate::{errors::UpubError, server::{auth::{AuthIdentity, Identity}, fetcher::Fetcher, Context}, url}; +use crate::{errors::UpubError, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url}; use super::{jsonld::LD, JsonLD}; @@ -26,7 +26,7 @@ pub async fn view( .set_summary(Some(&ctx.cfg().instance.description)) .set_inbox(apb::Node::link(url!(ctx, "/inbox"))) .set_outbox(apb::Node::link(url!(ctx, "/outbox"))) - .set_published(Some(ctx.app().created)) + .set_published(Some(ctx.app().published)) .set_endpoints(apb::Node::Empty) .set_preferred_username(Some(ctx.domain())) .set_public_key(apb::Node::object( @@ -50,7 +50,7 @@ pub async fn proxy_get( AuthIdentity(auth): AuthIdentity, ) -> crate::Result> { // only local users can request fetches - if !ctx.cfg().security.allow_public_debugger && !matches!(auth, Identity::Local(_)) { + if !ctx.cfg().security.allow_public_debugger && !auth.is_local() { return Err(UpubError::unauthorized()); } Ok(Json( @@ -59,7 +59,7 @@ pub async fn proxy_get( &query.id, None, ctx.base(), - &ctx.app().private_key, + ctx.pkey(), &format!("{}+proxy", ctx.domain()), ) .await? @@ -74,7 +74,7 @@ pub async fn proxy_form( Form(query): Form, ) -> crate::Result> { // only local users can request fetches - if !ctx.cfg().security.allow_public_debugger && !matches!(auth, Identity::Local(_)) { + if !ctx.cfg().security.allow_public_debugger && auth.is_local() { return Err(UpubError::unauthorized()); } Ok(Json( @@ -83,7 +83,7 @@ pub async fn proxy_form( &query.id, None, ctx.base(), - &ctx.app().private_key, + ctx.pkey(), &format!("{}+proxy", ctx.domain()), ) .await? diff --git a/src/routes/activitypub/inbox.rs b/src/routes/activitypub/inbox.rs index 1aec234..a0a0a23 100644 --- a/src/routes/activitypub/inbox.rs +++ b/src/routes/activitypub/inbox.rs @@ -41,7 +41,7 @@ pub async fn post( AuthIdentity(auth): AuthIdentity, Json(activity): Json ) -> crate::Result<()> { - let Identity::Remote(server) = auth else { + let Identity::Remote { domain: server, .. } = auth else { if activity.activity_type() == Some(ActivityType::Delete) { // this is spammy af, ignore them! // we basically received a delete for a user we can't fetch and verify, meaning remote @@ -63,8 +63,7 @@ pub async fn post( return Err(UpubError::bad_request()); }; - // TODO add whitelist of relays - if !server.ends_with(&Context::server(&actor)) { + if !(server == Context::server(&actor)) { return Err(UpubError::unauthorized()); } diff --git a/src/routes/activitypub/user/inbox.rs b/src/routes/activitypub/user/inbox.rs index 1d02f09..5e9a7a9 100644 --- a/src/routes/activitypub/user/inbox.rs +++ b/src/routes/activitypub/user/inbox.rs @@ -10,8 +10,8 @@ pub async fn get( ) -> crate::Result> { match auth { Identity::Anonymous => Err(StatusCode::FORBIDDEN.into()), - Identity::Remote(_) => Err(StatusCode::FORBIDDEN.into()), - Identity::Local(user) => if ctx.uid(&id) == user { + Identity::Remote { .. } => Err(StatusCode::FORBIDDEN.into()), + Identity::Local { id: user, .. } => if ctx.uid(&id) == user { crate::server::builders::collection(&url!(ctx, "/users/{id}/inbox"), None) } else { Err(StatusCode::FORBIDDEN.into()) @@ -25,7 +25,7 @@ pub async fn page( AuthIdentity(auth): AuthIdentity, Query(page): Query, ) -> crate::Result> { - let Identity::Local(uid) = &auth else { + let Identity::Local { id: uid, .. } = &auth else { // local inbox is only for local users return Err(UpubError::forbidden()); }; diff --git a/src/routes/activitypub/user/outbox.rs b/src/routes/activitypub/user/outbox.rs index d1be16f..774eb75 100644 --- a/src/routes/activitypub/user/outbox.rs +++ b/src/routes/activitypub/user/outbox.rs @@ -42,8 +42,8 @@ pub async fn post( ) -> Result { match auth { Identity::Anonymous => Err(StatusCode::UNAUTHORIZED.into()), - Identity::Remote(_) => Err(StatusCode::NOT_IMPLEMENTED.into()), - Identity::Local(uid) => if ctx.uid(&id) == uid { + Identity::Remote { .. } => Err(StatusCode::NOT_IMPLEMENTED.into()), + Identity::Local { id: uid, .. } => if ctx.uid(&id) == uid { tracing::debug!("processing new local activity: {}", serde_json::to_string(&activity).unwrap_or_default()); match activity.base_type() { None => Err(StatusCode::BAD_REQUEST.into()), diff --git a/src/routes/mastodon/accounts.rs b/src/routes/mastodon/accounts.rs index f1bea0e..ef9581f 100644 --- a/src/routes/mastodon/accounts.rs +++ b/src/routes/mastodon/accounts.rs @@ -20,7 +20,7 @@ pub async fn view( acct: x.preferred_username.clone(), avatar: x.icon.as_deref().unwrap_or("").to_string(), avatar_static: x.icon.unwrap_or_default(), - created_at: time::OffsetDateTime::from_unix_timestamp(x.created.timestamp()).unwrap(), + created_at: time::OffsetDateTime::from_unix_timestamp(x.published.timestamp()).unwrap(), display_name: x.name.unwrap_or_default(), // TODO hide these maybe followers_count: x.followers_count as u64, diff --git a/src/server/admin.rs b/src/server/admin.rs index c5f70e9..ca69feb 100644 --- a/src/server/admin.rs +++ b/src/server/admin.rs @@ -28,7 +28,7 @@ impl Administrable for super::Context { let ap_id = self.uid(&username); let db = self.db(); let domain = self.domain().to_string(); - let user_model = crate::model::actor::Model { + let user_model = crate::model::actor::ActiveModel { internal: NotSet, id: Set(ap_id.clone()), name: Set(display_name), @@ -46,7 +46,7 @@ impl Administrable for super::Context { shared_inbox: Set(None), outbox: Set(None), actor_type: Set(apb::ActorType::Person), - created: Set(chrono::Utc::now()), + published: Set(chrono::Utc::now()), updated: Set(chrono::Utc::now()), private_key: Set(Some(std::str::from_utf8(&key.private_key_to_pem().unwrap()).unwrap().to_string())), public_key: Set(std::str::from_utf8(&key.public_key_to_pem().unwrap()).unwrap().to_string()), diff --git a/src/server/auth.rs b/src/server/auth.rs index 2470e86..1bba370 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -9,8 +9,14 @@ use super::{fetcher::Fetcher, httpsign::HttpSignature}; #[derive(Debug, Clone)] pub enum Identity { Anonymous, - Local(String), - Remote(String), + Remote { + domain: String, + internal: i64, + }, + Local { + id: String, + internal: i64, + }, } impl Identity { @@ -18,18 +24,18 @@ impl Identity { let base_cond = Condition::any().add(model::addressing::Column::Actor.eq(apb::target::PUBLIC)); match self { Identity::Anonymous => base_cond, - Identity::Remote(server) => base_cond.add(model::addressing::Column::Server.eq(server)), + Identity::Remote { internal, .. } => base_cond.add(model::addressing::Column::Instance.eq(*internal)), // TODO should we allow all users on same server to see? or just specific user?? - Identity::Local(uid) => base_cond - .add(model::addressing::Column::Actor.eq(uid)) - .add(model::activity::Column::Actor.eq(uid)) - .add(model::object::Column::AttributedTo.eq(uid)), + Identity::Local { id, internal } => base_cond + .add(model::addressing::Column::Actor.eq(*internal)) + .add(model::activity::Column::Actor.eq(id)) + .add(model::object::Column::AttributedTo.eq(id)), } } - pub fn my_id(&self) -> Option<&str> { + pub fn my_id(&self) -> Option { match self { - Identity::Local(x) => Some(x.as_str()), + Identity::Local { internal, .. } => Some(*internal), _ => None, } } @@ -37,8 +43,8 @@ impl Identity { pub fn is(&self, id: &str) -> bool { match self { Identity::Anonymous => false, - Identity::Remote(_) => false, // TODO per-actor server auth should check this - Identity::Local(uid) => uid.as_str() == id + Identity::Remote { .. } => false, // TODO per-actor server auth should check this + Identity::Local { id, .. } => id.as_str() == id } } @@ -47,23 +53,16 @@ impl Identity { } pub fn is_local(&self) -> bool { - matches!(self, Self::Local(_)) + matches!(self, Self::Local { .. }) } pub fn is_remote(&self) -> bool { - matches!(self, Self::Remote(_)) + matches!(self, Self::Remote { .. }) } pub fn is_local_user(&self, uid: &str) -> bool { match self { - Self::Local(x) => x == uid, - _ => false, - } - } - - pub fn is_remote_server(&self, uid: &str) -> bool { - match self { - Self::Remote(x) => x == uid, + Self::Local { id, .. } => id == uid, _ => false, } } @@ -90,13 +89,19 @@ where .unwrap_or(""); if auth_header.starts_with("Bearer ") { - match model::session::Entity::find_by_id(auth_header.replace("Bearer ", "")) + match model::session::Entity::find() + .filter(model::session::Column::Secret.eq(auth_header.replace("Bearer ", ""))) .filter(model::session::Column::Expires.gt(chrono::Utc::now())) .one(ctx.db()) .await { - Ok(Some(x)) => identity = Identity::Local(x.actor), Ok(None) => return Err(UpubError::unauthorized()), + Ok(Some(x)) => { + // TODO could we store both actor ap id and internal id in session? to avoid this extra + // lookup on *every* local authed request we receive... + let internal = model::actor::Entity::ap_to_internal(&x.actor, ctx.db()).await?; + identity = Identity::Local { id: x.actor, internal }; + }, Err(e) => { tracing::error!("failed querying user session: {e}"); return Err(UpubError::internal_server_error()) @@ -122,7 +127,13 @@ where .build_from_parts(parts) .verify(&user.public_key) { - Ok(true) => identity = Identity::Remote(Context::server(&user_id)), + Ok(true) => { + // TODO can we avoid this extra db rountrip made on each server fetch? + let domain = Context::server(&user_id); + // TODO this will fail because we never fetch and insert into instance oops + let internal = model::instance::Entity::domain_to_internal(&domain, ctx.db()).await?; + identity = Identity::Remote { domain, internal }; + }, Ok(false) => tracing::warn!("invalid signature: {http_signature:?}"), Err(e) => tracing::error!("error verifying signature: {e}"), }, diff --git a/src/server/builders.rs b/src/server/builders.rs index 7664d63..039883b 100644 --- a/src/server/builders.rs +++ b/src/server/builders.rs @@ -8,7 +8,7 @@ pub async fn paginate( filter: Condition, db: &DatabaseConnection, page: Pagination, - my_id: Option<&str>, + my_id: Option, ) -> crate::Result> { let limit = page.batch.unwrap_or(20).min(50); let offset = page.offset.unwrap_or(0); @@ -27,7 +27,7 @@ pub async fn paginate( let items : Vec = items .into_iter() .map(|item| { - let attach = attachments.remove(item.id()); + let attach = attachments.remove(&item.internal()); item.ap(attach) }) .collect(); diff --git a/src/server/context.rs b/src/server/context.rs index 2021622..3064568 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -1,9 +1,9 @@ use std::{collections::BTreeSet, sync::Arc}; use openssl::rsa::Rsa; -use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, SelectColumns, Set}; +use sea_orm::{ActiveValue::NotSet, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, RelationTrait, SelectColumns, Set}; -use crate::{config::Config, model, server::fetcher::Fetcher}; +use crate::{config::Config, errors::UpubError, model, server::fetcher::Fetcher}; use uriproxy::UriClass; use super::dispatcher::Dispatcher; @@ -19,7 +19,8 @@ struct ContextInner { base_url: String, dispatcher: Dispatcher, // TODO keep these pre-parsed - app: model::application::Model, + app: model::actor::Model, + pkey: String, relays: BTreeSet, } @@ -46,44 +47,73 @@ impl Context { for _ in 0..1 { // TODO customize delivery workers amount dispatcher.spawn(db.clone(), domain.clone(), 30); // TODO ew don't do it this deep and secretly!! } - let app = match model::application::Entity::find().one(&db).await? { + let base_url = format!("{}{}", protocol, domain); + let app = match model::actor::Entity::find_by_ap_id(&base_url).one(&db).await? { Some(model) => model, None => { tracing::info!("generating application keys"); let rsa = Rsa::generate(2048)?; let privk = std::str::from_utf8(&rsa.private_key_to_pem()?)?.to_string(); let pubk = std::str::from_utf8(&rsa.public_key_to_pem()?)?.to_string(); - let system = model::application::ActiveModel { - id: sea_orm::ActiveValue::NotSet, - private_key: sea_orm::ActiveValue::Set(privk.clone()), - public_key: sea_orm::ActiveValue::Set(pubk.clone()), - created: sea_orm::ActiveValue::Set(chrono::Utc::now()), + let system = model::actor::ActiveModel { + internal: NotSet, + id: NotSet, + domain: Set(domain.clone()), + preferred_username: Set(domain.clone()), + actor_type: Set(apb::ActorType::Application), + private_key: Set(Some(privk)), + public_key: Set(pubk), + following: Set(None), + following_count: Set(0), + followers: Set(None), + followers_count: Set(0), + statuses_count: Set(0), + summary: Set(Some("micro social network, federated".to_string())), + name: Set(Some("μpub".to_string())), + image: Set(None), + icon: Set(Some("https://cdn.alemi.dev/social/circle-square.png".to_string())), + inbox: Set(Some(format!("{base_url}/inbox"))), + shared_inbox: Set(Some(format!("{base_url}/inbox"))), + outbox: Set(Some(format!("{base_url}/outbox"))), + published: Set(chrono::Utc::now()), + updated: Set(chrono::Utc::now()), }; - model::application::Entity::insert(system).exec(&db).await?; + model::actor::Entity::insert(system).exec(&db).await?; // sqlite doesn't resurn last inserted id so we're better off just querying again, it's just one time - model::application::Entity::find().one(&db).await?.expect("could not find app config just inserted") + model::actor::Entity::find().one(&db).await?.expect("could not find app config just inserted") } }; - let relays = model::relay::Entity::find() - .select_only() - .select_column(model::relay::Column::Id) - .filter(model::relay::Column::Accepted.eq(true)) - .into_tuple::() - .all(&db) - .await?; + // TODO maybe we could provide a more descriptive error... + let pkey = app.private_key.as_deref().ok_or_else(UpubError::internal_server_error)?.to_string(); + + // TODO how to handle relations when there's two to the same model??? + // let relays = model::relation::Entity::find() + // .filter(model::relation::Column::Following.eq(app.internal)) + // .filter(model::relation::Column::Accept.is_not_null()) + // .left_join(model::relation::Relation::ActorsFollower.def()) + // .select_only() + // .select_column(model::actor::Column::Id) + // .into_tuple::() + // .all(&db) + // .await?; + + let relays = Vec::new(); Ok(Context(Arc::new(ContextInner { - base_url: format!("{}{}", protocol, domain), - db, domain, protocol, app, dispatcher, config, + base_url, db, domain, protocol, app, dispatcher, config, pkey, relays: BTreeSet::from_iter(relays.into_iter()), }))) } - pub fn app(&self) -> &model::application::Model { + pub fn app(&self) -> &model::actor::Model { &self.0.app } + pub fn pkey(&self) -> &str { + &self.0.pkey + } + pub fn db(&self) -> &DatabaseConnection { &self.0.db } @@ -150,6 +180,18 @@ impl Context { id.starts_with(self.base()) } + pub async fn is_local_internal_object(&self, internal: i64) -> crate::Result { + Ok(model::object::Entity::find_by_id(internal).select_only().one(self.db()).await?.is_some()) + } + + pub async fn is_local_internal_activity(&self, internal: i64) -> crate::Result { + Ok(model::activity::Entity::find_by_id(internal).select_only().one(self.db()).await?.is_some()) + } + + pub async fn is_local_internal_actor(&self, internal: i64) -> crate::Result { + Ok(model::actor::Entity::find_by_id(internal).select_only().one(self.db()).await?.is_some()) + } + pub async fn expand_addressing(&self, targets: Vec) -> crate::Result> { let mut out = Vec::new(); for target in targets { @@ -171,26 +213,38 @@ impl Context { Ok(out) } - pub async fn address_to(&self, aid: Option<&str>, oid: Option<&str>, targets: &[String]) -> crate::Result<()> { - let local_activity = aid.map(|x| self.is_local(x)).unwrap_or(false); - let local_object = oid.map(|x| self.is_local(x)).unwrap_or(false); - let addressings : Vec = targets + pub async fn address_to(&self, aid: Option, oid: Option, targets: &[String]) -> crate::Result<()> { + // TODO address_to became kind of expensive, with these two selects right away and then another + // select for each target we're addressing to... can this be improved?? + let local_activity = if let Some(x) = aid { self.is_local_internal_activity(x).await? } else { false }; + let local_object = if let Some(x) = oid { self.is_local_internal_object(x).await? } else { false }; + let mut addressing = Vec::new(); + for target in targets .iter() .filter(|to| !to.is_empty()) .filter(|to| !to.ends_with("/followers")) .filter(|to| local_activity || local_object || to.as_str() == apb::target::PUBLIC || self.is_local(to)) - .map(|to| model::addressing::ActiveModel { - id: sea_orm::ActiveValue::NotSet, - server: Set(Context::server(to)), - actor: Set(to.to_string()), - activity: Set(aid.map(|x| x.to_string())), - object: Set(oid.map(|x| x.to_string())), - published: Set(chrono::Utc::now()), - }) - .collect(); + { + let (server, actor) = if target == apb::target::PUBLIC { (None, None) } else { + ( + Some(model::instance::Entity::domain_to_internal(&Context::server(&target), self.db()).await?), + Some(model::actor::Entity::ap_to_internal(&target, self.db()).await?), + ) + }; + addressing.push( + model::addressing::ActiveModel { + internal: sea_orm::ActiveValue::NotSet, + instance: Set(server), + actor: Set(actor), + activity: Set(aid), + object: Set(oid), + published: Set(chrono::Utc::now()), + } + ); + } - if !addressings.is_empty() { - model::addressing::Entity::insert_many(addressings) + if !addressing.is_empty() { + model::addressing::Entity::insert_many(addressing) .exec(self.db()) .await?; } @@ -207,15 +261,15 @@ impl Context { { // TODO fetch concurrently match self.fetch_user(target).await { - Ok(model::user::Model { inbox: Some(inbox), .. }) => deliveries.push( + Ok(model::actor::Model { inbox: Some(inbox), .. }) => deliveries.push( model::delivery::ActiveModel { - id: sea_orm::ActiveValue::NotSet, + internal: sea_orm::ActiveValue::NotSet, actor: Set(from.to_string()), // TODO we should resolve each user by id and check its inbox because we can't assume // it's /users/{id}/inbox for every software, but oh well it's waaaaay easier now target: Set(inbox), activity: Set(aid.to_string()), - created: Set(chrono::Utc::now()), + published: Set(chrono::Utc::now()), not_before: Set(chrono::Utc::now()), attempt: Set(0), } @@ -238,7 +292,9 @@ impl Context { pub async fn dispatch(&self, uid: &str, activity_targets: Vec, aid: &str, oid: Option<&str>) -> crate::Result<()> { let addressed = self.expand_addressing(activity_targets).await?; - self.address_to(Some(aid), oid, &addressed).await?; + let internal_aid = model::activity::Entity::ap_to_internal(aid, self.db()).await?; + let internal_oid = if let Some(o) = oid { Some(model::object::Entity::ap_to_internal(o, self.db()).await?) } else { None }; + self.address_to(Some(internal_aid), internal_oid, &addressed).await?; self.deliver_to(aid, uid, &addressed).await?; Ok(()) } diff --git a/src/server/dispatcher.rs b/src/server/dispatcher.rs index f48f5a6..e24620c 100644 --- a/src/server/dispatcher.rs +++ b/src/server/dispatcher.rs @@ -125,7 +125,7 @@ async fn worker(db: &DatabaseConnection, domain: &str, poll_interval: u64, waker actor: sea_orm::ActiveValue::Set(delivery.actor), target: sea_orm::ActiveValue::Set(delivery.target), activity: sea_orm::ActiveValue::Set(delivery.activity), - created: sea_orm::ActiveValue::Set(delivery.created), + published: sea_orm::ActiveValue::Set(delivery.published), attempt: sea_orm::ActiveValue::Set(delivery.attempt + 1), }; model::delivery::Entity::insert(new_delivery).exec(db).await?; diff --git a/src/server/fetcher.rs b/src/server/fetcher.rs index 9abeb88..4762fc1 100644 --- a/src/server/fetcher.rs +++ b/src/server/fetcher.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use apb::{target::Addressed, Activity, Actor, ActorMut, Base, Collection, Link, Object}; +use apb::{target::Addressed, Activity, Actor, ActorMut, Base, Collection, Object}; use base64::Engine; use reqwest::{header::{ACCEPT, CONTENT_TYPE, USER_AGENT}, Method, Response}; use sea_orm::EntityTrait; @@ -139,14 +139,14 @@ impl Fetcher for Context { async fn pull_user(&self, id: &str) -> crate::Result { let mut user = Self::request( - Method::GET, id, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), + Method::GET, id, None, &format!("https://{}", self.domain()), self.pkey(), self.domain(), ).await?.json::().await?; // TODO try fetching these numbers from audience/generator fields to avoid making 2 more GETs if let Some(followers_url) = &user.followers().id() { let req = Self::request( Method::GET, followers_url, None, - &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), + &format!("https://{}", self.domain()), self.pkey(), self.domain(), ).await; if let Ok(res) = req { if let Ok(user_followers) = res.json::().await { @@ -160,7 +160,7 @@ impl Fetcher for Context { if let Some(following_url) = &user.following().id() { let req = Self::request( Method::GET, following_url, None, - &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), + &format!("https://{}", self.domain()), self.pkey(), self.domain(), ).await; if let Ok(res) = req { if let Ok(user_following) = res.json::().await { @@ -191,14 +191,14 @@ impl Fetcher for Context { let addressed = activity.addressed(); let expanded_addresses = self.expand_addressing(addressed).await?; - self.address_to(Some(&activity.id), None, &expanded_addresses).await?; + self.address_to(Some(activity.internal), None, &expanded_addresses).await?; Ok(activity) } async fn pull_activity(&self, id: &str) -> crate::Result { let activity = Self::request( - Method::GET, id, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), + Method::GET, id, None, &format!("https://{}", self.domain()), self.pkey(), self.domain(), ).await?.json::().await?; if let Some(activity_actor) = activity.actor().id() { @@ -228,7 +228,7 @@ impl Fetcher for Context { async fn pull_object(&self, id: &str) -> crate::Result { Ok( Context::request( - Method::GET, id, None, &format!("https://{}", self.domain()), &self.app().private_key, self.domain(), + Method::GET, id, None, &format!("https://{}", self.domain()), self.pkey(), self.domain(), ) .await? .json::() @@ -244,7 +244,7 @@ async fn fetch_object_inner(ctx: &Context, id: &str, depth: usize) -> crate::Res } let object = Context::request( - Method::GET, id, None, &format!("https://{}", ctx.domain()), &ctx.app().private_key, ctx.domain(), + Method::GET, id, None, &format!("https://{}", ctx.domain()), ctx.pkey(), ctx.domain(), ).await?.json::().await?; if let Some(oid) = object.id() { @@ -274,7 +274,7 @@ async fn fetch_object_inner(ctx: &Context, id: &str, depth: usize) -> crate::Res let object_model = ctx.insert_object(object, None).await?; let expanded_addresses = ctx.expand_addressing(addressed).await?; - ctx.address_to(None, Some(&object_model.id), &expanded_addresses).await?; + ctx.address_to(None, Some(object_model.internal), &expanded_addresses).await?; Ok(object_model) } @@ -288,9 +288,7 @@ pub trait Fetchable : Sync + Send { impl Fetchable for apb::Node { async fn fetch(&mut self, ctx: &crate::server::Context) -> crate::Result<&mut Self> { if let apb::Node::Link(uri) = self { - let from = format!("{}{}", ctx.protocol(), ctx.domain()); // TODO helper to avoid this? - let pkey = &ctx.app().private_key; - *self = Context::request(Method::GET, uri.href(), None, &from, pkey, ctx.domain()) + *self = Context::request(Method::GET, uri.href(), None, ctx.base(), ctx.pkey(), ctx.domain()) .await? .json::() .await? diff --git a/src/server/normalizer.rs b/src/server/normalizer.rs index a4d2fde..4d358c2 100644 --- a/src/server/normalizer.rs +++ b/src/server/normalizer.rs @@ -83,7 +83,7 @@ impl Normalizer for super::Context { document_type: Set(apb::DocumentType::Page), name: Set(l.link_name().map(|x| x.to_string())), media_type: Set(l.link_media_type().unwrap_or("link").to_string()), - created: Set(chrono::Utc::now()), + published: Set(chrono::Utc::now()), }, Node::Object(o) => model::attachment::ActiveModel { internal: sea_orm::ActiveValue::NotSet, @@ -92,7 +92,7 @@ impl Normalizer for super::Context { document_type: Set(o.as_document().map_or(apb::DocumentType::Document, |x| x.document_type().unwrap_or(apb::DocumentType::Page))), name: Set(o.name().map(|x| x.to_string())), media_type: Set(o.media_type().unwrap_or("link").to_string()), - created: Set(o.published().unwrap_or_else(chrono::Utc::now)), + published: Set(o.published().unwrap_or_else(chrono::Utc::now)), }, }; model::attachment::Entity::insert(attachment_model) @@ -120,7 +120,7 @@ impl Normalizer for super::Context { document_type: Set(img.as_document().map_or(apb::DocumentType::Document, |x| x.document_type().unwrap_or(apb::DocumentType::Page))), name: Set(img.name().map(|x| x.to_string())), media_type: Set(img.media_type().unwrap_or(media_type.as_deref().unwrap_or("link")).to_string()), - created: Set(img.published().unwrap_or_else(chrono::Utc::now)), + published: Set(img.published().unwrap_or_else(chrono::Utc::now)), }; model::attachment::Entity::insert(attachment_model) .exec(self.db()) diff --git a/src/server/outbox.rs b/src/server/outbox.rs index 6131b59..742c323 100644 --- a/src/server/outbox.rs +++ b/src/server/outbox.rs @@ -1,6 +1,6 @@ use apb::{target::Addressed, Activity, ActivityMut, ActorMut, BaseMut, Node, Object, ObjectMut, PublicKeyMut}; use reqwest::StatusCode; -use sea_orm::{sea_query::Expr, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, SelectColumns, Set}; +use sea_orm::{sea_query::Expr, ActiveModelTrait, ActiveValue::{Set, NotSet}, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, SelectColumns}; use crate::{errors::UpubError, model, routes::activitypub::jsonld::LD}; @@ -25,11 +25,11 @@ impl apb::server::Outbox for Context { if let Some(c) = content { let mut tmp = mdhtml::safe_markdown(&c); for (full, [user, domain]) in re.captures_iter(&tmp.clone()).map(|x| x.extract()) { - if let Ok(Some(uid)) = model::user::Entity::find() - .filter(model::user::Column::PreferredUsername.eq(user)) - .filter(model::user::Column::Domain.eq(domain)) + if let Ok(Some(uid)) = model::actor::Entity::find() + .filter(model::actor::Column::PreferredUsername.eq(user)) + .filter(model::actor::Column::Domain.eq(domain)) .select_only() - .select_column(model::user::Column::Id) + .select_column(model::actor::Column::Id) .into_tuple::() .one(self.db()) .await @@ -178,12 +178,12 @@ impl apb::server::Outbox for Context { match accepted_activity.activity_type { apb::ActivityType::Follow => { - model::user::Entity::update_many() + model::actor::Entity::update_many() .col_expr( - model::user::Column::FollowersCount, - Expr::col(model::user::Column::FollowersCount).add(1) + model::actor::Column::FollowersCount, + Expr::col(model::actor::Column::FollowersCount).add(1) ) - .filter(model::user::Column::Id.eq(&uid)) + .filter(model::actor::Column::Id.eq(&uid)) .exec(self.db()) .await?; model::relation::Entity::insert( @@ -303,7 +303,7 @@ impl apb::server::Outbox for Context { match object_node.object_type() { Some(apb::ObjectType::Actor(_)) => { - let mut actor_model = model::user::Model::new( + let mut actor_model = model::actor::Model::new( &object_node // TODO must set these, but we will ignore them .set_actor_type(Some(apb::ActorType::Person)) @@ -311,7 +311,7 @@ impl apb::server::Outbox for Context { serde_json::Value::new_object().set_public_key_pem("") )) )?; - let old_actor_model = model::user::Entity::find_by_id(&actor_model.id) + let old_actor_model = model::actor::Entity::find_by_id(&actor_model.id) .one(self.db()) .await? .ok_or_else(UpubError::not_found)?; @@ -328,12 +328,12 @@ impl apb::server::Outbox for Context { let mut update_model = actor_model.into_active_model(); update_model.updated = sea_orm::Set(chrono::Utc::now()); - update_model.reset(model::user::Column::Name); - update_model.reset(model::user::Column::Summary); - update_model.reset(model::user::Column::Image); - update_model.reset(model::user::Column::Icon); + update_model.reset(model::actor::Column::Name); + update_model.reset(model::actor::Column::Summary); + update_model.reset(model::actor::Column::Image); + update_model.reset(model::actor::Column::Icon); - model::user::Entity::update(update_model) + model::actor::Entity::update(update_model) .exec(self.db()).await?; }, Some(apb::ObjectType::Note) => { @@ -398,17 +398,17 @@ impl apb::server::Outbox for Context { .set_actor(Node::link(uid.clone())) )?; - let share_model = model::share::ActiveModel { + let share_model = model::announce::ActiveModel { + internal: NotSet, actor: Set(uid.clone()), - shares: Set(oid.clone()), - date: Set(chrono::Utc::now()), - ..Default::default() + object: Set(oid.clone()), + published: Set(chrono::Utc::now()), }; - model::share::Entity::insert(share_model).exec(self.db()).await?; + model::announce::Entity::insert(share_model).exec(self.db()).await?; model::activity::Entity::insert(activity_model.into_active_model()) .exec(self.db()).await?; model::object::Entity::update_many() - .col_expr(model::object::Column::Shares, Expr::col(model::object::Column::Shares).add(1)) + .col_expr(model::object::Column::Announces, Expr::col(model::object::Column::Announces).add(1)) .filter(model::object::Column::Id.eq(oid)) .exec(self.db()) .await?; From df583bc79199aa7ba626cffbeb4e1511cacecb5e Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 25 May 2024 07:22:41 +0200 Subject: [PATCH 09/90] fix: also cli tasks, forgot about them oops --- src/cli/faker.rs | 43 ++++++++++++++++++++++++------------------- src/cli/fetch.rs | 10 +++++----- src/cli/fix.rs | 14 +++++++------- src/cli/mod.rs | 2 +- src/cli/relay.rs | 31 ++++++++++++++++--------------- src/cli/update.rs | 23 +++++++++++++---------- src/model/actor.rs | 4 ++++ 7 files changed, 70 insertions(+), 57 deletions(-) diff --git a/src/cli/faker.rs b/src/cli/faker.rs index 5d8899e..858c0c0 100644 --- a/src/cli/faker.rs +++ b/src/cli/faker.rs @@ -1,15 +1,16 @@ -use crate::model::{addressing, config, credential, activity, object, user, Audience}; +use crate::model::{addressing, config, credential, activity, object, actor, Audience}; use openssl::rsa::Rsa; -use sea_orm::IntoActiveModel; +use sea_orm::{ActiveValue::NotSet, IntoActiveModel}; -pub async fn faker(ctx: crate::server::Context, count: u64) -> Result<(), sea_orm::DbErr> { +pub async fn faker(ctx: crate::server::Context, count: i64) -> Result<(), sea_orm::DbErr> { use sea_orm::{EntityTrait, Set}; let domain = ctx.domain(); let db = ctx.db(); let key = Rsa::generate(2048).unwrap(); - let test_user = user::Model { + let test_user = actor::Model { + internal: 42, id: format!("{domain}/users/test"), name: Some("μpub".into()), domain: clean_domain(domain), @@ -19,24 +20,25 @@ pub async fn faker(ctx: crate::server::Context, count: u64) -> Result<(), sea_or following_count: 0, followers: None, followers_count: 0, - statuses_count: count as i64, + statuses_count: count as i32, icon: Some("https://cdn.alemi.dev/social/circle-square.png".to_string()), image: Some("https://cdn.alemi.dev/social/someriver-xs.jpg".to_string()), inbox: None, shared_inbox: None, outbox: None, actor_type: apb::ActorType::Person, - created: chrono::Utc::now(), + published: chrono::Utc::now(), updated: chrono::Utc::now(), private_key: Some(std::str::from_utf8(&key.private_key_to_pem().unwrap()).unwrap().to_string()), // TODO generate a fresh one every time public_key: std::str::from_utf8(&key.public_key_to_pem().unwrap()).unwrap().to_string(), }; - user::Entity::insert(test_user.clone().into_active_model()).exec(db).await?; + actor::Entity::insert(test_user.clone().into_active_model()).exec(db).await?; config::Entity::insert(config::ActiveModel { - id: Set(test_user.id.clone()), + internal: NotSet, + actor: Set(test_user.id.clone()), accept_follow_requests: Set(true), show_followers: Set(true), show_following: Set(true), @@ -45,8 +47,9 @@ pub async fn faker(ctx: crate::server::Context, count: u64) -> Result<(), sea_or }).exec(db).await?; credential::Entity::insert(credential::ActiveModel { - id: Set(test_user.id.clone()), - email: Set("mail@example.net".to_string()), + internal: NotSet, + actor: Set(test_user.id.clone()), + login: Set("mail@example.net".to_string()), password: Set(sha256::digest("very-strong-password")), }).exec(db).await?; @@ -57,15 +60,16 @@ pub async fn faker(ctx: crate::server::Context, count: u64) -> Result<(), sea_or let aid = uuid::Uuid::new_v4(); addressing::Entity::insert(addressing::ActiveModel { - actor: Set(apb::target::PUBLIC.to_string()), - server: Set("www.w3.org".to_string()), - activity: Set(Some(format!("{domain}/activities/{aid}"))), - object: Set(Some(format!("{domain}/objects/{oid}"))), + actor: Set(None), + instance: Set(None), + activity: Set(Some(42 + i)), + object: Set(Some(42 + i)), published: Set(chrono::Utc::now()), ..Default::default() }).exec(db).await?; object::Entity::insert(object::ActiveModel { + internal: Set(42 + i), id: Set(format!("{domain}/objects/{oid}")), name: Set(None), object_type: Set(apb::ObjectType::Note), @@ -74,11 +78,11 @@ pub async fn faker(ctx: crate::server::Context, count: u64) -> Result<(), sea_or context: Set(Some(context.clone())), in_reply_to: Set(None), content: Set(Some(format!("[{i}] Tic(k). Quasiparticle of intensive multiplicity. Tics (or ticks) are intrinsically several components of autonomously numbering anorganic populations, propagating by contagion between segmentary divisions in the order of nature. Ticks - as nonqualitative differentially-decomposable counting marks - each designate a multitude comprehended as a singular variation in tic(k)-density."))), - published: Set(chrono::Utc::now() - std::time::Duration::from_secs(60*i)), - updated: Set(None), - comments: Set(0), + published: Set(chrono::Utc::now() - std::time::Duration::from_secs(60*i as u64)), + updated: Set(chrono::Utc::now()), + replies: Set(0), likes: Set(0), - shares: Set(0), + announces: Set(0), to: Set(Audience(vec![apb::target::PUBLIC.to_string()])), bto: Set(Audience::default()), cc: Set(Audience(vec![])), @@ -88,12 +92,13 @@ pub async fn faker(ctx: crate::server::Context, count: u64) -> Result<(), sea_or }).exec(db).await?; activity::Entity::insert(activity::ActiveModel { + internal: Set(42 + i), id: Set(format!("{domain}/activities/{aid}")), activity_type: Set(apb::ActivityType::Create), actor: Set(format!("{domain}/users/test")), object: Set(Some(format!("{domain}/objects/{oid}"))), target: Set(None), - published: Set(chrono::Utc::now() - std::time::Duration::from_secs(60*i)), + published: Set(chrono::Utc::now() - std::time::Duration::from_secs(60*i as u64)), to: Set(Audience(vec![apb::target::PUBLIC.to_string()])), bto: Set(Audience::default()), cc: Set(Audience(vec![])), diff --git a/src/cli/fetch.rs b/src/cli/fetch.rs index 0f1c4f7..ef2230d 100644 --- a/src/cli/fetch.rs +++ b/src/cli/fetch.rs @@ -1,4 +1,4 @@ -use sea_orm::{EntityTrait, IntoActiveModel}; +use sea_orm::EntityTrait; use crate::server::fetcher::Fetchable; @@ -13,18 +13,18 @@ pub async fn fetch(ctx: crate::server::Context, uri: String, save: bool) -> crat if save { match obj.base_type() { Some(apb::BaseType::Object(apb::ObjectType::Actor(_))) => { - crate::model::user::Entity::insert( - crate::model::user::Model::new(obj).unwrap().into_active_model() + crate::model::actor::Entity::insert( + crate::model::actor::ActiveModel::new(obj).unwrap() ).exec(ctx.db()).await.unwrap(); }, Some(apb::BaseType::Object(apb::ObjectType::Activity(_))) => { crate::model::activity::Entity::insert( - crate::model::activity::Model::new(obj).unwrap().into_active_model() + crate::model::activity::ActiveModel::new(obj).unwrap() ).exec(ctx.db()).await.unwrap(); }, Some(apb::BaseType::Object(apb::ObjectType::Note)) => { crate::model::object::Entity::insert( - crate::model::object::Model::new(obj).unwrap().into_active_model() + crate::model::object::ActiveModel::new(obj).unwrap() ).exec(ctx.db()).await.unwrap(); }, Some(apb::BaseType::Object(t)) => tracing::warn!("not implemented: {:?}", t), diff --git a/src/cli/fix.rs b/src/cli/fix.rs index 9ad85d8..a7d7a1a 100644 --- a/src/cli/fix.rs +++ b/src/cli/fix.rs @@ -11,13 +11,13 @@ pub async fn fix(ctx: crate::server::Context, likes: bool, shares: bool, replies { let mut stream = crate::model::like::Entity::find().stream(db).await?; while let Some(like) = stream.try_next().await? { - store.insert(like.likes.clone(), store.get(&like.likes).unwrap_or(&0) + 1); + store.insert(like.object, store.get(&like.object).unwrap_or(&0) + 1); } } for (k, v) in store { let m = crate::model::object::ActiveModel { - id: sea_orm::Set(k.clone()), + internal: sea_orm::Set(k), likes: sea_orm::Set(v), ..Default::default() }; @@ -34,16 +34,16 @@ pub async fn fix(ctx: crate::server::Context, likes: bool, shares: bool, replies tracing::info!("fixing shares..."); let mut store = std::collections::HashMap::new(); { - let mut stream = crate::model::share::Entity::find().stream(db).await?; + let mut stream = crate::model::announce::Entity::find().stream(db).await?; while let Some(share) = stream.try_next().await? { - store.insert(share.shares.clone(), store.get(&share.shares).unwrap_or(&0) + 1); + store.insert(share.object.clone(), store.get(&share.object).unwrap_or(&0) + 1); } } for (k, v) in store { let m = crate::model::object::ActiveModel { - id: sea_orm::Set(k.clone()), - shares: sea_orm::Set(v), + internal: sea_orm::Set(k), + announces: sea_orm::Set(v), ..Default::default() }; if let Err(e) = crate::model::object::Entity::update(m) @@ -71,7 +71,7 @@ pub async fn fix(ctx: crate::server::Context, likes: bool, shares: bool, replies for (k, v) in store { let m = crate::model::object::ActiveModel { id: sea_orm::Set(k.clone()), - comments: sea_orm::Set(v), + replies: sea_orm::Set(v), ..Default::default() }; if let Err(e) = crate::model::object::Entity::update(m) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 293646e..bcf8eea 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -104,7 +104,7 @@ pub async fn run( ).await?; match command { CliCommand::Faker { count } => - Ok(faker(ctx, count).await?), + Ok(faker(ctx, count as i64).await?), CliCommand::Fetch { uri, save } => Ok(fetch(ctx, uri, save).await?), CliCommand::Relay { actor, accept } => diff --git a/src/cli/relay.rs b/src/cli/relay.rs index 850a84f..4203499 100644 --- a/src/cli/relay.rs +++ b/src/cli/relay.rs @@ -1,19 +1,20 @@ -use sea_orm::{ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QueryOrder}; +use sea_orm::{ActiveValue::{Set, NotSet}, ColumnTrait, EntityTrait, QueryFilter, QueryOrder}; pub async fn relay(ctx: crate::server::Context, actor: String, accept: bool) -> crate::Result<()> { let aid = ctx.aid(&uuid::Uuid::new_v4().to_string()); - let mut activity_model = crate::model::activity::Model { - id: aid.clone(), - activity_type: apb::ActivityType::Follow, - actor: ctx.base().to_string(), - object: Some(actor.clone()), - target: None, - published: chrono::Utc::now(), - to: crate::model::Audience(vec![actor.clone()]), - bto: crate::model::Audience::default(), - cc: crate::model::Audience(vec![apb::target::PUBLIC.to_string()]), - bcc: crate::model::Audience::default(), + let mut activity_model = crate::model::activity::ActiveModel { + internal: NotSet, + id: Set(aid.clone()), + activity_type: Set(apb::ActivityType::Follow), + actor: Set(ctx.base().to_string()), + object: Set(Some(actor.clone())), + target: Set(None), + published: Set(chrono::Utc::now()), + to: Set(crate::model::Audience(vec![actor.clone()])), + bto: Set(crate::model::Audience::default()), + cc: Set(crate::model::Audience(vec![apb::target::PUBLIC.to_string()])), + bcc: Set(crate::model::Audience::default()), }; if accept { @@ -25,11 +26,11 @@ pub async fn relay(ctx: crate::server::Context, actor: String, accept: bool) -> .one(ctx.db()) .await? .expect("no follow request to accept"); - activity_model.activity_type = apb::ActivityType::Accept(apb::AcceptType::Accept); - activity_model.object = Some(follow_req.id); + activity_model.activity_type = Set(apb::ActivityType::Accept(apb::AcceptType::Accept)); + activity_model.object = Set(Some(follow_req.id)); }; - crate::model::activity::Entity::insert(activity_model.into_active_model()) + crate::model::activity::Entity::insert(activity_model) .exec(ctx.db()).await?; ctx.dispatch(ctx.base(), vec![actor, apb::target::PUBLIC.to_string()], &aid, None).await?; diff --git a/src/cli/update.rs b/src/cli/update.rs index d5318ce..f43c9b2 100644 --- a/src/cli/update.rs +++ b/src/cli/update.rs @@ -1,5 +1,5 @@ use futures::TryStreamExt; -use sea_orm::{ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; +use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use crate::server::fetcher::Fetcher; @@ -8,8 +8,8 @@ pub async fn update_users(ctx: crate::server::Context, days: i64) -> crate::Resu let mut insertions = Vec::new(); { - let mut stream = crate::model::user::Entity::find() - .filter(crate::model::user::Column::Updated.lt(chrono::Utc::now() - chrono::Duration::days(days))) + let mut stream = crate::model::actor::Entity::find() + .filter(crate::model::actor::Column::Updated.lt(chrono::Utc::now() - chrono::Duration::days(days))) .stream(ctx.db()) .await?; @@ -18,18 +18,21 @@ pub async fn update_users(ctx: crate::server::Context, days: i64) -> crate::Resu if ctx.is_local(&user.id) { continue } match ctx.pull_user(&user.id).await { Err(e) => tracing::warn!("could not update user {}: {e}", user.id), - Ok(u) => { - insertions.push(u); - count += 1; + Ok(doc) => match crate::model::actor::ActiveModel::new(&doc) { + Ok(u) => { + insertions.push((user.id, u)); + count += 1; + }, + Err(e) => tracing::warn!("failed deserializing user '{}': {e}", user.id), }, } } } - for u in insertions { - tracing::info!("updating user {}", u.id); - crate::model::user::Entity::delete_by_id(&u.id).exec(ctx.db()).await?; - crate::model::user::Entity::insert(u.into_active_model()).exec(ctx.db()).await?; + for (uid, user_model) in insertions { + tracing::info!("updating user {}", uid); + crate::model::actor::Entity::delete_by_ap_id(&uid).exec(ctx.db()).await?; + crate::model::actor::Entity::insert(user_model).exec(ctx.db()).await?; } tracing::info!("updated {count} users"); diff --git a/src/model/actor.rs b/src/model/actor.rs index 6a8086f..59a1732 100644 --- a/src/model/actor.rs +++ b/src/model/actor.rs @@ -138,6 +138,10 @@ impl Entity { Entity::find().filter(Column::Id.eq(id)) } + pub fn delete_by_ap_id(id: &str) -> sea_orm::DeleteMany { + Entity::delete_many().filter(Column::Id.eq(id)) + } + pub async fn ap_to_internal(id: &str, db: &DatabaseConnection) -> crate::Result { Entity::find() .filter(Column::Id.eq(id)) From bcfd71eb0666bd7bb8bb2691f8e4bac43787ade2 Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 26 May 2024 18:41:27 +0200 Subject: [PATCH 10/90] fix: index relations by activity too since the only way to find them is via the activity that generated them --- .../m20240524_000002_create_relations_likes_shares.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/migrations/m20240524_000002_create_relations_likes_shares.rs b/src/migrations/m20240524_000002_create_relations_likes_shares.rs index fbd8504..36a8bdc 100644 --- a/src/migrations/m20240524_000002_create_relations_likes_shares.rs +++ b/src/migrations/m20240524_000002_create_relations_likes_shares.rs @@ -96,6 +96,10 @@ impl MigrationTrait for Migration { .create_index(Index::create().name("index-relations-following").table(Relations::Table).col(Relations::Following).to_owned()) .await?; + manager + .create_index(Index::create().name("index-relations-activity").table(Relations::Table).col(Relations::Activity).to_owned()) + .await?; + manager .create_table( Table::create() @@ -209,6 +213,10 @@ impl MigrationTrait for Migration { .drop_index(Index::drop().name("index-relations-following").table(Relations::Table).to_owned()) .await?; + manager + .drop_index(Index::drop().name("index-relations-activity").table(Relations::Table).to_owned()) + .await?; + manager .drop_table(Table::drop().table(Likes::Table).to_owned()) .await?; From c94bfdcbe8e6bfc150d0f5e730c97da28b7ec06e Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 26 May 2024 18:41:56 +0200 Subject: [PATCH 11/90] feat: naive attempt to resolve followers/following --- src/model/actor.rs | 8 +++++++ src/model/relation.rs | 56 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/model/actor.rs b/src/model/actor.rs index 59a1732..b2d3b77 100644 --- a/src/model/actor.rs +++ b/src/model/actor.rs @@ -61,6 +61,8 @@ pub enum Relation { Mentions, #[sea_orm(has_many = "super::object::Entity")] Objects, + #[sea_orm(has_many = "super::relation::Entity")] + Relations, #[sea_orm(has_many = "super::session::Entity")] Sessions, } @@ -125,6 +127,12 @@ impl Related for Entity { } } +impl Related for Entity { + fn to() -> RelationDef { + Relation::Relations.def() + } +} + impl Related for Entity { fn to() -> RelationDef { Relation::Sessions.def() diff --git a/src/model/relation.rs b/src/model/relation.rs index 2a91741..6ca98b4 100644 --- a/src/model/relation.rs +++ b/src/model/relation.rs @@ -1,4 +1,4 @@ -use sea_orm::entity::prelude::*; +use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns}; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "relations")] @@ -47,4 +47,58 @@ pub enum Relation { ActorsFollowing, } +impl Related for Entity { + fn to() -> RelationDef { + Relation::ActorsFollowing.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ActivitiesFollow.def() + } +} + impl ActiveModelBehavior for ActiveModel {} + +impl Entity { + pub async fn followers(uid: &str, db: &DatabaseConnection) -> crate::Result> { + let internal_id = super::actor::Entity::ap_to_internal(uid, db).await?; + let out = Entity::find() + .join( + sea_orm::JoinType::InnerJoin, + Entity::belongs_to(super::actor::Entity) + .from(Column::Follower) + .to(super::actor::Column::Internal) + .into() + ) + .filter(Column::Following.eq(internal_id)) + .select_only() + .select_column(super::actor::Column::Id) + .into_tuple::() + .all(db) + .await?; + + Ok(out) + } + + pub async fn following(uid: &str, db: &DatabaseConnection) -> crate::Result> { + let internal_id = super::actor::Entity::ap_to_internal(uid, db).await?; + let out = Entity::find() + .join( + sea_orm::JoinType::InnerJoin, + Entity::belongs_to(super::actor::Entity) + .from(Column::Following) + .to(super::actor::Column::Internal) + .into() + ) + .filter(Column::Follower.eq(internal_id)) + .select_only() + .select_column(super::actor::Column::Id) + .into_tuple::() + .all(db) + .await?; + + Ok(out) + } +} From 3c3e98a4f43525b1a3a943671bc6c79300fe732a Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 26 May 2024 18:42:22 +0200 Subject: [PATCH 12/90] chore: initial work converting outbox logic --- src/model/object.rs | 4 + src/server/outbox.rs | 244 +++++++++++++++++++++++++------------------ 2 files changed, 146 insertions(+), 102 deletions(-) diff --git a/src/model/object.rs b/src/model/object.rs index bbaab0e..2abea5d 100644 --- a/src/model/object.rs +++ b/src/model/object.rs @@ -127,6 +127,10 @@ impl Entity { Entity::find().filter(Column::Id.eq(id)) } + pub fn delete_by_ap_id(id: &str) -> sea_orm::DeleteMany { + Entity::delete_many().filter(Column::Id.eq(id)) + } + pub async fn ap_to_internal(id: &str, db: &DatabaseConnection) -> crate::Result { Entity::find() .filter(Column::Id.eq(id)) diff --git a/src/server/outbox.rs b/src/server/outbox.rs index 742c323..f6689ee 100644 --- a/src/server/outbox.rs +++ b/src/server/outbox.rs @@ -1,4 +1,4 @@ -use apb::{target::Addressed, Activity, ActivityMut, ActorMut, BaseMut, Node, Object, ObjectMut, PublicKeyMut}; +use apb::{target::Addressed, Activity, ActivityMut, ActorMut, Base, BaseMut, Node, Object, ObjectMut, PublicKeyMut}; use reqwest::StatusCode; use sea_orm::{sea_query::Expr, ActiveModelTrait, ActiveValue::{Set, NotSet}, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, SelectColumns}; @@ -44,27 +44,26 @@ impl apb::server::Outbox for Context { object .set_id(Some(&oid)) .set_attributed_to(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) .set_content(content.as_deref()) .set_url(Node::maybe_link(self.cfg().instance.frontend.as_ref().map(|x| format!("{x}/objects/{raw_oid}")))), Some(self.domain().to_string()), ).await?; - let activity_model = model::activity::Model { - id: aid.clone(), - activity_type: apb::ActivityType::Create, - actor: uid.clone(), - object: Some(oid.clone()), - target: None, - cc: object_model.cc.clone(), - bcc: object_model.bcc.clone(), - to: object_model.to.clone(), - bto: object_model.bto.clone(), - published: object_model.published, + let activity_model = model::activity::ActiveModel { + internal: NotSet, + id: Set(aid.clone()), + activity_type: Set(apb::ActivityType::Create), + actor: Set(uid.clone()), + object: Set(Some(oid.clone())), + target: Set(None), + cc: Set(object_model.cc.clone()), + bcc: Set(object_model.bcc.clone()), + to: Set(object_model.to.clone()), + bto: Set(object_model.bto.clone()), + published: Set(object_model.published), }; - model::activity::Entity::insert(activity_model.into_active_model()) - .exec(self.db()).await?; + model::activity::Entity::insert(activity_model).exec(self.db()).await?; self.dispatch(&uid, activity_targets, &aid, Some(&oid)).await?; @@ -85,19 +84,18 @@ impl apb::server::Outbox for Context { object .set_id(Some(&oid)) .set_attributed_to(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) .set_to(activity.to()) .set_bto(activity.bto()) .set_cc(activity.cc()) - .set_bcc(activity.bcc()), + .set_bcc(activity.bcc()) + .set_url(Node::maybe_link(self.cfg().instance.frontend.as_ref().map(|x| format!("{x}/objects/{raw_oid}")))), Some(self.domain().to_string()), ).await?; - let activity_model = model::activity::Model::new( + let activity_model = model::activity::ActiveModel::new( &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) .set_object(Node::link(oid.clone())) )?; @@ -112,26 +110,28 @@ impl apb::server::Outbox for Context { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let activity_targets = activity.addressed(); let oid = activity.object().id().ok_or_else(UpubError::bad_request)?; - self.fetch_object(&oid).await?; - let activity_model = model::activity::Model::new( + let obj_model = self.fetch_object(&oid).await?; + let activity_model = model::activity::ActiveModel::new( &activity .set_id(Some(&aid)) - .set_published(Some(chrono::Utc::now())) .set_actor(Node::link(uid.clone())) )?; + let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; + let like_model = model::like::ActiveModel { - actor: Set(uid.clone()), - likes: Set(oid.clone()), - date: Set(chrono::Utc::now()), - ..Default::default() + internal: NotSet, + actor: Set(internal_uid), + object: Set(obj_model.internal), + published: Set(chrono::Utc::now()), }; + model::like::Entity::insert(like_model).exec(self.db()).await?; - model::activity::Entity::insert(activity_model.into_active_model()) + model::activity::Entity::insert(activity_model) .exec(self.db()).await?; model::object::Entity::update_many() .col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).add(1)) - .filter(model::object::Column::Id.eq(oid)) + .filter(model::object::Column::Internal.eq(obj_model.internal)) .exec(self.db()) .await?; @@ -143,17 +143,31 @@ impl apb::server::Outbox for Context { async fn follow(&self, uid: String, activity: serde_json::Value) -> crate::Result { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let activity_targets = activity.addressed(); - if activity.object().id().is_none() { - return Err(UpubError::bad_request()); - } + let target = activity.object().id().ok_or_else(UpubError::bad_request)?; - let activity_model = model::activity::Model::new( + let activity_model = model::activity::ActiveModel::new( &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) )?; - model::activity::Entity::insert(activity_model.into_active_model()) + + let follower_internal = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; + let following_internal = model::actor::Entity::ap_to_internal(&target, self.db()).await?; + + model::activity::Entity::insert(activity_model) + .exec(self.db()).await?; + + let internal_aid = model::activity::Entity::ap_to_internal(&aid, self.db()).await?; + + let relation_model = model::relation::ActiveModel { + internal: NotSet, + follower: Set(follower_internal), + following: Set(following_internal), + activity: Set(internal_aid), + accept: Set(None), + }; + + model::relation::Entity::insert(relation_model) .exec(self.db()).await?; self.dispatch(&uid, activity_targets, &aid, None).await?; @@ -164,17 +178,27 @@ impl apb::server::Outbox for Context { async fn accept(&self, uid: String, activity: serde_json::Value) -> crate::Result { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let activity_targets = activity.addressed(); - if activity.object().id().is_none() { + let accepted_id = activity.object().id().ok_or_else(UpubError::bad_request)?; + let accepted_activity = model::activity::Entity::find_by_ap_id(&accepted_id) + .one(self.db()).await? + .ok_or_else(UpubError::not_found)?; + + if accepted_activity.activity_type != apb::ActivityType::Follow { return Err(UpubError::bad_request()); } - let Some(accepted_id) = activity.object().id() else { - return Err(UpubError::bad_request()); - }; - let Some(accepted_activity) = model::activity::Entity::find_by_id(accepted_id) - .one(self.db()).await? - else { - return Err(UpubError::not_found()); - }; + if uid != accepted_activity.object.ok_or_else(UpubError::bad_request)? { + return Err(UpubError::forbidden()); + } + + let activity_model = model::activity::ActiveModel::new( + &activity + .set_id(Some(&aid)) + .set_actor(Node::link(uid.clone())) + )?; + model::activity::Entity::insert(activity_model.into_active_model()) + .exec(self.db()).await?; + + let internal_aid = model::activity::Entity::ap_to_internal(&aid, self.db()).await?; match accepted_activity.activity_type { apb::ActivityType::Follow => { @@ -186,72 +210,97 @@ impl apb::server::Outbox for Context { .filter(model::actor::Column::Id.eq(&uid)) .exec(self.db()) .await?; - model::relation::Entity::insert( - model::relation::ActiveModel { - follower: Set(accepted_activity.actor), following: Set(uid.clone()), - ..Default::default() - } - ).exec(self.db()).await?; + model::relation::Entity::update_many() + .filter(model::relation::Column::Activity.eq(accepted_activity.internal)) + .col_expr(model::relation::Column::Accept, Expr::value(Some(internal_aid))) + .exec(self.db()).await?; }, - t => tracing::warn!("no side effects implemented for accepting {t:?}"), + t => tracing::error!("no side effects implemented for accepting {t:?}"), } - let activity_model = model::activity::Model::new( - &activity - .set_id(Some(&aid)) - .set_actor(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) - )?; - model::activity::Entity::insert(activity_model.into_active_model()) - .exec(self.db()).await?; - self.dispatch(&uid, activity_targets, &aid, None).await?; Ok(aid) } - async fn reject(&self, _uid: String, _activity: serde_json::Value) -> crate::Result { - todo!() + async fn reject(&self, uid: String, activity: serde_json::Value) -> crate::Result { + let aid = self.aid(&uuid::Uuid::new_v4().to_string()); + let activity_targets = activity.addressed(); + let rejected_id = activity.object().id().ok_or_else(UpubError::bad_request)?; + let rejected_activity = model::activity::Entity::find_by_ap_id(&rejected_id) + .one(self.db()).await? + .ok_or_else(UpubError::not_found)?; + + if rejected_activity.activity_type != apb::ActivityType::Follow { + return Err(UpubError::bad_request()); + } + if uid != rejected_activity.object.ok_or_else(UpubError::bad_request)? { + return Err(UpubError::forbidden()); + } + + let activity_model = model::activity::ActiveModel::new( + &activity + .set_id(Some(&aid)) + .set_actor(Node::link(uid.clone())) + )?; + model::activity::Entity::insert(activity_model) + .exec(self.db()).await?; + + let internal_aid = model::activity::Entity::ap_to_internal(&aid, self.db()).await?; + + model::relation::Entity::delete_many() + .filter(model::relation::Column::Activity.eq(internal_aid)) + .exec(self.db()) + .await?; + + self.dispatch(&uid, activity_targets, &aid, None).await?; + + Ok(aid) } async fn undo(&self, uid: String, activity: serde_json::Value) -> crate::Result { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let activity_targets = activity.addressed(); let old_aid = activity.object().id().ok_or_else(UpubError::bad_request)?; - let old_activity = model::activity::Entity::find_by_id(old_aid) + let old_activity = model::activity::Entity::find_by_ap_id(&old_aid) .one(self.db()) .await? .ok_or_else(UpubError::not_found)?; if old_activity.actor != uid { return Err(UpubError::forbidden()); } - match old_activity.activity_type { - apb::ActivityType::Like => { - model::like::Entity::delete_many() - .filter(model::like::Column::Actor.eq(old_activity.actor)) - .filter(model::like::Column::Likes.eq(old_activity.object.unwrap_or("".into()))) - .exec(self.db()) - .await?; - }, - apb::ActivityType::Follow => { - model::relation::Entity::delete_many() - .filter(model::relation::Column::Follower.eq(old_activity.actor)) - .filter(model::relation::Column::Following.eq(old_activity.object.unwrap_or("".into()))) - .exec(self.db()) - .await?; - }, - t => tracing::warn!("extra side effects for activity {t:?} not implemented"), - } - let activity_model = model::activity::Model::new( + let activity_object = old_activity.object.ok_or_else(UpubError::bad_request)?; + let actor_internal = model::actor::Entity::ap_to_internal(&old_activity.actor, self.db()).await?; + + let activity_model = model::activity::ActiveModel::new( &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) )?; model::activity::Entity::insert(activity_model.into_active_model()) .exec(self.db()) .await?; + match old_activity.activity_type { + apb::ActivityType::Like => { + let object_internal = model::object::Entity::ap_to_internal(&activity_object, self.db()).await?; + model::like::Entity::delete_many() + .filter(model::like::Column::Actor.eq(actor_internal)) + .filter(model::like::Column::Object.eq(object_internal)) + .exec(self.db()) + .await?; + }, + apb::ActivityType::Follow => { + let target_internal = model::actor::Entity::ap_to_internal(&activity_object, self.db()).await?; + model::relation::Entity::delete_many() + .filter(model::relation::Column::Follower.eq(actor_internal)) + .filter(model::relation::Column::Following.eq(target_internal)) + .exec(self.db()) + .await?; + }, + t => tracing::error!("extra side effects for activity {t:?} not implemented"), + } + self.dispatch(&uid, activity_targets, &aid, None).await?; Ok(aid) @@ -261,34 +310,28 @@ impl apb::server::Outbox for Context { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let oid = activity.object().id().ok_or_else(UpubError::bad_request)?; - let object = model::object::Entity::find_by_id(&oid) + let object = model::object::Entity::find_by_ap_id(&oid) .one(self.db()) .await? .ok_or_else(UpubError::not_found)?; - let Some(author_id) = object.attributed_to else { - // can't change local objects attributed to nobody - return Err(UpubError::forbidden()) - }; - - if author_id != uid { - // can't change objects of others + if uid != object.attributed_to.ok_or_else(UpubError::forbidden)? { + // can't change objects of others, and objects from noone count as others return Err(UpubError::forbidden()); } let addressed = activity.addressed(); - let activity_model = model::activity::Model::new( + let activity_model = model::activity::ActiveModel::new( &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) )?; - model::object::Entity::delete_by_id(&oid) + model::activity::Entity::insert(activity_model) .exec(self.db()) .await?; - model::activity::Entity::insert(activity_model.into_active_model()) + model::object::Entity::delete_by_ap_id(&oid) .exec(self.db()) .await?; @@ -300,18 +343,12 @@ impl apb::server::Outbox for Context { async fn update(&self, uid: String, activity: serde_json::Value) -> crate::Result { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let object_node = activity.object().extract().ok_or_else(UpubError::bad_request)?; + + let target = object_node.id().ok_or_else(UpubError::bad_request)?.to_string(); match object_node.object_type() { Some(apb::ObjectType::Actor(_)) => { - let mut actor_model = model::actor::Model::new( - &object_node - // TODO must set these, but we will ignore them - .set_actor_type(Some(apb::ActorType::Person)) - .set_public_key(apb::Node::object( - serde_json::Value::new_object().set_public_key_pem("") - )) - )?; - let old_actor_model = model::actor::Entity::find_by_id(&actor_model.id) + let old_actor_model = model::actor::Entity::find_by_ap_id(&target) .one(self.db()) .await? .ok_or_else(UpubError::not_found)?; @@ -321,6 +358,9 @@ impl apb::server::Outbox for Context { return Err(UpubError::forbidden()); } + let mut new_actor_model = model::actor::ActiveModel::default(); + new_actor_model.internal = Set(old_actor_model.internal); + if actor_model.name.is_none() { actor_model.name = old_actor_model.name } if actor_model.summary.is_none() { actor_model.summary = old_actor_model.summary } if actor_model.image.is_none() { actor_model.image = old_actor_model.image } From bbca51a34b87d566d4594052f56cc70fb6d3fdaa Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 26 May 2024 18:42:37 +0200 Subject: [PATCH 13/90] docs: what are roadmaps lmaoooooooooooooo --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0d4e069..305855e 100644 --- a/README.md +++ b/README.md @@ -62,18 +62,19 @@ don't hesitate to get in touch, i'd be thrilled to showcase the project to you! - [x] like, share, reply via frontend - [x] backend config - [x] frontend config + - [x] optimize `addressing` database schema - [ ] mentions, notifications + - [ ] hashtags + - [ ] public vs unlisted for discovery - [ ] mastodon-like search bar - [ ] polls - [ ] better editing via web frontend - [ ] remote media proxy - [ ] upload media - - [ ] hashtags - - [ ] public vs unlisted for discovery - [ ] user fields - [ ] lists - [ ] full mastodon api - - [ ] optimize `addressing` database schema + - [ ] get rid of internal ids from code ## what about the name? μpub (or simply `upub`) means "[micro](https://en.wikipedia.org/wiki/International_System_of_Units#Prefixes)-pub", but could also be read "upub", "you-pub" or "mu-pub" From 9a04a67d39f728aee9d3b1ad89bb14920f4a4664 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 01:55:08 +0200 Subject: [PATCH 14/90] fix: finish porting outbox --- src/server/outbox.rs | 106 +++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/src/server/outbox.rs b/src/server/outbox.rs index f6689ee..8cb3dc0 100644 --- a/src/server/outbox.rs +++ b/src/server/outbox.rs @@ -1,8 +1,8 @@ -use apb::{target::Addressed, Activity, ActivityMut, ActorMut, Base, BaseMut, Node, Object, ObjectMut, PublicKeyMut}; +use apb::{target::Addressed, Activity, ActivityMut, Base, BaseMut, Node, Object, ObjectMut}; use reqwest::StatusCode; -use sea_orm::{sea_query::Expr, ActiveModelTrait, ActiveValue::{Set, NotSet}, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, SelectColumns}; +use sea_orm::{sea_query::Expr, ActiveValue::{Set, NotSet, Unchanged}, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, SelectColumns}; -use crate::{errors::UpubError, model, routes::activitypub::jsonld::LD}; +use crate::{errors::UpubError, model}; use super::{fetcher::Fetcher, normalizer::Normalizer, Context}; @@ -343,9 +343,18 @@ impl apb::server::Outbox for Context { async fn update(&self, uid: String, activity: serde_json::Value) -> crate::Result { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let object_node = activity.object().extract().ok_or_else(UpubError::bad_request)?; - + let addressed = activity.addressed(); let target = object_node.id().ok_or_else(UpubError::bad_request)?.to_string(); + let activity_model = model::activity::ActiveModel::new( + &activity + .set_id(Some(&aid)) + .set_actor(Node::link(uid.clone())) + )?; + + model::activity::Entity::insert(activity_model) + .exec(self.db()).await?; + match object_node.object_type() { Some(apb::ObjectType::Actor(_)) => { let old_actor_model = model::actor::Entity::find_by_ap_id(&target) @@ -359,68 +368,56 @@ impl apb::server::Outbox for Context { } let mut new_actor_model = model::actor::ActiveModel::default(); - new_actor_model.internal = Set(old_actor_model.internal); + new_actor_model.internal = Unchanged(old_actor_model.internal); - if actor_model.name.is_none() { actor_model.name = old_actor_model.name } - if actor_model.summary.is_none() { actor_model.summary = old_actor_model.summary } - if actor_model.image.is_none() { actor_model.image = old_actor_model.image } - if actor_model.icon.is_none() { actor_model.icon = old_actor_model.icon } + if let Some(name) = object_node.name() { + new_actor_model.name = Set(Some(name.to_string())); + } + if let Some(summary) = object_node.summary() { + new_actor_model.summary = Set(Some(summary.to_string())); + } + if let Some(image) = object_node.image().id() { + new_actor_model.image = Set(Some(image)); + } + if let Some(icon) = object_node.icon().id() { + new_actor_model.icon = Set(Some(icon)); + } + new_actor_model.updated = Set(chrono::Utc::now()); - let mut update_model = actor_model.into_active_model(); - update_model.updated = sea_orm::Set(chrono::Utc::now()); - update_model.reset(model::actor::Column::Name); - update_model.reset(model::actor::Column::Summary); - update_model.reset(model::actor::Column::Image); - update_model.reset(model::actor::Column::Icon); - - model::actor::Entity::update(update_model) + model::actor::Entity::update(new_actor_model) .exec(self.db()).await?; }, Some(apb::ObjectType::Note) => { - let mut object_model = model::object::Model::new( - &object_node.set_published(Some(chrono::Utc::now())) - )?; - - let old_object_model = model::object::Entity::find_by_id(&object_model.id) + let old_object_model = model::object::Entity::find_by_ap_id(&target) .one(self.db()) .await? .ok_or_else(UpubError::not_found)?; - // can't change local objects attributed to nobody - let author_id = old_object_model.attributed_to.ok_or_else(UpubError::forbidden)?; - if author_id != uid { + if uid != old_object_model.attributed_to.ok_or_else(UpubError::forbidden)? { // can't change objects of others return Err(UpubError::forbidden()); } - if object_model.name.is_none() { object_model.name = old_object_model.name } - if object_model.summary.is_none() { object_model.summary = old_object_model.summary } - if object_model.content.is_none() { object_model.content = old_object_model.content } + let mut new_object_model = model::object::ActiveModel::default(); + new_object_model.internal = Unchanged(old_object_model.internal); - let mut update_model = object_model.into_active_model(); - update_model.updated = sea_orm::Set(Some(chrono::Utc::now())); - update_model.reset(model::object::Column::Name); - update_model.reset(model::object::Column::Summary); - update_model.reset(model::object::Column::Content); - update_model.reset(model::object::Column::Sensitive); + if let Some(name) = object_node.name() { + new_object_model.name = Set(Some(name.to_string())); + } + if let Some(summary) = object_node.summary() { + new_object_model.summary = Set(Some(summary.to_string())); + } + if let Some(content) = object_node.content() { + new_object_model.content = Set(Some(content.to_string())); + } + new_object_model.updated = Set(chrono::Utc::now()); - model::object::Entity::update(update_model) + model::object::Entity::update(new_object_model) .exec(self.db()).await?; }, _ => return Err(UpubError::Status(StatusCode::NOT_IMPLEMENTED)), } - let addressed = activity.addressed(); - let activity_model = model::activity::Model::new( - &activity - .set_id(Some(&aid)) - .set_actor(Node::link(uid.clone())) - .set_published(Some(chrono::Utc::now())) - )?; - - model::activity::Entity::insert(activity_model.into_active_model()) - .exec(self.db()).await?; - self.dispatch(&uid, addressed, &aid, None).await?; Ok(aid) @@ -430,26 +427,27 @@ impl apb::server::Outbox for Context { let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let activity_targets = activity.addressed(); let oid = activity.object().id().ok_or_else(UpubError::bad_request)?; - self.fetch_object(&oid).await?; - let activity_model = model::activity::Model::new( + let obj = self.fetch_object(&oid).await?; + let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; + + let activity_model = model::activity::ActiveModel::new( &activity .set_id(Some(&aid)) - .set_published(Some(chrono::Utc::now())) .set_actor(Node::link(uid.clone())) )?; let share_model = model::announce::ActiveModel { internal: NotSet, - actor: Set(uid.clone()), - object: Set(oid.clone()), + actor: Set(internal_uid), + object: Set(obj.internal), published: Set(chrono::Utc::now()), }; - model::announce::Entity::insert(share_model).exec(self.db()).await?; - model::activity::Entity::insert(activity_model.into_active_model()) + model::activity::Entity::insert(activity_model) .exec(self.db()).await?; + model::announce::Entity::insert(share_model).exec(self.db()).await?; model::object::Entity::update_many() .col_expr(model::object::Column::Announces, Expr::col(model::object::Column::Announces).add(1)) - .filter(model::object::Column::Id.eq(oid)) + .filter(model::object::Column::Internal.eq(obj.internal)) .exec(self.db()) .await?; From fea7c1ecdf8df8d777e5a6a3fd1ccf246442dd7b Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 05:38:51 +0200 Subject: [PATCH 15/90] fix: no more errors! no more warnings!! finished upgrading inbox to new schema, there's ton of space for improvement but lets first see if it works --- src/cli/fetch.rs | 19 +- src/cli/fix.rs | 2 +- src/errors.rs | 4 + src/model/activity.rs | 1 + src/model/addressing.rs | 9 - src/model/attachment.rs | 7 +- src/routes/activitypub/inbox.rs | 2 +- src/server/auth.rs | 4 +- src/server/context.rs | 8 +- src/server/fetcher.rs | 17 +- src/server/inbox.rs | 341 ++++++++++++++++---------------- src/server/normalizer.rs | 34 +++- src/server/outbox.rs | 12 +- web/src/components/post.rs | 1 - web/src/components/timeline.rs | 2 +- web/src/lib.rs | 2 +- web/src/page/register.rs | 6 +- 17 files changed, 241 insertions(+), 230 deletions(-) diff --git a/src/cli/fetch.rs b/src/cli/fetch.rs index ef2230d..a589e6b 100644 --- a/src/cli/fetch.rs +++ b/src/cli/fetch.rs @@ -1,6 +1,6 @@ use sea_orm::EntityTrait; -use crate::server::fetcher::Fetchable; +use crate::server::{fetcher::Fetchable, normalizer::Normalizer, Context}; pub async fn fetch(ctx: crate::server::Context, uri: String, save: bool) -> crate::Result<()> { use apb::Base; @@ -8,24 +8,23 @@ pub async fn fetch(ctx: crate::server::Context, uri: String, save: bool) -> crat let mut node = apb::Node::link(uri.to_string()); node.fetch(&ctx).await?; - let obj = node.get().expect("node still empty after fetch?"); + let obj = node.extract().expect("node still empty after fetch?"); + let server = Context::server(&uri); + + println!("{}", serde_json::to_string_pretty(&obj).unwrap()); if save { match obj.base_type() { Some(apb::BaseType::Object(apb::ObjectType::Actor(_))) => { crate::model::actor::Entity::insert( - crate::model::actor::ActiveModel::new(obj).unwrap() + crate::model::actor::ActiveModel::new(&obj).unwrap() ).exec(ctx.db()).await.unwrap(); }, Some(apb::BaseType::Object(apb::ObjectType::Activity(_))) => { - crate::model::activity::Entity::insert( - crate::model::activity::ActiveModel::new(obj).unwrap() - ).exec(ctx.db()).await.unwrap(); + ctx.insert_activity(obj, Some(server)).await.unwrap(); }, Some(apb::BaseType::Object(apb::ObjectType::Note)) => { - crate::model::object::Entity::insert( - crate::model::object::ActiveModel::new(obj).unwrap() - ).exec(ctx.db()).await.unwrap(); + ctx.insert_object(obj, Some(server)).await.unwrap(); }, Some(apb::BaseType::Object(t)) => tracing::warn!("not implemented: {:?}", t), Some(apb::BaseType::Link(_)) => tracing::error!("fetched another link?"), @@ -33,7 +32,5 @@ pub async fn fetch(ctx: crate::server::Context, uri: String, save: bool) -> crat } } - println!("{}", serde_json::to_string_pretty(&obj).unwrap()); - Ok(()) } diff --git a/src/cli/fix.rs b/src/cli/fix.rs index a7d7a1a..53ede8e 100644 --- a/src/cli/fix.rs +++ b/src/cli/fix.rs @@ -36,7 +36,7 @@ pub async fn fix(ctx: crate::server::Context, likes: bool, shares: bool, replies { let mut stream = crate::model::announce::Entity::find().stream(db).await?; while let Some(share) = stream.try_next().await? { - store.insert(share.object.clone(), store.get(&share.object).unwrap_or(&0) + 1); + store.insert(share.object, store.get(&share.object).unwrap_or(&0) + 1); } } diff --git a/src/errors.rs b/src/errors.rs index 446fdbd..a346c89 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -62,6 +62,10 @@ impl UpubError { pub fn internal_server_error() -> Self { Self::Status(axum::http::StatusCode::INTERNAL_SERVER_ERROR) } + + pub fn field(field: &'static str) -> Self { + Self::Field(crate::model::FieldError(field)) + } } pub type UpubResult = Result; diff --git a/src/model/activity.rs b/src/model/activity.rs index eddc86d..44d5aa7 100644 --- a/src/model/activity.rs +++ b/src/model/activity.rs @@ -89,6 +89,7 @@ impl Entity { } impl ActiveModel { + //#[deprecated = "should remove this, get models thru normalizer"] pub fn new(activity: &impl apb::Activity) -> Result { Ok(ActiveModel { internal: sea_orm::ActiveValue::NotSet, diff --git a/src/model/addressing.rs b/src/model/addressing.rs index 72ac9b3..8bbd139 100644 --- a/src/model/addressing.rs +++ b/src/model/addressing.rs @@ -97,15 +97,6 @@ pub enum Event { impl Event { - pub fn id(&self) -> &str { - match self { - Event::Tombstone => "", - Event::Activity(x) => x.id.as_str(), - Event::StrayObject { object, liked: _ } => object.id.as_str(), - Event::DeepActivity { activity: _, liked: _, object } => object.id.as_str(), - } - } - pub fn internal(&self) -> i64 { match self { Event::Tombstone => 0, diff --git a/src/model/attachment.rs b/src/model/attachment.rs index ac32246..3956819 100644 --- a/src/model/attachment.rs +++ b/src/model/attachment.rs @@ -72,10 +72,9 @@ impl BatchFillable for &[Event] { let mut out : std::collections::BTreeMap> = std::collections::BTreeMap::new(); for attach in attachments.into_iter().flatten() { - if out.contains_key(&attach.object) { - out.get_mut(&attach.object).expect("contains but get failed?").push(attach); - } else { - out.insert(attach.object, vec![attach]); + match out.entry(attach.object) { + std::collections::btree_map::Entry::Vacant(a) => { a.insert(vec![attach]); }, + std::collections::btree_map::Entry::Occupied(mut e) => { e.get_mut().push(attach); }, } } diff --git a/src/routes/activitypub/inbox.rs b/src/routes/activitypub/inbox.rs index a0a0a23..32bb379 100644 --- a/src/routes/activitypub/inbox.rs +++ b/src/routes/activitypub/inbox.rs @@ -63,7 +63,7 @@ pub async fn post( return Err(UpubError::bad_request()); }; - if !(server == Context::server(&actor)) { + if server != Context::server(&actor) { return Err(UpubError::unauthorized()); } diff --git a/src/server/auth.rs b/src/server/auth.rs index 1bba370..2eddd9d 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -40,11 +40,11 @@ impl Identity { } } - pub fn is(&self, id: &str) -> bool { + pub fn is(&self, uid: &str) -> bool { match self { Identity::Anonymous => false, Identity::Remote { .. } => false, // TODO per-actor server auth should check this - Identity::Local { id, .. } => id.as_str() == id + Identity::Local { id, .. } => id.as_str() == uid } } diff --git a/src/server/context.rs b/src/server/context.rs index 3064568..2bd443b 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeSet, sync::Arc}; use openssl::rsa::Rsa; -use sea_orm::{ActiveValue::NotSet, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, RelationTrait, SelectColumns, Set}; +use sea_orm::{ActiveValue::NotSet, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, SelectColumns, Set}; use crate::{config::Config, errors::UpubError, model, server::fetcher::Fetcher}; use uriproxy::UriClass; @@ -150,6 +150,7 @@ impl Context { } // TODO remove this!! + //#[deprecated = "context is id of first post in thread"] pub fn context_id(&self, id: &str) -> String { if id.starts_with("tag:") { return id.to_string(); @@ -227,8 +228,8 @@ impl Context { { let (server, actor) = if target == apb::target::PUBLIC { (None, None) } else { ( - Some(model::instance::Entity::domain_to_internal(&Context::server(&target), self.db()).await?), - Some(model::actor::Entity::ap_to_internal(&target, self.db()).await?), + Some(model::instance::Entity::domain_to_internal(&Context::server(target), self.db()).await?), + Some(model::actor::Entity::ap_to_internal(target, self.db()).await?), ) }; addressing.push( @@ -290,6 +291,7 @@ impl Context { Ok(()) } + //#[deprecated = "should probably directly invoke address_to() since we most likely have internal ids at this point"] pub async fn dispatch(&self, uid: &str, activity_targets: Vec, aid: &str, oid: Option<&str>) -> crate::Result<()> { let addressed = self.expand_addressing(activity_targets).await?; let internal_aid = model::activity::Entity::ap_to_internal(aid, self.db()).await?; diff --git a/src/server/fetcher.rs b/src/server/fetcher.rs index 4762fc1..6fbc65d 100644 --- a/src/server/fetcher.rs +++ b/src/server/fetcher.rs @@ -180,20 +180,13 @@ impl Fetcher for Context { } let activity_document = self.pull_activity(id).await?; - let activity_model = model::activity::ActiveModel::new(&activity_document)?; + let activity_model = self.insert_activity(activity_document, Some(Context::server(id))).await?; - model::activity::Entity::insert(activity_model) - .exec(self.db()).await?; - - // TODO fetch it back to get the internal id - let activity = model::activity::Entity::find_by_ap_id(id) - .one(self.db()).await?.ok_or_else(UpubError::internal_server_error)?; - - let addressed = activity.addressed(); + let addressed = activity_model.addressed(); let expanded_addresses = self.expand_addressing(addressed).await?; - self.address_to(Some(activity.internal), None, &expanded_addresses).await?; + self.address_to(Some(activity_model.internal), None, &expanded_addresses).await?; - Ok(activity) + Ok(activity_model) } async fn pull_activity(&self, id: &str) -> crate::Result { @@ -216,7 +209,7 @@ impl Fetcher for Context { Ok(activity) } - async fn fetch_thread(&self, id: &str) -> crate::Result<()> { + async fn fetch_thread(&self, _id: &str) -> crate::Result<()> { // crawl_replies(self, id, 0).await todo!() } diff --git a/src/server/inbox.rs b/src/server/inbox.rs index 1a10650..998980d 100644 --- a/src/server/inbox.rs +++ b/src/server/inbox.rs @@ -1,8 +1,8 @@ use apb::{target::Addressed, Activity, Base, Object}; use reqwest::StatusCode; -use sea_orm::{sea_query::Expr, ActiveModelTrait, ColumnTrait, Condition, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, SelectColumns, Set}; +use sea_orm::{sea_query::Expr, ActiveValue::{Set, NotSet}, ColumnTrait, Condition, EntityTrait, QueryFilter, QuerySelect, SelectColumns}; -use crate::{errors::{LoggableError, UpubError}, model::{self, FieldError}, server::normalizer::Normalizer}; +use crate::{errors::{LoggableError, UpubError}, model, server::normalizer::Normalizer}; use super::{fetcher::Fetcher, Context}; @@ -13,48 +13,43 @@ impl apb::server::Inbox for Context { type Activity = serde_json::Value; async fn create(&self, server: String, activity: serde_json::Value) -> crate::Result<()> { - let activity_model = model::activity::Model::new(&activity)?; - let aid = activity_model.id.clone(); let Some(object_node) = activity.object().extract() else { // TODO we could process non-embedded activities or arrays but im lazy rn tracing::error!("refusing to process activity without embedded object: {}", serde_json::to_string_pretty(&activity).unwrap()); return Err(UpubError::unprocessable()); }; + let activity_model = self.insert_activity(activity, Some(server.clone())).await?; let object_model = self.insert_object(object_node, Some(server)).await?; - let expanded_addressing = self.expand_addressing(activity.addressed()).await?; - self.address_to(Some(&aid), Some(&object_model.id), &expanded_addressing).await?; - tracing::info!("{} posted {}", aid, object_model.id); + let expanded_addressing = self.expand_addressing(activity_model.addressed()).await?; + self.address_to(Some(activity_model.internal), Some(object_model.internal), &expanded_addressing).await?; + tracing::info!("{} posted {}", activity_model.id, object_model.id); Ok(()) } - async fn like(&self, _: String, activity: serde_json::Value) -> crate::Result<()> { - let aid = activity.id().ok_or(UpubError::bad_request())?; + async fn like(&self, server: String, activity: serde_json::Value) -> crate::Result<()> { let uid = activity.actor().id().ok_or(UpubError::bad_request())?; + let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; let object_uri = activity.object().id().ok_or(UpubError::bad_request())?; let obj = self.fetch_object(&object_uri).await?; - let oid = obj.id; let like = model::like::ActiveModel { - id: sea_orm::ActiveValue::NotSet, - actor: sea_orm::Set(uid.clone()), - likes: sea_orm::Set(oid.clone()), - date: sea_orm::Set(activity.published().unwrap_or(chrono::Utc::now())), + internal: NotSet, + actor: Set(internal_uid), + object: Set(obj.internal), + published: Set(activity.published().unwrap_or(chrono::Utc::now())), }; match model::like::Entity::insert(like).exec(self.db()).await { Err(sea_orm::DbErr::RecordNotInserted) => Err(UpubError::not_modified()), Err(sea_orm::DbErr::Exec(_)) => Err(UpubError::not_modified()), // bad fix for sqlite Err(e) => { - tracing::error!("unexpected error procesing like from {uid} to {oid}: {e}"); + tracing::error!("unexpected error procesing like from {uid} to {}: {e}", obj.id); Err(UpubError::internal_server_error()) } Ok(_) => { - let activity_model = model::activity::Model::new(&activity)?.into_active_model(); - model::activity::Entity::insert(activity_model) - .exec(self.db()) - .await?; - let mut expanded_addressing = self.expand_addressing(activity.addressed()).await?; + let activity_model = self.insert_activity(activity, Some(server)).await?; + let mut expanded_addressing = self.expand_addressing(activity_model.addressed()).await?; if expanded_addressing.is_empty() { // WHY MASTODON!!!!!!! expanded_addressing.push( - model::object::Entity::find_by_id(&oid) + model::object::Entity::find_by_id(obj.internal) .select_only() .select_column(model::object::Column::AttributedTo) .into_tuple::() @@ -63,190 +58,188 @@ impl apb::server::Inbox for Context { .ok_or_else(UpubError::not_found)? ); } - self.address_to(Some(aid), None, &expanded_addressing).await?; + self.address_to(Some(activity_model.internal), None, &expanded_addressing).await?; model::object::Entity::update_many() .col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).add(1)) - .filter(model::object::Column::Id.eq(oid.clone())) + .filter(model::object::Column::Internal.eq(obj.internal)) .exec(self.db()) .await?; - tracing::info!("{} liked {}", uid, oid); + tracing::info!("{} liked {}", uid, obj.id); Ok(()) }, } } async fn follow(&self, _: String, activity: serde_json::Value) -> crate::Result<()> { - let activity_model = model::activity::Model::new(&activity)?; - let aid = activity_model.id.clone(); - let target_user_uri = activity_model.object - .as_deref() - .ok_or_else(UpubError::bad_request)? - .to_string(); - let usr = self.fetch_user(&target_user_uri).await?; - let target_user_id = usr.id; - tracing::info!("{} wants to follow {}", activity_model.actor, target_user_id); - model::activity::Entity::insert(activity_model.into_active_model()) + let aid = activity.id().ok_or_else(UpubError::bad_request)?.to_string(); + let source_actor = activity.actor().id().ok_or_else(UpubError::bad_request)?; + let source_actor_internal = model::actor::Entity::ap_to_internal(&source_actor, self.db()).await?; + let target_actor = activity.object().id().ok_or_else(UpubError::bad_request)?; + let usr = self.fetch_user(&target_actor).await?; + let activity_model = model::activity::ActiveModel::new(&activity)?; + model::activity::Entity::insert(activity_model) + .exec(self.db()).await?; + let internal_aid = model::activity::Entity::ap_to_internal(&aid, self.db()).await?; + let relation_model = model::relation::ActiveModel { + internal: NotSet, + accept: Set(None), + activity: Set(internal_aid), + follower: Set(source_actor_internal), + following: Set(usr.internal), + }; + model::relation::Entity::insert(relation_model) .exec(self.db()).await?; let mut expanded_addressing = self.expand_addressing(activity.addressed()).await?; - if !expanded_addressing.contains(&target_user_id) { - expanded_addressing.push(target_user_id); + if !expanded_addressing.contains(&target_actor) { + expanded_addressing.push(target_actor); } - self.address_to(Some(&aid), None, &expanded_addressing).await?; + self.address_to(Some(internal_aid), None, &expanded_addressing).await?; + tracing::info!("{} wants to follow {}", source_actor, usr.id); Ok(()) } async fn accept(&self, _: String, activity: serde_json::Value) -> crate::Result<()> { // TODO what about TentativeAccept - let activity_model = model::activity::Model::new(&activity)?; - - if let Some(mut r) = model::relay::Entity::find_by_id(&activity_model.actor) + let aid = activity.id().ok_or_else(UpubError::bad_request)?.to_string(); + let target_actor = activity.actor().id().ok_or_else(UpubError::bad_request)?; + let follow_request_id = activity.object().id().ok_or_else(UpubError::bad_request)?; + let follow_activity = model::activity::Entity::find_by_ap_id(&follow_request_id) .one(self.db()) .await? - { - r.accepted = true; - model::relay::Entity::update(r.into_active_model()).exec(self.db()).await?; - model::activity::Entity::insert(activity_model.clone().into_active_model()) - .exec(self.db()) - .await?; - tracing::info!("relay {} is now broadcasting to us", activity_model.actor); - return Ok(()); - } + .ok_or_else(UpubError::not_found)?; - let Some(follow_request_id) = &activity_model.object else { - return Err(UpubError::bad_request()); - }; - let Some(follow_activity) = model::activity::Entity::find_by_id(follow_request_id) - .one(self.db()).await? - else { - return Err(UpubError::not_found()); - }; - if follow_activity.object.unwrap_or("".into()) != activity_model.actor { + if follow_activity.object.unwrap_or("".into()) != follow_activity.actor { return Err(UpubError::forbidden()); } - tracing::info!("{} accepted follow request by {}", activity_model.actor, follow_activity.actor); + let activity_model = model::activity::ActiveModel::new(&activity)?; + model::activity::Entity::insert(activity_model) + .exec(self.db()) + .await?; + let accept_internal_id = model::activity::Entity::ap_to_internal(&aid, self.db()).await?; - model::activity::Entity::insert(activity_model.clone().into_active_model()) - .exec(self.db()) - .await?; - model::user::Entity::update_many() + model::actor::Entity::update_many() .col_expr( - model::user::Column::FollowingCount, - Expr::col(model::user::Column::FollowingCount).add(1) + model::actor::Column::FollowingCount, + Expr::col(model::actor::Column::FollowingCount).add(1) ) - .filter(model::user::Column::Id.eq(&follow_activity.actor)) + .filter(model::actor::Column::Id.eq(&follow_activity.actor)) .exec(self.db()) .await?; - model::relation::Entity::insert( - model::relation::ActiveModel { - follower: Set(follow_activity.actor.clone()), - following: Set(activity_model.actor), - ..Default::default() - } - ).exec(self.db()).await?; + model::actor::Entity::update_many() + .col_expr( + model::actor::Column::FollowersCount, + Expr::col(model::actor::Column::FollowersCount).add(1) + ) + .filter(model::actor::Column::Id.eq(&follow_activity.actor)) + .exec(self.db()) + .await?; + + model::relation::Entity::update_many() + .col_expr(model::relation::Column::Accept, Expr::value(Some(accept_internal_id))) + .filter(model::relation::Column::Activity.eq(follow_activity.internal)) + .exec(self.db()).await?; + + tracing::info!("{} accepted follow request by {}", target_actor, follow_activity.actor); let mut expanded_addressing = self.expand_addressing(activity.addressed()).await?; if !expanded_addressing.contains(&follow_activity.actor) { expanded_addressing.push(follow_activity.actor); } - self.address_to(Some(&activity_model.id), None, &expanded_addressing).await?; + self.address_to(Some(accept_internal_id), None, &expanded_addressing).await?; Ok(()) } async fn reject(&self, _: String, activity: serde_json::Value) -> crate::Result<()> { // TODO what about TentativeReject? - let activity_model = model::activity::Model::new(&activity)?; - let Some(follow_request_id) = &activity_model.object else { - return Err(UpubError::bad_request()); - }; - let Some(follow_activity) = model::activity::Entity::find_by_id(follow_request_id) - .one(self.db()).await? - else { - return Err(UpubError::not_found()); - }; - if follow_activity.object.unwrap_or("".into()) != activity_model.actor { + let aid = activity.id().ok_or_else(UpubError::bad_request)?.to_string(); + let uid = activity.actor().id().ok_or_else(UpubError::bad_request)?; + let follow_request_id = activity.object().id().ok_or_else(UpubError::bad_request)?; + let follow_activity = model::activity::Entity::find_by_ap_id(&follow_request_id) + .one(self.db()) + .await? + .ok_or_else(UpubError::not_found)?; + + if follow_activity.object.unwrap_or("".into()) != uid { return Err(UpubError::forbidden()); } - tracing::info!("{} rejected follow request by {}", activity_model.actor, follow_activity.actor); - - model::activity::Entity::insert(activity_model.clone().into_active_model()) + let activity_model = model::activity::ActiveModel::new(&activity)?; + model::activity::Entity::insert(activity_model) .exec(self.db()) .await?; + let internal_aid = model::activity::Entity::ap_to_internal(&aid, self.db()).await?; + + model::relation::Entity::delete_many() + .filter(model::relation::Column::Activity.eq(internal_aid)) + .exec(self.db()) + .await?; + + tracing::info!("{} rejected follow request by {}", uid, follow_activity.actor); let mut expanded_addressing = self.expand_addressing(activity.addressed()).await?; if !expanded_addressing.contains(&follow_activity.actor) { expanded_addressing.push(follow_activity.actor); } - self.address_to(Some(&activity_model.id), None, &expanded_addressing).await?; + + self.address_to(Some(internal_aid), None, &expanded_addressing).await?; Ok(()) } async fn delete(&self, _: String, activity: serde_json::Value) -> crate::Result<()> { - // TODO verify the signature before just deleting lmao - let oid = activity.object().id().ok_or(UpubError::bad_request())?; - tracing::debug!("deleting '{oid}'"); // this is so spammy wtf! - // TODO maybe we should keep the tombstone? - model::user::Entity::delete_by_id(&oid).exec(self.db()).await.info_failed("failed deleting from users"); - model::activity::Entity::delete_by_id(&oid).exec(self.db()).await.info_failed("failed deleting from activities"); - model::object::Entity::delete_by_id(&oid).exec(self.db()).await.info_failed("failed deleting from objects"); + let oid = activity.object().id().ok_or_else(UpubError::bad_request)?; + model::actor::Entity::delete_by_ap_id(&oid).exec(self.db()).await.info_failed("failed deleting from users"); + model::object::Entity::delete_by_ap_id(&oid).exec(self.db()).await.info_failed("failed deleting from objects"); + tracing::debug!("deleted '{oid}'"); Ok(()) } - async fn update(&self, server: String, activity: serde_json::Value) -> crate::Result<()> { - let activity_model = model::activity::Model::new(&activity)?; - let aid = activity_model.id.clone(); + async fn update(&self, _server: String, activity: serde_json::Value) -> crate::Result<()> { + let uid = activity.actor().id().ok_or_else(UpubError::bad_request)?; + let aid = activity.id().ok_or_else(UpubError::bad_request)?; let Some(object_node) = activity.object().extract() else { // TODO we could process non-embedded activities or arrays but im lazy rn tracing::error!("refusing to process activity without embedded object: {}", serde_json::to_string_pretty(&activity).unwrap()); return Err(UpubError::unprocessable()); }; - let Some(oid) = object_node.id().map(|x| x.to_string()) else { - return Err(UpubError::bad_request()); - }; - // make sure we're allowed to edit this object - if let Some(object_author) = object_node.attributed_to().id() { - if server != Context::server(&object_author) { - return Err(UpubError::forbidden()); - } - } else if server != Context::server(&oid) { - return Err(UpubError::forbidden()); - }; - match object_node.object_type() { - Some(apb::ObjectType::Actor(_)) => { - // TODO oof here is an example of the weakness of this model, we have to go all the way - // back up to serde_json::Value because impl Object != impl Actor - let actor_model = model::user::Model::new(&object_node)?; - let mut update_model = actor_model.into_active_model(); - update_model.updated = sea_orm::Set(chrono::Utc::now()); - update_model.reset(model::user::Column::Name); - update_model.reset(model::user::Column::Summary); - update_model.reset(model::user::Column::Image); - update_model.reset(model::user::Column::Icon); - model::user::Entity::update(update_model) - .exec(self.db()).await?; - }, - Some(apb::ObjectType::Note) => { - let object_model = model::object::Model::new(&object_node)?; - let mut update_model = object_model.into_active_model(); - update_model.updated = sea_orm::Set(Some(chrono::Utc::now())); - update_model.reset(model::object::Column::Name); - update_model.reset(model::object::Column::Summary); - update_model.reset(model::object::Column::Content); - update_model.reset(model::object::Column::Sensitive); - model::object::Entity::update(update_model) - .exec(self.db()).await?; - }, - Some(t) => tracing::warn!("no side effects implemented for update type {t:?}"), - None => tracing::warn!("empty type on embedded updated object"), - } + let oid = object_node.id().ok_or_else(UpubError::bad_request)?.to_string(); - tracing::info!("{} updated {}", aid, oid); - model::activity::Entity::insert(activity_model.into_active_model()) + let activity_model = model::activity::ActiveModel::new(&activity)?; + model::activity::Entity::insert(activity_model) .exec(self.db()) .await?; + let internal_aid = model::activity::Entity::ap_to_internal(aid, self.db()).await?; + + let internal_oid = match object_node.object_type().ok_or_else(UpubError::bad_request)? { + apb::ObjectType::Actor(_) => { + let internal_uid = model::actor::Entity::ap_to_internal(&oid, self.db()).await?; + let mut actor_model = model::actor::ActiveModel::new(&object_node)?; + actor_model.internal = Set(internal_uid); + actor_model.updated = Set(chrono::Utc::now()); + model::actor::Entity::update(actor_model) + .exec(self.db()) + .await?; + Some(internal_uid) + }, + apb::ObjectType::Note => { + let internal_oid = model::object::Entity::ap_to_internal(&oid, self.db()).await?; + let mut object_model = model::object::ActiveModel::new(&object_node)?; + object_model.internal = Set(internal_oid); + object_model.updated = Set(chrono::Utc::now()); + model::object::Entity::update(object_model) + .exec(self.db()) + .await?; + Some(internal_oid) + }, + t => { + tracing::warn!("no side effects implemented for update type {t:?}"); + None + }, + }; + + tracing::info!("{} updated {}", uid, oid); let expanded_addressing = self.expand_addressing(activity.addressed()).await?; - self.address_to(Some(&aid), Some(&oid), &expanded_addressing).await?; + self.address_to(Some(internal_aid), internal_oid, &expanded_addressing).await?; Ok(()) } @@ -254,9 +247,8 @@ impl apb::server::Inbox for Context { let uid = activity.actor().id().ok_or_else(UpubError::bad_request)?; // TODO in theory we could work with just object_id but right now only accept embedded let undone_activity = activity.object().extract().ok_or_else(UpubError::bad_request)?; - let undone_aid = undone_activity.id().ok_or_else(UpubError::bad_request)?; - let undone_object_uri = undone_activity.object().id().ok_or_else(UpubError::bad_request)?; let activity_type = undone_activity.activity_type().ok_or_else(UpubError::bad_request)?; + let undone_object_id = undone_activity.object().id().ok_or_else(UpubError::bad_request)?; let undone_activity_author = undone_activity.actor().id().ok_or_else(UpubError::bad_request)?; // can't undo activities from remote actors! @@ -264,32 +256,36 @@ impl apb::server::Inbox for Context { return Err(UpubError::forbidden()); }; - let obj = self.fetch_object(&undone_object_uri).await?; - let undone_object_id = obj.id; + self.insert_activity(activity.clone(), Some(server)).await?; match activity_type { apb::ActivityType::Like => { + let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; + let internal_oid = model::object::Entity::ap_to_internal(&undone_object_id, self.db()).await?; model::like::Entity::delete_many() .filter( Condition::all() - .add(model::like::Column::Actor.eq(&uid)) - .add(model::like::Column::Likes.eq(&undone_object_id)) + .add(model::like::Column::Actor.eq(internal_uid)) + .add(model::like::Column::Object.eq(internal_oid)) ) .exec(self.db()) .await?; model::object::Entity::update_many() - .filter(model::object::Column::Id.eq(&undone_object_id)) + .filter(model::object::Column::Internal.eq(internal_oid)) .col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).sub(1)) .exec(self.db()) .await?; }, apb::ActivityType::Follow => { + let undone_aid = undone_activity.id().ok_or_else(UpubError::bad_request)?; + let internal_aid = model::activity::Entity::ap_to_internal(undone_aid, self.db()).await?; model::relation::Entity::delete_many() - .filter( - Condition::all() - .add(model::relation::Column::Follower.eq(&uid)) - .add(model::relation::Column::Following.eq(&undone_object_id)) - ) + .filter(model::relation::Column::Activity.eq(internal_aid)) + .exec(self.db()) + .await?; + model::actor::Entity::update_many() + .filter(model::actor::Column::Id.eq(&undone_object_id)) + .col_expr(model::actor::Column::FollowersCount, Expr::col(model::actor::Column::FollowersCount).sub(1)) .exec(self.db()) .await?; }, @@ -299,48 +295,41 @@ impl apb::server::Inbox for Context { }, } - model::activity::Entity::delete_by_id(undone_aid).exec(self.db()).await?; - Ok(()) - } - async fn announce(&self, _: String, activity: serde_json::Value) -> crate::Result<()> { - let activity_model = model::activity::Model::new(&activity)?; - let Some(object_uri) = &activity_model.object else { - return Err(FieldError("object").into()); - }; - let obj = self.fetch_object(object_uri).await?; - let oid = obj.id; + async fn announce(&self, server: String, activity: serde_json::Value) -> crate::Result<()> { + let uid = activity.actor().id().ok_or_else(|| UpubError::field("actor"))?; + let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; + let announced_id = activity.object().id().ok_or_else(|| UpubError::field("object"))?; + let activity_model = self.insert_activity(activity.clone(), Some(server)).await?; + let announced = self.fetch_object(&announced_id).await?; // relays send us activities as Announce, but we don't really want to count those towards the // total shares count of an object, so just fetch the object and be done with it if self.is_relay(&activity_model.actor) { - tracing::info!("relay {} broadcasted {}", activity_model.actor, oid); + tracing::info!("relay {} broadcasted {}", activity_model.actor, announced_id); return Ok(()) } - let share = model::share::ActiveModel { - id: sea_orm::ActiveValue::NotSet, - actor: sea_orm::Set(activity_model.actor.clone()), - shares: sea_orm::Set(oid.clone()), - date: sea_orm::Set(activity.published().unwrap_or(chrono::Utc::now())), + let share = model::announce::ActiveModel { + internal: NotSet, + actor: Set(internal_uid), + object: Set(announced.internal), + published: Set(activity.published().unwrap_or(chrono::Utc::now())), }; let expanded_addressing = self.expand_addressing(activity.addressed()).await?; - self.address_to(Some(&activity_model.id), None, &expanded_addressing).await?; - model::share::Entity::insert(share) + self.address_to(Some(activity_model.internal), None, &expanded_addressing).await?; + model::announce::Entity::insert(share) .exec(self.db()).await?; - model::activity::Entity::insert(activity_model.clone().into_active_model()) - .exec(self.db()) - .await?; model::object::Entity::update_many() - .col_expr(model::object::Column::Shares, Expr::col(model::object::Column::Shares).add(1)) - .filter(model::object::Column::Id.eq(oid.clone())) + .col_expr(model::object::Column::Announces, Expr::col(model::object::Column::Announces).add(1)) + .filter(model::object::Column::Internal.eq(announced.internal)) .exec(self.db()) .await?; - tracing::info!("{} shared {}", activity_model.actor, oid); + tracing::info!("{} shared {}", activity_model.actor, announced.id); Ok(()) } } diff --git a/src/server/normalizer.rs b/src/server/normalizer.rs index 4d358c2..d7253a9 100644 --- a/src/server/normalizer.rs +++ b/src/server/normalizer.rs @@ -1,5 +1,5 @@ use apb::{Node, Base, Object, Document}; -use sea_orm::{sea_query::Expr, ActiveValue::Set, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; +use sea_orm::{sea_query::Expr, ActiveValue::{NotSet, Set}, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; use crate::{errors::UpubError, model, server::Context}; use super::fetcher::Fetcher; @@ -7,6 +7,7 @@ use super::fetcher::Fetcher; #[axum::async_trait] pub trait Normalizer { async fn insert_object(&self, obj: impl apb::Object, server: Option) -> crate::Result; + async fn insert_activity(&self, act: impl apb::Activity, server: Option) -> crate::Result; } #[axum::async_trait] @@ -129,4 +130,35 @@ impl Normalizer for super::Context { Ok(object) } + + async fn insert_activity(&self, activity: impl apb::Activity, server: Option) -> crate::Result { + let mut activity_model = model::activity::Model { + internal: 0, + id: activity.id().ok_or_else(|| UpubError::field("id"))?.to_string(), + activity_type: activity.activity_type().ok_or_else(|| UpubError::field("type"))?, + actor: activity.actor().id().ok_or_else(|| UpubError::field("actor"))?, + object: activity.object().id(), + target: activity.target().id(), + published: activity.published().unwrap_or(chrono::Utc::now()), + to: activity.to().into(), + bto: activity.bto().into(), + cc: activity.cc().into(), + bcc: activity.bcc().into(), + }; + if let Some(server) = server { + if Context::server(&activity_model.actor) != server + || Context::server(&activity_model.id) != server { + return Err(UpubError::forbidden()); + } + } + let mut active_model = activity_model.clone().into_active_model(); + active_model.internal = NotSet; + model::activity::Entity::insert(active_model) + .exec(self.db()) + .await?; + + let internal = model::activity::Entity::ap_to_internal(&activity_model.id, self.db()).await?; + activity_model.internal = internal; + Ok(activity_model) + } } diff --git a/src/server/outbox.rs b/src/server/outbox.rs index 8cb3dc0..3211652 100644 --- a/src/server/outbox.rs +++ b/src/server/outbox.rs @@ -367,8 +367,10 @@ impl apb::server::Outbox for Context { return Err(UpubError::forbidden()); } - let mut new_actor_model = model::actor::ActiveModel::default(); - new_actor_model.internal = Unchanged(old_actor_model.internal); + let mut new_actor_model = model::actor::ActiveModel { + internal: Unchanged(old_actor_model.internal), + ..Default::default() + }; if let Some(name) = object_node.name() { new_actor_model.name = Set(Some(name.to_string())); @@ -398,8 +400,10 @@ impl apb::server::Outbox for Context { return Err(UpubError::forbidden()); } - let mut new_object_model = model::object::ActiveModel::default(); - new_object_model.internal = Unchanged(old_object_model.internal); + let mut new_object_model = model::object::ActiveModel { + internal: Unchanged(old_object_model.internal), + ..Default::default() + }; if let Some(name) = object_node.name() { new_object_model.name = Set(Some(name.to_string())); diff --git a/web/src/components/post.rs b/web/src/components/post.rs index 3a9b39d..d6d89fe 100644 --- a/web/src/components/post.rs +++ b/web/src/components/post.rs @@ -1,7 +1,6 @@ use apb::{ActivityMut, Base, BaseMut, Object, ObjectMut}; use leptos::*; -use leptos_use::DebounceOptions; use crate::{prelude::*, WEBFINGER}; #[derive(Debug, Clone, Copy, Default)] diff --git a/web/src/components/timeline.rs b/web/src/components/timeline.rs index 8fee171..a86b50b 100644 --- a/web/src/components/timeline.rs +++ b/web/src/components/timeline.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeSet, pin::Pin, sync::Arc}; use apb::{Activity, ActivityMut, Base, Object}; use leptos::*; -use leptos_use::{signal_debounced, signal_throttled, use_display_media, use_document_visibility, use_element_size, use_infinite_scroll_with_options, use_scroll, use_scroll_with_options, use_window, use_window_scroll, UseDisplayMediaReturn, UseElementSizeReturn, UseInfiniteScrollOptions, UseScrollOptions, UseScrollReturn}; +use leptos_use::{signal_throttled, use_element_size, use_window_scroll, UseElementSizeReturn}; use crate::prelude::*; #[derive(Debug, Clone, Copy)] diff --git a/web/src/lib.rs b/web/src/lib.rs index 362dfc0..b1b68b0 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -99,7 +99,7 @@ impl WebfingerCache { Ok(res) => match res.error_for_status() { Ok(res) => match res.json::().await { Ok(doc) => { - if let Some(uid) = doc.links.into_iter().find(|x| x.rel == "self").map(|x| x.href).flatten() { + if let Some(uid) = doc.links.into_iter().find(|x| x.rel == "self").and_then(|x| x.href) { self.0.insert(query, LookupStatus::Found(uid)); } else { self.0.insert(query, LookupStatus::NotFound); diff --git a/web/src/page/register.rs b/web/src/page/register.rs index 981ed45..395b45d 100644 --- a/web/src/page/register.rs +++ b/web/src/page/register.rs @@ -19,13 +19,13 @@ pub fn RegisterPage() -> impl IntoView {
( Method::PUT, &format!("{URL_BASE}/auth"), None, auth ).await { - Ok(x) => {}, + Ok(_x) => {}, Err(e) => set_error.set(Some( view! {
{e.to_string()}
} )), From 53c8aec8b4f6eec7484d0b2b7b01278cc603f452 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 06:20:54 +0200 Subject: [PATCH 16/90] feat: allow specifying bind address --- src/main.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 51d1065..c1e7446 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,7 +51,11 @@ struct Args { #[derive(Clone, Subcommand)] enum Mode { /// run fediverse server - Serve, + Serve { + #[arg(short, long, default_value="127.0.0.1:3000")] + /// addr to bind and serve onto + bind: String, + }, /// print current or default configuration Config, @@ -114,7 +118,7 @@ async fn main() { Mode::Config => println!("{}", toml::to_string_pretty(&config).expect("failed serializing config")), - Mode::Serve => { + Mode::Serve { bind } => { let ctx = server::Context::new(db, domain, config) .await.expect("failed creating server context"); @@ -129,7 +133,7 @@ async fn main() { .with_state(ctx); // run our app with hyper, listening locally on port 3000 - let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") + let listener = tokio::net::TcpListener::bind(bind) .await.expect("could not bind tcp socket"); axum::serve(listener, router) From 9588dd1e234f6648fe3ce9852476f90c5bbedb6c Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 06:26:11 +0200 Subject: [PATCH 17/90] build: update lockfile --- Cargo.lock | 671 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 448 insertions(+), 223 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8d3f0e..24a006e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,47 +79,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -127,9 +128,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "apb" @@ -153,13 +160,13 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-recursion" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -181,7 +188,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -192,7 +199,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -204,6 +211,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "attribute-derive" version = "0.9.1" @@ -215,7 +228,7 @@ dependencies = [ "manyhow", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -231,14 +244,14 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" @@ -318,9 +331,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -401,9 +414,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0901fc8eb0aca4c83be0106d6f2db17d86a08dfc2c25f0e84464bf381158add6" +checksum = "dbe5b10e214954177fb1dc9fbd20a1a2608fe99e6c832033bdc7cea287a20d77" dependencies = [ "borsh-derive", "cfg_aliases", @@ -411,15 +424,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51670c3aa053938b0ee3bd67c3817e471e626151131b934038e83c5bf8de48f5" +checksum = "d7a8646f94ab393e43e8b35a2558b1624bed28b97ee09c5d15456e3c9463f46d" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "syn_derive", ] @@ -465,15 +478,15 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" [[package]] name = "cc" -version = "1.0.94" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg-if" @@ -561,7 +574,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -578,9 +591,9 @@ checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "comrak" @@ -627,16 +640,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "console_log" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" -dependencies = [ - "log", - "web-sys", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -725,9 +728,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -743,9 +746,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -785,12 +788,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ - "darling_core 0.20.8", - "darling_macro 0.20.8", + "darling_core 0.20.9", + "darling_macro 0.20.9", ] [[package]] @@ -823,16 +826,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.60", + "strsim 0.11.1", + "syn 2.0.66", ] [[package]] @@ -859,13 +862,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ - "darling_core 0.20.8", + "darling_core 0.20.9", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -875,7 +878,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -887,10 +890,10 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fa90da96b8fd491f5754d1f7a731f73921e3b7aa0ce333c821a0e43666ac14" dependencies = [ - "darling 0.20.8", + "darling 0.20.9", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -933,7 +936,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -969,9 +972,9 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.4.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" +checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" [[package]] name = "digest" @@ -999,9 +1002,9 @@ checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" dependencies = [ "serde", ] @@ -1029,18 +1032,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" dependencies = [ "serde", + "typeid", ] [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1075,9 +1079,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "finl_unicode" @@ -1219,7 +1223,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -1264,9 +1268,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -1288,15 +1292,95 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "gloo-net" -version = "0.5.0" +name = "gloo" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net 0.3.1", + "gloo-render", + "gloo-storage", + "gloo-timers 0.2.6", + "gloo-utils 0.1.7", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen 0.5.0", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" dependencies = [ "futures-channel", "futures-core", "futures-sink", - "gloo-utils", + "gloo-utils 0.1.7", "http 0.2.12", "js-sys", "pin-project", @@ -1308,6 +1392,62 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -1320,6 +1460,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-utils" version = "0.2.0" @@ -1334,16 +1487,33 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.4.4" +name = "gloo-worker" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http 1.1.0", "indexmap", "slab", @@ -1373,9 +1543,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", "allocator-api2", @@ -1387,7 +1557,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -1464,7 +1634,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -1636,7 +1806,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -1647,7 +1817,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -1668,6 +1838,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -1733,17 +1909,17 @@ dependencies = [ [[package]] name = "leptos-use" -version = "0.10.7" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6be6f1d02c3277f03a55c4a05ee5a069ea75ec5323c4415f6efed69cb5acea" +checksum = "3272d90b77cdbb99e9060f90eb6f5738e56128b2f912db57a50efb006a26e262" dependencies = [ "async-trait", "cfg-if", "cookie", "default-struct-builder", "futures-util", - "gloo-timers", - "gloo-utils", + "gloo-timers 0.3.0", + "gloo-utils 0.2.0", "js-sys", "lazy_static", "leptos", @@ -1813,7 +1989,7 @@ dependencies = [ "quote", "rstml", "serde", - "syn 2.0.60", + "syn 2.0.66", "walkdir", ] @@ -1835,7 +2011,7 @@ dependencies = [ "quote", "rstml", "server_fn_macro", - "syn 2.0.60", + "syn 2.0.66", "tracing", "uuid", ] @@ -1846,7 +2022,7 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057de706568ce8f1f223ae69f796c10ad0563ad270d10717e70c2b2d22eefa60" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "cfg-if", "futures", "indexmap", @@ -1856,7 +2032,7 @@ dependencies = [ "rustc-hash", "self_cell", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.6.5", "serde_json", "slotmap", "thiserror", @@ -1873,7 +2049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7fcc2a95a20c8f41adb39770e65c48ffe33cd9503b83669c54edd9b33ba8aa8" dependencies = [ "cfg-if", - "gloo-net", + "gloo-net 0.5.0", "itertools", "js-sys", "lazy_static", @@ -1910,9 +2086,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -1955,15 +2131,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1994,7 +2170,7 @@ dependencies = [ "manyhow-macros", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2090,9 +2266,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -2164,11 +2340,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -2207,9 +2382,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -2218,9 +2393,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -2296,7 +2471,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2347,7 +2522,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2364,9 +2539,9 @@ checksum = "56d80efc4b6721e8be2a10a5df21a30fa0b470f1539e53d8b4e6e75faf938b63" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -2374,22 +2549,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" @@ -2486,7 +2661,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2562,12 +2737,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2616,9 +2791,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -2631,7 +2806,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "version_check", "yansi", ] @@ -2694,7 +2869,7 @@ dependencies = [ "proc-macro-utils", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2742,6 +2917,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "regex" version = "1.10.4" @@ -2797,11 +2981,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e6cc1e89e689536eb5aeede61520e874df5a4707df811cd5da4aa5fbb2aae19" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", @@ -2910,7 +3094,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.60", + "syn 2.0.66", "syn_derive", "thiserror", ] @@ -2933,9 +3117,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -2945,9 +3129,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -2958,9 +3142,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "ring", "rustls-webpki", @@ -2982,15 +3166,15 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" @@ -3004,15 +3188,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -3058,7 +3242,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -3116,7 +3300,7 @@ dependencies = [ "proc-macro2", "quote", "sea-bae", - "syn 2.0.60", + "syn 2.0.66", "unicode-ident", ] @@ -3180,7 +3364,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "thiserror", ] @@ -3215,11 +3399,11 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -3228,9 +3412,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -3238,9 +3422,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "send_wrapper" @@ -3253,9 +3437,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] @@ -3268,7 +3452,18 @@ checksum = "9980133dc534d02ab08df3b384295223a45090c40a4c46240e3eaa982b495910" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", ] [[package]] @@ -3296,13 +3491,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -3316,9 +3511,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -3348,9 +3543,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -3387,7 +3582,7 @@ dependencies = [ "const_format", "dashmap", "futures", - "gloo-net", + "gloo-net 0.5.0", "http 1.1.0", "js-sys", "once_cell", @@ -3415,7 +3610,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "xxhash-rust", ] @@ -3426,7 +3621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4ad11700cbccdbd313703916eb8c97301ee423c4a06e5421b77956fdcb36a9f" dependencies = [ "server_fn_macro", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -3481,9 +3676,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3547,9 +3742,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3965,9 +4160,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -3983,7 +4178,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -4083,22 +4278,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -4157,6 +4352,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tld" +version = "2.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ca5fc340fcb4f52570c502cf443fc22d5521e9ef2bb03528e3634254016cf7" +dependencies = [ + "phf", + "phf_codegen", +] + [[package]] name = "tokio" version = "1.37.0" @@ -4184,7 +4389,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -4210,35 +4415,34 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -4256,15 +4460,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.6", + "winnow 0.6.8", ] [[package]] @@ -4332,7 +4536,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -4374,6 +4578,17 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tracing-subscriber-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79804e80980173c6c8e53d98508eb24a2dbc4ee17a3e8d2ca8e5bad6bf13a898" +dependencies = [ + "gloo", + "tracing", + "tracing-subscriber", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -4403,9 +4618,15 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.17.0" @@ -4464,7 +4685,7 @@ dependencies = [ "apb", "async-recursion", "axum", - "base64 0.22.0", + "base64 0.22.1", "chrono", "clap", "futures", @@ -4474,6 +4695,7 @@ dependencies = [ "nodeinfo", "openssl", "rand", + "regex", "reqwest", "sea-orm", "sea-orm-migration", @@ -4500,9 +4722,9 @@ dependencies = [ "apb", "chrono", "console_error_panic_hook", - "console_log", "dashmap", "futures", + "jrd", "lazy_static", "leptos", "leptos-use", @@ -4515,7 +4737,10 @@ dependencies = [ "serde_default", "serde_json", "thiserror", + "tld", "tracing", + "tracing-subscriber", + "tracing-subscriber-wasm", "uriproxy", "web-sys", ] @@ -4524,7 +4749,7 @@ dependencies = [ name = "uriproxy" version = "0.1.0" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", ] [[package]] @@ -4580,9 +4805,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -4590,9 +4815,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc35703541cbccb5278ef7b589d79439fc808ff0b5867195a3230f9a47421d39" +checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b" dependencies = [ "erased-serde", "serde", @@ -4601,9 +4826,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "285b43c29d0b4c0e65aad24561baee67a1b69dc9be9375d4a85138cbf556f7f8" +checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b" dependencies = [ "sval", "sval_buffer", @@ -4678,7 +4903,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -4712,7 +4937,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4758,7 +4983,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "redox_syscall", + "redox_syscall 0.4.1", "wasite", ] @@ -4780,11 +5005,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4952,9 +5177,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -5007,22 +5232,22 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] From b5d3e4e864f4a15f5d2d4974e86a5111a77627e8 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 06:28:04 +0200 Subject: [PATCH 18/90] ci: new db new me --- .tci | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.tci b/.tci index 8f71056..7ff0ce0 100755 --- a/.tci +++ b/.tci @@ -7,13 +7,13 @@ systemctl --user stop upub echo "installing new binary" cp ./target/release/upub /opt/bin/upub echo "migrating database" -/opt/bin/upub --db "sqlite:///srv/tci/upub.db" --domain https://feditest.alemi.dev migrate +/opt/bin/upub --db "sqlite:///srv/tci/upub.db" --domain https://upub.alemi.dev migrate echo "restarting service" systemctl --user start upub echo "rebuilding frontend" cd web -CARGO_BUILD_JOBS=1 /opt/bin/trunk build --release --public-url 'https://feditest.alemi.dev/web' +CARGO_BUILD_JOBS=1 /opt/bin/trunk build --release --public-url 'https://upub.alemi.dev/web' echo "deploying frontend" -rm /srv/http/feditest/web/* -mv ./dist/* /srv/http/feditest/web/ +rm /srv/http/upub/web/* +mv ./dist/* /srv/http/upub/web/ echo "done" From 65a9c29fbd82addbdd61f9523df0e339ca6d2db9 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 06:35:15 +0200 Subject: [PATCH 19/90] fix: properly create app with id, load relays --- src/server/context.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/server/context.rs b/src/server/context.rs index 2bd443b..1c04cf1 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -21,7 +21,12 @@ struct ContextInner { // TODO keep these pre-parsed app: model::actor::Model, pkey: String, - relays: BTreeSet, + relay: Relays, +} + +pub struct Relays { + sources: BTreeSet, + sinks: BTreeSet, } #[macro_export] @@ -57,7 +62,7 @@ impl Context { let pubk = std::str::from_utf8(&rsa.public_key_to_pem()?)?.to_string(); let system = model::actor::ActiveModel { internal: NotSet, - id: NotSet, + id: Set(base_url.clone()), domain: Set(domain.clone()), preferred_username: Set(domain.clone()), actor_type: Set(apb::ActorType::Application), @@ -87,22 +92,16 @@ impl Context { // TODO maybe we could provide a more descriptive error... let pkey = app.private_key.as_deref().ok_or_else(UpubError::internal_server_error)?.to_string(); - // TODO how to handle relations when there's two to the same model??? - // let relays = model::relation::Entity::find() - // .filter(model::relation::Column::Following.eq(app.internal)) - // .filter(model::relation::Column::Accept.is_not_null()) - // .left_join(model::relation::Relation::ActorsFollower.def()) - // .select_only() - // .select_column(model::actor::Column::Id) - // .into_tuple::() - // .all(&db) - // .await?; + let relay_sinks = model::relation::Entity::followers(&app.id, &db).await?; + let relay_sources = model::relation::Entity::following(&app.id, &db).await?; - let relays = Vec::new(); + let relay = Relays { + sources: BTreeSet::from_iter(relay_sources), + sinks: BTreeSet::from_iter(relay_sinks), + }; Ok(Context(Arc::new(ContextInner { - base_url, db, domain, protocol, app, dispatcher, config, pkey, - relays: BTreeSet::from_iter(relays.into_iter()), + base_url, db, domain, protocol, app, dispatcher, config, pkey, relay, }))) } From ead63ad4462b747eddcf311e4929eacbbd3e636f Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 06:39:45 +0200 Subject: [PATCH 20/90] fix: check bot source and sink relays --- src/server/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/context.rs b/src/server/context.rs index 1c04cf1..1db4b34 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -301,6 +301,6 @@ impl Context { } pub fn is_relay(&self, id: &str) -> bool { - self.0.relays.contains(id) + self.0.relay.sources.contains(id) || self.0.relay.sinks.contains(id) } } From 2ee3bf67f6815aef0366cceb61f5da38467e4935 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 06:53:06 +0200 Subject: [PATCH 21/90] fix(web): oops really need to make this configurable --- web/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib.rs b/web/src/lib.rs index b1b68b0..bfb4dbc 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -10,7 +10,7 @@ pub use auth::Auth; pub mod prelude; -pub const URL_BASE: &str = "https://feditest.alemi.dev"; +pub const URL_BASE: &str = "https://upub.alemi.dev"; pub const URL_PREFIX: &str = "/web"; pub const URL_SENSITIVE: &str = "https://cdn.alemi.dev/social/nsfw.png"; pub const DEFAULT_AVATAR_URL: &str = "https://cdn.alemi.dev/social/gradient.png"; From a3fe45d36bbb57cfff904762e03a260dec73d248 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 06:54:57 +0200 Subject: [PATCH 22/90] fix: more rebrandinggg --- README.md | 2 +- web/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 305855e..58719a4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ all interactions happen with ActivityPub's client-server methods (basically POST development is still active, so expect more stuff to come! since most fediverse software uses Mastodon's API, μpub plans to implement it as an optional feature, becoming eventually compatible with most existing frontends and mobile applications, but focus right now is on producing something specific to μpub needs -a test instance is _usually_ available at [feditest.alemi.dev](https://feditest.alemi.dev) +a test instance is _usually_ available at [upub.alemi.dev](https://upub.alemi.dev) ## about the database schema im going to be very real i tried to do migrations but its getting super messy so until further notice assume db to be volatile. next change may be a migration (easy!) or a whole db rebuild (aaaaaaaaaa...), so if you're not comfortable with either manually exporting/importing or dropping and starting from scratch, **you really shouldn't put upub in prod yet**! diff --git a/web/index.html b/web/index.html index b4d81ec..a71a7d5 100644 --- a/web/index.html +++ b/web/index.html @@ -9,7 +9,7 @@ - + From 570b045bf0bf7e2dbb5b5c4068335687b4bffa34 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 07:16:37 +0200 Subject: [PATCH 23/90] fix: hash pwd! --- src/server/admin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/admin.rs b/src/server/admin.rs index ca69feb..bd7c2ba 100644 --- a/src/server/admin.rs +++ b/src/server/admin.rs @@ -74,7 +74,7 @@ impl Administrable for super::Context { internal: NotSet, actor: Set(ap_id), login: Set(username), - password: Set(password), + password: Set(sha256::digest(password)), }; crate::model::credential::Entity::insert(credentials_model) From c5b06cd16b9c600f96db9160e792b7f7bc5793de Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 07:26:31 +0200 Subject: [PATCH 24/90] feat: added refresh route (optional) --- src/config.rs | 3 ++ src/routes/activitypub/auth.rs | 53 +++++++++++++++++++++++++++++----- src/routes/activitypub/mod.rs | 5 ++-- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/config.rs b/src/config.rs index aba6a41..5ebd247 100644 --- a/src/config.rs +++ b/src/config.rs @@ -70,6 +70,9 @@ pub struct SecurityConfig { #[serde_inline_default(true)] pub show_reply_ids: bool, + + #[serde(default)] + pub allow_login_refresh: bool, } diff --git a/src/routes/activitypub/auth.rs b/src/routes/activitypub/auth.rs index c1928f0..46081cf 100644 --- a/src/routes/activitypub/auth.rs +++ b/src/routes/activitypub/auth.rs @@ -1,6 +1,6 @@ use axum::{http::StatusCode, extract::State, Json}; use rand::Rng; -use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter}; +use sea_orm::{ActiveValue::{Set, NotSet}, ColumnTrait, Condition, EntityTrait, QueryFilter}; use crate::{errors::UpubError, model, server::{admin::Administrable, Context}}; @@ -18,6 +18,15 @@ pub struct AuthSuccess { expires: chrono::DateTime, } +fn token() -> String { + // TODO should probably use crypto-safe rng + rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(128) + .map(char::from) + .collect() +} + pub async fn login( State(ctx): State, Json(login): Json @@ -32,12 +41,7 @@ pub async fn login( .await? { Some(x) => { - // TODO should probably use crypto-safe rng - let token : String = rand::thread_rng() - .sample_iter(&rand::distributions::Alphanumeric) - .take(128) - .map(char::from) - .collect(); + let token = token(); let expires = chrono::Utc::now() + std::time::Duration::from_secs(3600 * 6); model::session::Entity::insert( model::session::ActiveModel { @@ -58,6 +62,41 @@ pub async fn login( } } +#[derive(Debug, Clone, serde::Deserialize)] +pub struct RefreshForm { + token: String, +} + +pub async fn refresh( + State(ctx): State, + Json(login): Json +) -> crate::Result> { + if !ctx.cfg().security.allow_login_refresh { + return Err(UpubError::forbidden()); + } + + let prev = model::session::Entity::find() + .filter(model::session::Column::Secret.eq(login.token)) + .one(ctx.db()) + .await? + .ok_or_else(UpubError::unauthorized)?; + + let token = token(); + let expires = chrono::Utc::now() + std::time::Duration::from_secs(3600 * 6); + let user = prev.actor; + let new_session = model::session::ActiveModel { + internal: NotSet, + actor: Set(user.clone()), + secret: Set(token.clone()), + expires: Set(expires), + }; + model::session::Entity::insert(new_session) + .exec(ctx.db()) + .await?; + + Ok(Json(AuthSuccess { token, expires, user })) +} + #[derive(Debug, Clone, serde::Deserialize)] pub struct RegisterForm { username: String, diff --git a/src/routes/activitypub/mod.rs b/src/routes/activitypub/mod.rs index 743b84c..035e0dc 100644 --- a/src/routes/activitypub/mod.rs +++ b/src/routes/activitypub/mod.rs @@ -11,7 +11,7 @@ pub mod well_known; pub mod jsonld; pub use jsonld::JsonLD; -use axum::{http::StatusCode, response::IntoResponse, routing::{get, post, put}, Router}; +use axum::{http::StatusCode, response::IntoResponse, routing::{get, patch, post, put}, Router}; pub trait ActivityPubRouter { fn ap_routes(self) -> Self; @@ -35,8 +35,9 @@ impl ActivityPubRouter for Router { .route("/outbox", get(ap::outbox::get)) .route("/outbox/page", get(ap::outbox::page)) // AUTH routes - .route("/auth", post(ap::auth::login)) .route("/auth", put(ap::auth::register)) + .route("/auth", post(ap::auth::login)) + .route("/auth", patch(ap::auth::refresh)) // .well-known and discovery .route("/.well-known/webfinger", get(ap::well_known::webfinger)) .route("/.well-known/host-meta", get(ap::well_known::host_meta)) From 318fa4f670016274a4a9a89e4aad031050c995e3 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 07:31:34 +0200 Subject: [PATCH 25/90] fix: refresh session only if necessary --- src/routes/activitypub/auth.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/routes/activitypub/auth.rs b/src/routes/activitypub/auth.rs index 46081cf..76dd5a8 100644 --- a/src/routes/activitypub/auth.rs +++ b/src/routes/activitypub/auth.rs @@ -81,6 +81,10 @@ pub async fn refresh( .await? .ok_or_else(UpubError::unauthorized)?; + if prev.expires > chrono::Utc::now() { + return Ok(Json(AuthSuccess { token: prev.secret, user: prev.actor, expires: prev.expires })); + } + let token = token(); let expires = chrono::Utc::now() + std::time::Duration::from_secs(3600 * 6); let user = prev.actor; From dfbac324f56694b275a925a0c3d1cf8b9b954356 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 07:36:38 +0200 Subject: [PATCH 26/90] feat(web): try to refresh credentials login upon start --- web/src/app.rs | 32 +++++++++++++++++++++++++------- web/src/components/login.rs | 8 ++++---- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/web/src/app.rs b/web/src/app.rs index 920262b..b07ddc4 100644 --- a/web/src/app.rs +++ b/web/src/app.rs @@ -1,19 +1,37 @@ use leptos::*; use leptos_router::*; +use reqwest::Method; use crate::prelude::*; -use leptos_use::{storage::use_local_storage, use_cookie, use_cookie_with_options, utils::{FromToStringCodec, JsonCodec}, UseCookieOptions}; +use leptos_use::{storage::use_local_storage, use_cookie, utils::{FromToStringCodec, JsonCodec}}; #[component] pub fn App() -> impl IntoView { - let (token, set_token) = use_cookie_with_options::( - "token", - UseCookieOptions::default() - .max_age(1000 * 60 * 60 * 6) - ); - let (config, set_config, _) = use_local_storage::("config"); + let (token, set_token) = use_cookie::("token"); let (userid, set_userid) = use_cookie::("user_id"); + let (config, set_config, _) = use_local_storage::("config"); + + spawn_local(async move { + match reqwest::Client::new() + .request(Method::PATCH, format!("{URL_BASE}/auth")) + .json(&serde_json::json!({"token": token.get().unwrap_or_default()})) + .send() + .await + { + Err(e) => tracing::error!("could not refresh token: {e}"), + Ok(res) => match res.error_for_status() { + Err(e) => tracing::error!("server rejected refresh: {e}"), + Ok(doc) => match doc.json::().await { + Err(e) => tracing::error!("failed parsing auth response: {e}"), + Ok(auth) => { + set_token.set(Some(auth.token)); + set_userid.set(Some(auth.user)); + }, + } + } + } + }); let auth = Auth { token, userid }; provide_context(auth); diff --git a/web/src/components/login.rs b/web/src/components/login.rs index 88a84ae..4d17cde 100644 --- a/web/src/components/login.rs +++ b/web/src/components/login.rs @@ -91,8 +91,8 @@ struct LoginForm { #[derive(Debug, Clone, serde::Deserialize)] -struct AuthResponse { - token: String, - user: String, - expires: chrono::DateTime, +pub struct AuthResponse { + pub token: String, + pub user: String, + pub expires: chrono::DateTime, } From 399fdecee7885889147fcf4ac46e8329cfb40271 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 07:37:46 +0200 Subject: [PATCH 27/90] fix(web): only refresh if logged in --- web/src/app.rs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/web/src/app.rs b/web/src/app.rs index b07ddc4..8e51553 100644 --- a/web/src/app.rs +++ b/web/src/app.rs @@ -12,26 +12,28 @@ pub fn App() -> impl IntoView { let (userid, set_userid) = use_cookie::("user_id"); let (config, set_config, _) = use_local_storage::("config"); - spawn_local(async move { - match reqwest::Client::new() - .request(Method::PATCH, format!("{URL_BASE}/auth")) - .json(&serde_json::json!({"token": token.get().unwrap_or_default()})) - .send() - .await - { - Err(e) => tracing::error!("could not refresh token: {e}"), - Ok(res) => match res.error_for_status() { - Err(e) => tracing::error!("server rejected refresh: {e}"), - Ok(doc) => match doc.json::().await { - Err(e) => tracing::error!("failed parsing auth response: {e}"), - Ok(auth) => { - set_token.set(Some(auth.token)); - set_userid.set(Some(auth.user)); - }, + if let Some(tok) = token.get_untracked() { + spawn_local(async move { + match reqwest::Client::new() + .request(Method::PATCH, format!("{URL_BASE}/auth")) + .json(&serde_json::json!({"token": tok})) + .send() + .await + { + Err(e) => tracing::error!("could not refresh token: {e}"), + Ok(res) => match res.error_for_status() { + Err(e) => tracing::error!("server rejected refresh: {e}"), + Ok(doc) => match doc.json::().await { + Err(e) => tracing::error!("failed parsing auth response: {e}"), + Ok(auth) => { + set_token.set(Some(auth.token)); + set_userid.set(Some(auth.user)); + }, + } } } - } - }); + }) + }; let auth = Auth { token, userid }; provide_context(auth); From eb3c647691b60a1886e839eb6714b0ef7f057c87 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 07:47:20 +0200 Subject: [PATCH 28/90] fix: set published i thought i could remove this and let db do it but i need it to build a Model before so ehh lets put it backk --- src/server/outbox.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/server/outbox.rs b/src/server/outbox.rs index 3211652..08054e5 100644 --- a/src/server/outbox.rs +++ b/src/server/outbox.rs @@ -45,6 +45,7 @@ impl apb::server::Outbox for Context { .set_id(Some(&oid)) .set_attributed_to(Node::link(uid.clone())) .set_content(content.as_deref()) + .set_published(Some(chrono::Utc::now())) .set_url(Node::maybe_link(self.cfg().instance.frontend.as_ref().map(|x| format!("{x}/objects/{raw_oid}")))), Some(self.domain().to_string()), ).await?; @@ -84,6 +85,7 @@ impl apb::server::Outbox for Context { object .set_id(Some(&oid)) .set_attributed_to(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) .set_to(activity.to()) .set_bto(activity.bto()) .set_cc(activity.cc()) @@ -97,6 +99,7 @@ impl apb::server::Outbox for Context { .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) .set_object(Node::link(oid.clone())) + .set_published(Some(chrono::Utc::now())) )?; model::activity::Entity::insert(activity_model.into_active_model()) @@ -115,6 +118,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; @@ -149,6 +153,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; let follower_internal = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; @@ -194,6 +199,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; model::activity::Entity::insert(activity_model.into_active_model()) .exec(self.db()).await?; @@ -242,6 +248,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; model::activity::Entity::insert(activity_model) .exec(self.db()).await?; @@ -276,6 +283,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; model::activity::Entity::insert(activity_model.into_active_model()) .exec(self.db()) @@ -325,6 +333,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; model::activity::Entity::insert(activity_model) @@ -350,6 +359,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; model::activity::Entity::insert(activity_model) @@ -438,6 +448,7 @@ impl apb::server::Outbox for Context { &activity .set_id(Some(&aid)) .set_actor(Node::link(uid.clone())) + .set_published(Some(chrono::Utc::now())) )?; let share_model = model::announce::ActiveModel { From 6397647511463b86aaeec26b979df9ef82292791 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 27 May 2024 16:50:42 +0200 Subject: [PATCH 29/90] fix(web): only load tl after refresh also improved tl.more() --- web/src/app.rs | 66 +++++++++++++++------------------- web/src/components/login.rs | 18 ++-------- web/src/components/timeline.rs | 42 ++++++++-------------- web/src/page/object.rs | 6 +--- web/src/page/timeline.rs | 6 +--- web/src/page/user.rs | 8 ++--- 6 files changed, 50 insertions(+), 96 deletions(-) diff --git a/web/src/app.rs b/web/src/app.rs index 8e51553..9c0d3c7 100644 --- a/web/src/app.rs +++ b/web/src/app.rs @@ -12,29 +12,6 @@ pub fn App() -> impl IntoView { let (userid, set_userid) = use_cookie::("user_id"); let (config, set_config, _) = use_local_storage::("config"); - if let Some(tok) = token.get_untracked() { - spawn_local(async move { - match reqwest::Client::new() - .request(Method::PATCH, format!("{URL_BASE}/auth")) - .json(&serde_json::json!({"token": tok})) - .send() - .await - { - Err(e) => tracing::error!("could not refresh token: {e}"), - Ok(res) => match res.error_for_status() { - Err(e) => tracing::error!("server rejected refresh: {e}"), - Ok(doc) => match doc.json::().await { - Err(e) => tracing::error!("failed parsing auth response: {e}"), - Ok(auth) => { - set_token.set(Some(auth.token)); - set_userid.set(Some(auth.user)); - }, - } - } - } - }) - }; - let auth = Auth { token, userid }; provide_context(auth); provide_context(config); @@ -55,26 +32,39 @@ pub fn App() -> impl IntoView { let (menu, set_menu) = create_signal(screen_width <= 786); let (advanced, set_advanced) = create_signal(false); - spawn_local(async move { - if let Err(e) = server_tl.more(auth).await { - tracing::error!("error populating timeline: {e}"); - } - }); - let auth_present = auth.token.get_untracked().is_some(); // skip helper to use get_untracked - if auth_present { - spawn_local(async move { - if let Err(e) = home_tl.more(auth).await { - tracing::error!("error populating timeline: {e}"); - } - }); - } + let title_target = move || if auth_present { "/web/home" } else { "/web/server" }; - let title_target = if auth_present { "/web/home" } else { "/web/server" }; + if let Some(tok) = token.get_untracked() { + spawn_local(async move { + // refresh token first, or verify that we're still authed + match reqwest::Client::new() + .request(Method::PATCH, format!("{URL_BASE}/auth")) + .json(&serde_json::json!({"token": tok})) + .send() + .await + { + Err(e) => tracing::error!("could not refresh token: {e}"), + Ok(res) => match res.error_for_status() { + Err(e) => tracing::error!("server rejected refresh: {e}"), + Ok(doc) => match doc.json::().await { + Err(e) => tracing::error!("failed parsing auth response: {e}"), + Ok(auth) => { + set_token.set(Some(auth.token)); + set_userid.set(Some(auth.user)); + }, + } + } + } + + server_tl.more(auth); + if auth_present { home_tl.more(auth) }; + }) + }; view! {