From 45d16fa0a3018e4ec9b298c7038046bc5f332ebe Mon Sep 17 00:00:00 2001 From: alemi Date: Tue, 30 Apr 2024 01:50:25 +0200 Subject: [PATCH] feat: query and show objects liked by you it shows it in quite a jank way: inside the "audience" collections you find your id as only item. it's weird af but technically valid ap i think? will probably be replaced with a local api extension as soon as i read about those --- src/model/addressing.rs | 60 +++++++++++++++++++----- src/model/attachment.rs | 4 +- src/routes/activitypub/activity.rs | 2 +- src/routes/activitypub/context.rs | 5 +- src/routes/activitypub/inbox.rs | 3 +- src/routes/activitypub/object/mod.rs | 8 ++-- src/routes/activitypub/object/replies.rs | 5 +- src/routes/activitypub/outbox.rs | 1 + src/routes/activitypub/user/inbox.rs | 1 + src/routes/activitypub/user/mod.rs | 2 +- src/routes/activitypub/user/outbox.rs | 1 + src/routes/mastodon/accounts.rs | 2 +- src/server/auth.rs | 7 +++ src/server/builders.rs | 3 +- 14 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/model/addressing.rs b/src/model/addressing.rs index 7c9fdf50..39ba82e0 100644 --- a/src/model/addressing.rs +++ b/src/model/addressing.rs @@ -1,4 +1,4 @@ -use apb::{ActivityMut, ObjectMut}; +use apb::{ActivityMut, CollectionMut, ObjectMut}; use sea_orm::{entity::prelude::*, Condition, FromQueryResult, Iterable, Order, QueryOrder, QuerySelect, SelectColumns}; use crate::routes::activitypub::jsonld::LD; @@ -65,11 +65,15 @@ impl ActiveModelBehavior for ActiveModel {} #[derive(Debug, Clone)] pub enum Event { Tombstone, - StrayObject(crate::model::object::Model), Activity(crate::model::activity::Model), + StrayObject { + object: crate::model::object::Model, + liked: Option, + }, DeepActivity { activity: crate::model::activity::Model, object: crate::model::object::Model, + liked: Option, } } @@ -78,9 +82,9 @@ impl Event { pub fn id(&self) -> &str { match self { Event::Tombstone => "", - Event::StrayObject(x) => x.id.as_str(), Event::Activity(x) => x.id.as_str(), - Event::DeepActivity { activity: _, object } => object.id.as_str(), + Event::StrayObject { object, liked: _ } => object.id.as_str(), + Event::DeepActivity { activity: _, liked: _, object } => object.id.as_str(), } } @@ -93,11 +97,37 @@ impl Event { }; match self { Event::Activity(x) => x.ap(), - Event::DeepActivity { activity, object } => - activity.ap().set_object(apb::Node::object(object.ap().set_attachment(attachment))), - Event::StrayObject(x) => serde_json::Value::new_object() + Event::DeepActivity { activity, object, liked } => + activity.ap().set_object(apb::Node::object({ + let likes = object.likes; + let mut obj = object.ap() + .set_attachment(attachment); + if let Some(liked) = liked { + obj = obj.set_audience(apb::Node::object( // TODO setting this again ewww... + serde_json::Value::new_object() + .set_collection_type(Some(apb::CollectionType::OrderedCollection)) + .set_total_items(Some(likes as u64)) + .set_ordered_items(apb::Node::links(vec![liked])) + )); + } + obj + })), + Event::StrayObject { object, liked } => serde_json::Value::new_object() .set_activity_type(Some(apb::ActivityType::Activity)) - .set_object(apb::Node::object(x.ap().set_attachment(attachment))), + .set_object(apb::Node::object({ + let likes = object.likes; + let mut obj = object.ap() + .set_attachment(attachment); + if let Some(liked) = liked { + obj = obj.set_audience(apb::Node::object( // TODO setting this again ewww... + serde_json::Value::new_object() + .set_collection_type(Some(apb::CollectionType::OrderedCollection)) + .set_total_items(Some(likes as u64)) + .set_ordered_items(apb::Node::links(vec![liked])) + )); + } + obj + })), Event::Tombstone => serde_json::Value::new_object() .set_activity_type(Some(apb::ActivityType::Activity)) .set_object(apb::Node::object( @@ -112,10 +142,11 @@ impl FromQueryResult for Event { fn from_query_result(res: &sea_orm::QueryResult, _pre: &str) -> Result { let activity = crate::model::activity::Model::from_query_result(res, crate::model::activity::Entity.table_name()).ok(); let object = crate::model::object::Model::from_query_result(res, crate::model::object::Entity.table_name()).ok(); + let liked = res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok(); match (activity, object) { - (Some(activity), Some(object)) => Ok(Self::DeepActivity { activity, object }), + (Some(activity), Some(object)) => Ok(Self::DeepActivity { activity, object, liked }), (Some(activity), None) => Ok(Self::Activity(activity)), - (None, Some(object)) => Ok(Self::StrayObject(object)), + (None, Some(object)) => Ok(Self::StrayObject { object, liked }), (None, None) => Ok(Self::Tombstone), } } @@ -123,7 +154,7 @@ impl FromQueryResult for Event { impl Entity { - pub fn find_addressed() -> Select { + pub fn find_addressed(uid: Option<&str>) -> Select { let mut select = Entity::find() .distinct() .select_only() @@ -137,6 +168,13 @@ impl Entity { ) .order_by(Column::Published, Order::Desc); + if let Some(uid) = uid { + select = select + .filter(crate::model::like::Column::Actor.eq(uid)) + .join(sea_orm::JoinType::LeftJoin, crate::model::object::Relation::Like.def()) + .select_column_as(crate::model::like::Column::Actor, format!("{}{}", crate::model::like::Entity.table_name(), crate::model::like::Column::Actor.to_string())); + } + for col in crate::model::object::Column::iter() { select = select.select_column_as(col, format!("{}{}", crate::model::object::Entity.table_name(), col.to_string())); } diff --git a/src/model/attachment.rs b/src/model/attachment.rs index f3cf7eb3..aeb6530b 100644 --- a/src/model/attachment.rs +++ b/src/model/attachment.rs @@ -76,8 +76,8 @@ impl BatchFillable for &[Event] { .filter_map(|x| match x { Event::Tombstone => None, Event::Activity(_) => None, - Event::StrayObject(x) => Some(x.clone()), - Event::DeepActivity { activity: _, object } => Some(object.clone()), + Event::StrayObject { object, liked: _ } => Some(object.clone()), + Event::DeepActivity { activity: _, liked: _, object } => Some(object.clone()), }) .collect(); diff --git a/src/routes/activitypub/activity.rs b/src/routes/activitypub/activity.rs index 3b6358f8..fedfb1bf 100644 --- a/src/routes/activitypub/activity.rs +++ b/src/routes/activitypub/activity.rs @@ -18,7 +18,7 @@ pub async fn view( } } - let row = model::addressing::Entity::find_addressed() + let row = model::addressing::Entity::find_addressed(auth.my_id()) .filter(model::activity::Column::Id.eq(&aid)) .filter(auth.filter_condition()) .into_model::() diff --git a/src/routes/activitypub/context.rs b/src/routes/activitypub/context.rs index 85153c96..c24c83b4 100644 --- a/src/routes/activitypub/context.rs +++ b/src/routes/activitypub/context.rs @@ -11,7 +11,7 @@ pub async fn get( let local_context_id = url!(ctx, "/context/{id}"); let context = ctx.uri("context", id); - let count = model::addressing::Entity::find_addressed() + let count = model::addressing::Entity::find_addressed(auth.my_id()) .filter(auth.filter_condition()) .filter(model::object::Column::Context.eq(context)) .count(ctx.db()) @@ -40,7 +40,8 @@ pub async fn page( .add(auth.filter_condition()) .add(model::object::Column::Context.eq(context)), ctx.db(), - page + page, + auth.my_id(), ) .await } diff --git a/src/routes/activitypub/inbox.rs b/src/routes/activitypub/inbox.rs index 5f0aca52..fffb0ad5 100644 --- a/src/routes/activitypub/inbox.rs +++ b/src/routes/activitypub/inbox.rs @@ -21,7 +21,8 @@ pub async fn page( url!(ctx, "/inbox/page"), auth.filter_condition(), ctx.db(), - page + page, + auth.my_id(), ) .await } diff --git a/src/routes/activitypub/object/mod.rs b/src/routes/activitypub/object/mod.rs index 2a8406f6..683116fc 100644 --- a/src/routes/activitypub/object/mod.rs +++ b/src/routes/activitypub/object/mod.rs @@ -4,7 +4,7 @@ use apb::ObjectMut; use axum::extract::{Path, Query, State}; use sea_orm::{ColumnTrait, ModelTrait, QueryFilter}; -use crate::{errors::UpubError, model::{self, addressing::Event}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}}; +use crate::{errors::UpubError, model::{self, addressing::Event}, server::{auth::{AuthIdentity, Identity}, fetcher::Fetcher, Context}}; use super::{jsonld::LD, JsonLD, TryFetch}; @@ -27,7 +27,7 @@ pub async fn view( } } - let item = model::addressing::Entity::find_addressed() + let item = model::addressing::Entity::find_addressed(auth.my_id()) .filter(model::object::Column::Id.eq(&oid)) .filter(auth.filter_condition()) .into_model::() @@ -38,8 +38,8 @@ pub async fn view( let object = match item { Event::Tombstone => return Err(UpubError::not_found()), Event::Activity(_) => return Err(UpubError::not_found()), - Event::StrayObject(x) => x, - Event::DeepActivity { activity: _, object } => object, + Event::StrayObject { object, liked: _ } => object, + Event::DeepActivity { activity: _, liked: _, object } => object, }; let attachments = object.find_related(model::attachment::Entity) diff --git a/src/routes/activitypub/object/replies.rs b/src/routes/activitypub/object/replies.rs index ee484bd4..8f69b9ba 100644 --- a/src/routes/activitypub/object/replies.rs +++ b/src/routes/activitypub/object/replies.rs @@ -11,7 +11,7 @@ pub async fn get( let replies_id = url!(ctx, "/objects/{id}/replies"); let oid = ctx.uri("objects", id); - let count = model::addressing::Entity::find_addressed() + let count = model::addressing::Entity::find_addressed(auth.my_id()) .filter(auth.filter_condition()) .filter(model::object::Column::InReplyTo.eq(oid)) .count(ctx.db()) @@ -35,7 +35,8 @@ pub async fn page( .add(auth.filter_condition()) .add(model::object::Column::InReplyTo.eq(oid)), ctx.db(), - page + page, + auth.my_id(), ) .await } diff --git a/src/routes/activitypub/outbox.rs b/src/routes/activitypub/outbox.rs index 8bd3225c..1cd7e639 100644 --- a/src/routes/activitypub/outbox.rs +++ b/src/routes/activitypub/outbox.rs @@ -16,6 +16,7 @@ pub async fn page( auth.filter_condition(), // TODO filter local only stuff ctx.db(), page, + auth.my_id(), ) .await } diff --git a/src/routes/activitypub/user/inbox.rs b/src/routes/activitypub/user/inbox.rs index 418a225d..628ac362 100644 --- a/src/routes/activitypub/user/inbox.rs +++ b/src/routes/activitypub/user/inbox.rs @@ -41,6 +41,7 @@ pub async fn page( .add(model::activity::Column::Actor.eq(uid)), ctx.db(), page, + auth.my_id(), ) .await } diff --git a/src/routes/activitypub/user/mod.rs b/src/routes/activitypub/user/mod.rs index c24f7e2d..ca1af867 100644 --- a/src/routes/activitypub/user/mod.rs +++ b/src/routes/activitypub/user/mod.rs @@ -7,7 +7,7 @@ pub mod following; use axum::extract::{Path, Query, State}; use sea_orm::EntityTrait; -use apb::{ActorMut, BaseMut, CollectionMut, Node, ObjectMut}; +use apb::{ActorMut, Node, ObjectMut}; use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url}; use super::{jsonld::LD, JsonLD, TryFetch}; diff --git a/src/routes/activitypub/user/outbox.rs b/src/routes/activitypub/user/outbox.rs index c993df1c..decaa1e8 100644 --- a/src/routes/activitypub/user/outbox.rs +++ b/src/routes/activitypub/user/outbox.rs @@ -33,6 +33,7 @@ pub async fn page( ), ctx.db(), page, + auth.my_id(), ) .await } diff --git a/src/routes/mastodon/accounts.rs b/src/routes/mastodon/accounts.rs index 913db72b..f826dc7a 100644 --- a/src/routes/mastodon/accounts.rs +++ b/src/routes/mastodon/accounts.rs @@ -70,7 +70,7 @@ pub async fn statuses( Query(_query): Query, ) -> Result>, StatusCode> { let uid = ctx.uid(id); - model::addressing::Entity::find_addressed() + model::addressing::Entity::find_addressed(auth.my_id()) .filter(model::activity::Column::Actor.eq(uid)) .filter(auth.filter_condition()); diff --git a/src/server/auth.rs b/src/server/auth.rs index 38b54772..8c124fbf 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -31,6 +31,13 @@ impl Identity { } } + pub fn my_id(&self) -> Option<&str> { + match self { + Identity::Local(x) => Some(x.as_str()), + _ => None, + } + } + pub fn is_anon(&self) -> bool { matches!(self, Self::Anonymous) } diff --git a/src/server/builders.rs b/src/server/builders.rs index 7eac7c96..7664d636 100644 --- a/src/server/builders.rs +++ b/src/server/builders.rs @@ -8,11 +8,12 @@ pub async fn paginate( filter: Condition, db: &DatabaseConnection, page: Pagination, + my_id: Option<&str>, ) -> crate::Result> { let limit = page.batch.unwrap_or(20).min(50); let offset = page.offset.unwrap_or(0); - let items = crate::model::addressing::Entity::find_addressed() + let items = crate::model::addressing::Entity::find_addressed(my_id) .filter(filter) // TODO also limit to only local activities .limit(limit)