feat: likes and shares collections on objects
This commit is contained in:
parent
4be310cab1
commit
3581dc54e1
5 changed files with 133 additions and 2 deletions
|
@ -162,6 +162,7 @@ impl Entity {
|
||||||
|
|
||||||
impl crate::ext::IntoActivityPub for Model {
|
impl crate::ext::IntoActivityPub for Model {
|
||||||
fn into_activity_pub_json(self, ctx: &crate::Context) -> serde_json::Value {
|
fn into_activity_pub_json(self, ctx: &crate::Context) -> serde_json::Value {
|
||||||
|
let is_local = ctx.is_local(&self.id);
|
||||||
apb::new()
|
apb::new()
|
||||||
.set_object_type(Some(self.object_type))
|
.set_object_type(Some(self.object_type))
|
||||||
.set_attributed_to(apb::Node::maybe_link(self.attributed_to))
|
.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_sensitive(Some(self.sensitive))
|
||||||
.set_shares(apb::Node::object(
|
.set_shares(apb::Node::object(
|
||||||
apb::new()
|
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_collection_type(Some(apb::CollectionType::OrderedCollection))
|
||||||
.set_total_items(Some(self.announces as u64))
|
.set_total_items(Some(self.announces as u64))
|
||||||
))
|
))
|
||||||
.set_likes(apb::Node::object(
|
.set_likes(apb::Node::object(
|
||||||
apb::new()
|
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_collection_type(Some(apb::CollectionType::OrderedCollection))
|
||||||
.set_total_items(Some(self.likes as u64))
|
.set_total_items(Some(self.likes as u64))
|
||||||
))
|
))
|
||||||
.set_replies(apb::Node::object(
|
.set_replies(apb::Node::object(
|
||||||
apb::new()
|
apb::new()
|
||||||
.set_id(if ctx.is_local(&self.id) { Some(format!("{}/replies", self.id)) } else { None })
|
.set_id(if is_local { 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_first( if is_local { apb::Node::link(format!("{}/replies/page", self.id)) } else { apb::Node::Empty })
|
||||||
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
||||||
.set_total_items(Some(self.replies as u64))
|
.set_total_items(Some(self.replies as u64))
|
||||||
))
|
))
|
||||||
|
|
|
@ -72,6 +72,10 @@ impl ActivityPubRouter for Router<upub::Context> {
|
||||||
.route("/objects/{id}/replies/page", get(ap::object::replies::page))
|
.route("/objects/{id}/replies/page", get(ap::object::replies::page))
|
||||||
.route("/objects/{id}/context", get(ap::object::context::get))
|
.route("/objects/{id}/context", get(ap::object::context::get))
|
||||||
.route("/objects/{id}/context/page", get(ap::object::context::page))
|
.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
|
// file routes
|
||||||
.route("/file", post(ap::file::upload))
|
.route("/file", post(ap::file::upload))
|
||||||
.route("/file/{id}", get(ap::file::download))
|
.route("/file/{id}", get(ap::file::download))
|
||||||
|
|
60
upub/routes/src/activitypub/object/likes.rs
Normal file
60
upub/routes/src/activitypub/object/likes.rs
Normal 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))
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
pub mod replies;
|
pub mod replies;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
pub mod likes;
|
||||||
|
pub mod shares;
|
||||||
|
|
||||||
use apb::LD;
|
use apb::LD;
|
||||||
use axum::extract::{Path, Query, State};
|
use axum::extract::{Path, Query, State};
|
||||||
|
|
60
upub/routes/src/activitypub/object/shares.rs
Normal file
60
upub/routes/src/activitypub/object/shares.rs
Normal 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))
|
||||||
|
}
|
Loading…
Reference in a new issue