From 9f1e6da4b98d9ffc244f4668a3710d28ff04d132 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 22 Mar 2024 05:34:08 +0100 Subject: [PATCH] feat: add likes and shares, process likes also redid migrations because its easier and also its just me using this cursed thing and i can just delete the db and migrations look nicer this way --- src/activitypub/user.rs | 35 +++++++- .../m20240316_000001_create_table.rs | 14 +++- .../m20240322_000001_create_relations.rs | 4 +- .../m20240322_000002_add_likes_shares.rs | 81 +++++++++++++++++++ src/migrations/mod.rs | 2 + src/model/faker.rs | 3 + src/model/like.rs | 15 ++++ src/model/mod.rs | 3 + src/model/object.rs | 6 ++ src/model/share.rs | 16 ++++ 10 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 src/migrations/m20240322_000002_add_likes_shares.rs create mode 100644 src/model/like.rs create mode 100644 src/model/share.rs diff --git a/src/activitypub/user.rs b/src/activitypub/user.rs index 6d6e1043..5d145ec2 100644 --- a/src/activitypub/user.rs +++ b/src/activitypub/user.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use axum::{extract::{Path, Query, State}, http::StatusCode, Json}; -use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, IntoActiveModel, Order, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, SelectColumns}; +use sea_orm::{sea_query::Expr, ColumnTrait, Condition, DatabaseConnection, EntityTrait, IntoActiveModel, Order, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, SelectColumns}; use crate::{activitystream::{object::{activity::{Activity, ActivityMut, ActivityType}, collection::{page::CollectionPageMut, CollectionMut, CollectionType}, ObjectType}, Base, BaseMut, BaseType, Node}, model::{self, activity, object, user}, server::Context, url}; @@ -173,8 +173,37 @@ pub async fn inbox( None => { Err(StatusCode::BAD_REQUEST) }, Some(BaseType::Link(_x)) => Err(StatusCode::UNPROCESSABLE_ENTITY), // we could but not yet Some(BaseType::Object(ObjectType::Activity(ActivityType::Activity))) => Err(StatusCode::UNPROCESSABLE_ENTITY), // won't ingest useless stuff - Some(BaseType::Object(ObjectType::Activity(ActivityType::Follow))) => { todo!() }, - Some(BaseType::Object(ObjectType::Activity(ActivityType::Like))) => { todo!() }, + Some(BaseType::Object(ObjectType::Activity(ActivityType::Follow))) => { Ok(JsonLD(serde_json::Value::Null)) }, + Some(BaseType::Object(ObjectType::Activity(ActivityType::Like))) => { + let aid = object.actor().id().ok_or(StatusCode::BAD_REQUEST)?.to_string(); + let oid = object.object().id().ok_or(StatusCode::BAD_REQUEST)?.to_string(); + let like = model::like::ActiveModel { + id: sea_orm::ActiveValue::NotSet, + actor: sea_orm::Set(aid.clone()), + likes: sea_orm::Set(oid.clone()), + }; + match model::like::Entity::insert(like).exec(ctx.db()).await { + Err(sea_orm::DbErr::RecordNotInserted) => Err(StatusCode::NOT_MODIFIED), + Err(e) => { + tracing::error!("unexpected error procesing like from {aid} to {oid}: {e}"); + Err(StatusCode::INTERNAL_SERVER_ERROR) + } + Ok(_) => { + match 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())) + .exec(ctx.db()) + .await + { + Err(e) => { + tracing::error!("unexpected error incrementing object {oid} like counter: {e}"); + Err(StatusCode::INTERNAL_SERVER_ERROR) + }, + Ok(_) => Ok(JsonLD(serde_json::Value::Null)), + } + }, + } + }, Some(BaseType::Object(ObjectType::Activity(ActivityType::Create))) => { let Ok(activity_entity) = activity::Model::new(&object) else { return Err(StatusCode::UNPROCESSABLE_ENTITY); diff --git a/src/migrations/m20240316_000001_create_table.rs b/src/migrations/m20240316_000001_create_table.rs index c37b3f17..1078b910 100644 --- a/src/migrations/m20240316_000001_create_table.rs +++ b/src/migrations/m20240316_000001_create_table.rs @@ -10,7 +10,6 @@ impl MigrationTrait for Migration { .create_table( Table::create() .table(Users::Table) - .if_not_exists() .col( ColumnDef::new(Users::Id) .string() @@ -33,6 +32,7 @@ impl MigrationTrait for Migration { .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()) + .index(Index::create().col(Users::Domain)) .to_owned() ) .await?; @@ -41,7 +41,6 @@ impl MigrationTrait for Migration { .create_table( Table::create() .table(Activities::Table) - .if_not_exists() .col( ColumnDef::new(Activities::Id) .string() @@ -57,6 +56,9 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Activities::Cc).json().null()) .col(ColumnDef::new(Activities::Bcc).json().null()) .col(ColumnDef::new(Activities::Published).date_time().not_null()) + .index(Index::create().col(Activities::Published)) + .index(Index::create().col(Activities::Actor)) + .index(Index::create().col(Activities::Object)) .to_owned() ).await?; @@ -64,7 +66,6 @@ impl MigrationTrait for Migration { .create_table( Table::create() .table(Objects::Table) - .if_not_exists() .col( ColumnDef::new(Objects::Id) .string() @@ -76,12 +77,16 @@ impl MigrationTrait for Migration { .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()) + .index(Index::create().col(Objects::AttributedTo)) .to_owned() ).await?; @@ -151,6 +156,9 @@ enum Objects { Name, Summary, Content, + Likes, + Shares, + Comments, Context, Cc, Bcc, diff --git a/src/migrations/m20240322_000001_create_relations.rs b/src/migrations/m20240322_000001_create_relations.rs index 6eea2142..5443363e 100644 --- a/src/migrations/m20240322_000001_create_relations.rs +++ b/src/migrations/m20240322_000001_create_relations.rs @@ -10,7 +10,6 @@ impl MigrationTrait for Migration { .create_table( Table::create() .table(Relations::Table) - .if_not_exists() .col( ColumnDef::new(Relations::Id) .integer() @@ -20,6 +19,9 @@ impl MigrationTrait for Migration { ) .col(ColumnDef::new(Relations::Follower).string().not_null()) .col(ColumnDef::new(Relations::Following).string().not_null()) + .index(Index::create().col(Relations::Follower).col(Relations::Following).unique()) + .index(Index::create().col(Relations::Follower)) + .index(Index::create().col(Relations::Following)) .to_owned() ) .await?; diff --git a/src/migrations/m20240322_000002_add_likes_shares.rs b/src/migrations/m20240322_000002_add_likes_shares.rs new file mode 100644 index 00000000..3e775f46 --- /dev/null +++ b/src/migrations/m20240322_000002_add_likes_shares.rs @@ -0,0 +1,81 @@ +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()) + .index(Index::create().col(Likes::Actor).col(Likes::Likes).unique()) + .index(Index::create().col(Likes::Actor)) + .index(Index::create().col(Likes::Likes)) + .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()) + .index(Index::create().col(Shares::Actor)) + .index(Index::create().col(Shares::Shares)) + .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)] +enum Likes { + Table, + Id, + Actor, + Likes, +} + +#[derive(DeriveIden)] +enum Shares { + Table, + Id, + Actor, + Shares, + Date, +} diff --git a/src/migrations/mod.rs b/src/migrations/mod.rs index ceba0d8d..7c8fac8a 100644 --- a/src/migrations/mod.rs +++ b/src/migrations/mod.rs @@ -2,6 +2,7 @@ use sea_orm_migration::prelude::*; mod m20240316_000001_create_table; mod m20240322_000001_create_relations; +mod m20240322_000002_add_likes_shares; pub struct Migrator; @@ -11,6 +12,7 @@ impl MigratorTrait for Migrator { vec![ Box::new(m20240316_000001_create_table::Migration), Box::new(m20240322_000001_create_relations::Migration), + Box::new(m20240322_000002_add_likes_shares::Migration), ] } } diff --git a/src/model/faker.rs b/src/model/faker.rs index f63e4213..a533a356 100644 --- a/src/model/faker.rs +++ b/src/model/faker.rs @@ -50,6 +50,9 @@ UQIDAQAB context: Set(Some(context.clone())), 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)), + comments: Set(0), + likes: Set(0), + shares: Set(0), to: Set(Audience(vec![PUBLIC_TARGET.to_string()])), bto: Set(Audience::default()), cc: Set(Audience(vec![root.followers().id().unwrap_or("").to_string()])), diff --git a/src/model/like.rs b/src/model/like.rs new file mode 100644 index 00000000..6b35512b --- /dev/null +++ b/src/model/like.rs @@ -0,0 +1,15 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "likes")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + pub actor: String, + pub likes: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/model/mod.rs b/src/model/mod.rs index ceef3316..283c5ae3 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -1,7 +1,10 @@ pub mod user; pub mod object; pub mod activity; + pub mod relation; +pub mod share; +pub mod like; pub mod faker; diff --git a/src/model/object.rs b/src/model/object.rs index da169755..067e51c8 100644 --- a/src/model/object.rs +++ b/src/model/object.rs @@ -15,6 +15,9 @@ pub struct Model { pub name: Option, pub summary: Option, pub content: Option, + pub likes: u64, + pub shares: u64, + pub comments: u64, pub context: Option, pub cc: Audience, pub bcc: Audience, @@ -133,6 +136,9 @@ impl Model { content: object.content().map(|x| x.to_string()), context: object.context().id().map(|x| x.to_string()), published: object.published().ok_or(super::FieldError("published"))?, + comments: 0, + likes: 0, + shares: 0, to: object.to().into(), bto: object.bto().into(), cc: object.cc().into(), diff --git a/src/model/share.rs b/src/model/share.rs new file mode 100644 index 00000000..797a981e --- /dev/null +++ b/src/model/share.rs @@ -0,0 +1,16 @@ +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "shares")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + pub actor: String, + pub shares: String, + pub date: ChronoDateTime, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {}