fix(routes): proper replies collection

also don't include replies in first object fetch: needless work since
most of the AP world doesn't use them. if you do, explicitly dereference
/replies!
This commit is contained in:
əlemi 2024-12-26 16:13:58 +01:00
parent cc090ccb19
commit 9f8b6b8280
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 61 additions and 53 deletions

View file

@ -1,9 +1,9 @@
pub mod replies;
pub mod context;
use apb::{BaseMut, CollectionMut, ObjectMut, LD};
use apb::{BaseMut, Object, ObjectMut, LD};
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, QueryFilter, QuerySelect, SelectColumns, TransactionTrait};
use sea_orm::{ColumnTrait, QueryFilter, TransactionTrait};
use upub::{model, selector::{BatchFillable, RichActivity}, traits::Fetcher, Context};
use crate::{builders::JsonLD, AuthIdentity};
@ -27,7 +27,9 @@ pub async fn view(
}
}
let item = upub::Query::objects(auth.my_id())
let replies_url = upub::url!(ctx, "/objects/{id}/replies");
let item_model = upub::Query::objects(auth.my_id())
.filter(auth.filter_objects())
.filter(model::object::Column::Id.eq(&oid))
.into_model::<RichActivity>()
@ -41,31 +43,17 @@ pub async fn view(
.with_batched::<upub::model::hashtag::Entity>(ctx.db())
.await?;
let mut replies = apb::Node::Empty;
let mut item = ctx.ap(item_model);
if ctx.cfg().security.show_reply_ids {
let replies_ids = upub::Query::objects(auth.my_id())
.filter(auth.filter_objects())
.filter(model::object::Column::InReplyTo.eq(oid))
.select_only()
.select_column(model::object::Column::Id)
.into_tuple::<String>()
.all(ctx.db())
.await?;
let replies_patched =
apb::Node::object(
item.replies()
.into_inner()
.unwrap()
.set_id(Some(replies_url))
);
replies = apb::Node::object(
apb::new()
.set_id(Some(upub::url!(ctx, "/objects/{id}/replies")))
.set_first(apb::Node::link(upub::url!(ctx, "/objects/{id}/replies/page")))
.set_collection_type(Some(apb::CollectionType::Collection))
.set_total_items(item.object.as_ref().map(|x| x.replies as u64))
.set_items(apb::Node::links(replies_ids))
);
}
item = item.set_replies(replies_patched);
Ok(JsonLD(
item.object_ap()
.set_replies(replies)
.ld_context()
))
Ok(JsonLD(item.ld_context()))
}

View file

@ -1,9 +1,9 @@
use apb::{BaseMut, CollectionMut, LD};
use apb::{BaseMut, CollectionMut, CollectionPageMut, LD};
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, Condition, QueryFilter, QuerySelect, SelectColumns};
use sea_orm::{ColumnTrait, ConnectionTrait, PaginatorTrait, QueryFilter, QuerySelect, SelectColumns};
use upub::{model, traits::Fetcher, Context};
use crate::{activitypub::{Pagination, TryFetch}, builders::JsonLD, AuthIdentity};
use crate::{activitypub::{Pagination, TryFetch}, builders::JsonLD, ApiResult, AuthIdentity, Identity};
pub async fn get(
State(ctx): State<Context>,
@ -21,22 +21,21 @@ pub async fn get(
ctx.fetch_thread(&oid, ctx.db()).await?;
}
let replies_ids = upub::Query::objects(auth.my_id())
.filter(auth.filter_objects())
.filter(model::object::Column::InReplyTo.eq(ctx.oid(&id)))
.select_only()
.select_column(model::object::Column::Id)
.into_tuple::<String>()
.all(ctx.db())
.await?;
let replies_count = total_replies(&oid, &auth, ctx.db()).await?;
let replies_ids = replies_ids(&oid, &auth, ctx.db(), 20, 0).await?;
let first = apb::new()
.set_id(Some(upub::url!(ctx, "/objects/{id}/replies/page")))
.set_collection_type(Some(apb::CollectionType::OrderedCollectionPage))
.set_next(apb::Node::link(upub::url!(ctx, "/objects/{id}/replies/page?offset=20")))
.set_ordered_items(apb::Node::links(replies_ids));
Ok(JsonLD(
apb::new()
.set_id(Some(upub::url!(ctx, "/objects/{id}/replies")))
.set_collection_type(Some(apb::CollectionType::Collection))
.set_first(apb::Node::link(upub::url!(ctx, "/objects/{id}/replies/page")))
.set_total_items(Some(replies_ids.len() as u64))
.set_items(apb::Node::links(replies_ids))
.set_total_items(Some(replies_count))
.set_first(apb::Node::object(first))
.ld_context()
))
}
@ -49,19 +48,40 @@ pub async fn page(
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
let page_id = upub::url!(ctx, "/objects/{id}/replies/page");
let oid = ctx.oid(&id);
let (limit, offset) = page.pagination();
// TODO kinda weird ignoring this but its weirder to exclude replies from replies view...
page.replies = Some(true);
crate::builders::paginate_feed(
page_id,
Condition::all()
.add(auth.filter_activities())
.add(model::object::Column::InReplyTo.eq(oid)),
ctx.db(),
page,
auth.my_id(),
false,
let replies_ids = replies_ids(&oid, &auth, ctx.db(), limit, offset).await?;
crate::builders::collection_page(
&page_id,
offset,
limit,
apb::Node::links(replies_ids)
)
.await
}
async fn replies_ids(oid: &str, auth: &Identity, db: &impl ConnectionTrait, limit: u64, offset: u64) -> ApiResult<Vec<String>> {
let res = upub::Query::objects(auth.my_id())
.limit(limit)
.offset(offset)
.filter(auth.filter_objects())
.filter(model::object::Column::InReplyTo.eq(oid))
.select_only()
.select_column(model::object::Column::Id)
.into_tuple::<String>()
.all(db)
.await?;
Ok(res)
}
async fn total_replies(oid: &str, auth: &Identity, db: &impl ConnectionTrait) -> ApiResult<u64> {
let count = upub::Query::objects(None)
.filter(auth.filter_objects())
.filter(model::object::Column::InReplyTo.eq(oid))
.count(db)
.await?;
Ok(count)
}