feat: likes and shares collections on objects

This commit is contained in:
əlemi 2025-01-15 01:09:11 +01:00
parent 4be310cab1
commit 3581dc54e1
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 133 additions and 2 deletions

View file

@ -162,6 +162,7 @@ impl Entity {
impl crate::ext::IntoActivityPub for Model {
fn into_activity_pub_json(self, ctx: &crate::Context) -> serde_json::Value {
let is_local = ctx.is_local(&self.id);
apb::new()
.set_object_type(Some(self.object_type))
.set_attributed_to(apb::Node::maybe_link(self.attributed_to))
@ -188,18 +189,22 @@ impl crate::ext::IntoActivityPub for Model {
.set_sensitive(Some(self.sensitive))
.set_shares(apb::Node::object(
apb::new()
.set_id(if is_local { Some(format!("{}/shares", self.id)) } else { None })
.set_first(if is_local { apb::Node::link(format!("{}/shares/page", self.id)) } else { apb::Node::Empty })
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
.set_total_items(Some(self.announces as u64))
))
.set_likes(apb::Node::object(
apb::new()
.set_id(if is_local { Some(format!("{}/likes", self.id)) } else { None })
.set_first(if is_local { apb::Node::link(format!("{}/likes/page", self.id)) } else { apb::Node::Empty })
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
.set_total_items(Some(self.likes as u64))
))
.set_replies(apb::Node::object(
apb::new()
.set_id(if ctx.is_local(&self.id) { Some(format!("{}/replies", self.id)) } else { None })
.set_first( if ctx.is_local(&self.id) { apb::Node::link(format!("{}/replies/page", self.id)) } else { apb::Node::Empty })
.set_id(if is_local { Some(format!("{}/replies", self.id)) } else { None })
.set_first( if is_local { apb::Node::link(format!("{}/replies/page", self.id)) } else { apb::Node::Empty })
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
.set_total_items(Some(self.replies as u64))
))

View file

@ -72,6 +72,10 @@ impl ActivityPubRouter for Router<upub::Context> {
.route("/objects/{id}/replies/page", get(ap::object::replies::page))
.route("/objects/{id}/context", get(ap::object::context::get))
.route("/objects/{id}/context/page", get(ap::object::context::page))
.route("/objects/{id}/likes", get(ap::object::likes::get))
.route("/objects/{id}/likes/page", get(ap::object::likes::page))
.route("/objects/{id}/shares", get(ap::object::shares::get))
.route("/objects/{id}/shares/page", get(ap::object::shares::page))
// file routes
.route("/file", post(ap::file::upload))
.route("/file/{id}", get(ap::file::download))

View file

@ -0,0 +1,60 @@
use apb::{BaseMut, CollectionMut, LD};
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, RelationTrait};
use upub::{selector::{RichActivity, RichFillable}, Context};
use crate::{activitypub::Pagination, builders::JsonLD, AuthIdentity};
pub async fn get(
State(ctx): State<Context>,
Path(id): Path<String>,
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
let oid = ctx.oid(&id);
let object = upub::model::object::Entity::find_by_ap_id(&oid)
.one(ctx.db())
.await?
.ok_or_else(crate::ApiError::not_found)?;
Ok(JsonLD(
apb::new()
.set_id(Some(upub::url!(ctx, "/objects/{id}/likes")))
.set_collection_type(Some(apb::CollectionType::Collection))
.set_total_items(Some(object.likes as u64))
.set_first(apb::Node::link(upub::url!(ctx, "/objects/{id}/likes/page")))
.ld_context()
))
}
pub async fn page(
State(ctx): State<Context>,
Path(id): Path<String>,
Query(page): Query<Pagination>,
AuthIdentity(auth): AuthIdentity,
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
let oid = ctx.oid(&id);
let internal = upub::model::object::Entity::ap_to_internal(&oid, ctx.db())
.await?
.ok_or_else(crate::ApiError::not_found)?;
let (limit, offset) = page.pagination();
let items = upub::model::like::Entity::find()
.distinct()
.join(sea_orm::JoinType::InnerJoin, upub::model::like::Relation::Activities.def())
.join(sea_orm::JoinType::InnerJoin, upub::model::activity::Relation::Addressing.def())
.filter(auth.filter_activities())
.filter(upub::model::like::Column::Object.eq(internal))
.order_by_desc(upub::model::like::Column::Published)
.limit(limit)
.offset(offset)
.into_model::<RichActivity>()
.all(ctx.db())
.await?
.load_batched_models(ctx.db())
.await?
.into_iter()
.map(|item| ctx.ap(item))
.collect();
crate::builders::collection_page(&upub::url!(ctx, "/objects/{id}/likes/page"), page, apb::Node::array(items))
}

View file

@ -1,5 +1,7 @@
pub mod replies;
pub mod context;
pub mod likes;
pub mod shares;
use apb::LD;
use axum::extract::{Path, Query, State};

View file

@ -0,0 +1,60 @@
use apb::{BaseMut, CollectionMut, LD};
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, RelationTrait};
use upub::{selector::{RichActivity, RichFillable}, Context};
use crate::{activitypub::Pagination, builders::JsonLD, AuthIdentity};
pub async fn get(
State(ctx): State<Context>,
Path(id): Path<String>,
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
let oid = ctx.oid(&id);
let object = upub::model::object::Entity::find_by_ap_id(&oid)
.one(ctx.db())
.await?
.ok_or_else(crate::ApiError::not_found)?;
Ok(JsonLD(
apb::new()
.set_id(Some(upub::url!(ctx, "/objects/{id}/shares")))
.set_collection_type(Some(apb::CollectionType::Collection))
.set_total_items(Some(object.announces as u64))
.set_first(apb::Node::link(upub::url!(ctx, "/objects/{id}/shares/page")))
.ld_context()
))
}
pub async fn page(
State(ctx): State<Context>,
Path(id): Path<String>,
Query(page): Query<Pagination>,
AuthIdentity(auth): AuthIdentity,
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
let oid = ctx.oid(&id);
let internal = upub::model::object::Entity::ap_to_internal(&oid, ctx.db())
.await?
.ok_or_else(crate::ApiError::not_found)?;
let (limit, offset) = page.pagination();
let items = upub::model::announce::Entity::find()
.distinct()
.join(sea_orm::JoinType::InnerJoin, upub::model::announce::Relation::Activities.def())
.join(sea_orm::JoinType::InnerJoin, upub::model::activity::Relation::Addressing.def())
.filter(auth.filter_activities())
.filter(upub::model::announce::Column::Object.eq(internal))
.order_by_desc(upub::model::announce::Column::Published)
.limit(limit)
.offset(offset)
.into_model::<RichActivity>()
.all(ctx.db())
.await?
.load_batched_models(ctx.db())
.await?
.into_iter()
.map(|item| ctx.ap(item))
.collect();
crate::builders::collection_page(&upub::url!(ctx, "/objects/{id}/shares/page"), page, apb::Node::array(items))
}