feat: removed object timelines
it's back to original concept babyyyy activities all the way down
This commit is contained in:
parent
3698d1947d
commit
727e977c4e
19 changed files with 72 additions and 371 deletions
|
@ -1,7 +1,7 @@
|
|||
use std::collections::{hash_map::Entry, HashMap};
|
||||
|
||||
use sea_orm::{ConnectionTrait, DbErr, EntityTrait, FromQueryResult, ModelTrait, QueryFilter};
|
||||
use super::{RichActivity, RichObject};
|
||||
use super::RichActivity;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait BatchFillable: Sized {
|
||||
|
@ -9,8 +9,7 @@ pub trait BatchFillable: Sized {
|
|||
where
|
||||
E: BatchFillableComparison + EntityTrait,
|
||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
||||
RichActivity: BatchFillableAcceptor<Vec<E::Model>>,
|
||||
RichObject: BatchFillableAcceptor<Vec<E::Model>>;
|
||||
RichActivity: BatchFillableAcceptor<Vec<E::Model>>;
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,7 +21,6 @@ impl BatchFillable for Vec<RichActivity> {
|
|||
E: BatchFillableComparison + EntityTrait,
|
||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
||||
RichActivity: BatchFillableAcceptor<Vec<E::Model>>,
|
||||
RichObject: BatchFillableAcceptor<Vec<E::Model>>
|
||||
{
|
||||
let ids : Vec<i64> = self.iter().filter_map(|x| Some(x.object.as_ref()?.internal)).collect();
|
||||
let batch = E::find()
|
||||
|
@ -47,37 +45,6 @@ impl BatchFillable for Vec<RichActivity> {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BatchFillable for Vec<RichObject> {
|
||||
// TODO 3 iterations... can we make it in less passes?
|
||||
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
|
||||
where
|
||||
E: BatchFillableComparison + EntityTrait,
|
||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
||||
RichActivity: BatchFillableAcceptor<Vec<E::Model>>,
|
||||
RichObject: BatchFillableAcceptor<Vec<E::Model>>
|
||||
{
|
||||
let ids : Vec<i64> = self.iter().map(|x| x.object.internal).collect();
|
||||
let batch = E::find()
|
||||
.filter(E::comparison(ids))
|
||||
.all(tx)
|
||||
.await?;
|
||||
let mut map : HashMap<i64, Vec<E::Model>> = HashMap::new();
|
||||
for element in batch {
|
||||
match map.entry(element.key()) {
|
||||
Entry::Occupied(mut x) => { x.get_mut().push(element); },
|
||||
Entry::Vacant(x) => { x.insert(vec![element]); },
|
||||
}
|
||||
}
|
||||
for element in self.iter_mut() {
|
||||
if let Some(v) = map.remove(&element.object.internal) {
|
||||
element.accept(v, tx).await?;
|
||||
}
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BatchFillable for RichActivity {
|
||||
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
|
||||
|
@ -85,7 +52,6 @@ impl BatchFillable for RichActivity {
|
|||
E: BatchFillableComparison + EntityTrait,
|
||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
||||
RichActivity: BatchFillableAcceptor<Vec<E::Model>>,
|
||||
RichObject: BatchFillableAcceptor<Vec<E::Model>>
|
||||
{
|
||||
if let Some(ref obj) = self.object {
|
||||
let batch =E::find()
|
||||
|
@ -98,24 +64,6 @@ impl BatchFillable for RichActivity {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BatchFillable for RichObject {
|
||||
async fn with_batched<E>(mut self, tx: &impl ConnectionTrait) -> Result<Self, DbErr>
|
||||
where
|
||||
E: BatchFillableComparison + EntityTrait,
|
||||
E::Model: BatchFillableKey + Send + FromQueryResult + ModelTrait<Entity = E>,
|
||||
RichActivity: BatchFillableAcceptor<Vec<E::Model>>,
|
||||
RichObject: BatchFillableAcceptor<Vec<E::Model>>
|
||||
{
|
||||
let batch = E::find()
|
||||
.filter(E::comparison(vec![self.object.internal]))
|
||||
.all(tx)
|
||||
.await?;
|
||||
self.accept(batch, tx).await?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// welcome to interlocking trait hell, enjoy your stay
|
||||
mod hell {
|
||||
|
@ -207,42 +155,7 @@ use crate::selector::rich::{RichHashtag, RichMention};
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BatchFillableAcceptor<Vec<crate::model::attachment::Model>> for super::RichObject {
|
||||
async fn accept(&mut self, batch: Vec<crate::model::attachment::Model>, _tx: &impl ConnectionTrait) -> Result<(), DbErr> {
|
||||
self.attachments = Some(batch);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BatchFillableAcceptor<Vec<crate::model::hashtag::Model>> for super::RichObject {
|
||||
async fn accept(&mut self, batch: Vec<crate::model::hashtag::Model>, _tx: &impl ConnectionTrait) -> Result<(), DbErr> {
|
||||
self.hashtags = Some(batch.into_iter().map(|x| RichHashtag { hash: x }).collect());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BatchFillableAcceptor<Vec<crate::model::mention::Model>> for super::RichObject {
|
||||
async fn accept(&mut self, batch: Vec<crate::model::mention::Model>, tx: &impl ConnectionTrait) -> Result<(), DbErr> {
|
||||
// TODO batch load users from mentions rather than doing for loop
|
||||
let mut mentions = Vec::new();
|
||||
for row in batch {
|
||||
// TODO filter only needed rows
|
||||
if let Some(user) = crate::model::actor::Entity::find_by_id(row.actor).one(tx).await? {
|
||||
mentions.push(RichMention {
|
||||
mention: row,
|
||||
fqn: format!("@{}@{}", user.preferred_username, user.domain),
|
||||
id: user.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
self.mentions = Some(mentions);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use hell::*;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ mod query;
|
|||
pub use query::Query;
|
||||
|
||||
mod rich;
|
||||
pub use rich::{RichObject, RichActivity};
|
||||
pub use rich::RichActivity;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ use crate::model;
|
|||
pub struct Query;
|
||||
|
||||
impl Query {
|
||||
pub fn activities(my_id: Option<i64>) -> Select<model::addressing::Entity> {
|
||||
pub fn feed(my_id: Option<i64>) -> Select<model::addressing::Entity> {
|
||||
let mut select = model::addressing::Entity::find()
|
||||
.distinct_on([
|
||||
(model::addressing::Entity, model::addressing::Column::Published).into_column_ref(),
|
||||
(model::activity::Entity, model::activity::Column::Internal).into_column_ref(),
|
||||
])
|
||||
.join(sea_orm::JoinType::InnerJoin, model::addressing::Relation::Activities.def())
|
||||
.join(sea_orm::JoinType::LeftJoin, model::addressing::Relation::Activities.def())
|
||||
.join(sea_orm::JoinType::LeftJoin, model::addressing::Relation::Objects.def())
|
||||
.filter(
|
||||
// TODO ghetto double inner join because i want to filter out tombstones
|
||||
|
@ -43,34 +43,6 @@ impl Query {
|
|||
select
|
||||
}
|
||||
|
||||
pub fn objects(my_id: Option<i64>) -> Select<model::addressing::Entity> {
|
||||
let mut select = model::addressing::Entity::find()
|
||||
.distinct_on([
|
||||
(model::addressing::Entity, model::addressing::Column::Published).into_column_ref(),
|
||||
(model::object::Entity, model::object::Column::Internal).into_column_ref(),
|
||||
])
|
||||
.join(sea_orm::JoinType::InnerJoin, model::addressing::Relation::Objects.def())
|
||||
.order_by(model::addressing::Column::Published, Order::Desc)
|
||||
.order_by(model::object::Column::Internal, Order::Desc)
|
||||
.select_only();
|
||||
|
||||
for col in model::object::Column::iter() {
|
||||
select = select.select_column_as(col, format!("{}{}", model::object::Entity.table_name(), col.to_string()));
|
||||
}
|
||||
|
||||
if let Some(uid) = my_id {
|
||||
select = select
|
||||
.join(
|
||||
sea_orm::JoinType::LeftJoin,
|
||||
model::object::Relation::Likes.def()
|
||||
.on_condition(move |_l, _r| model::like::Column::Actor.eq(uid).into_condition()),
|
||||
)
|
||||
.select_column_as(model::like::Column::Actor, format!("{}{}", model::like::Entity.table_name(), model::like::Column::Actor.to_string()));
|
||||
}
|
||||
|
||||
select
|
||||
}
|
||||
|
||||
pub fn related(from: Option<i64>, to: Option<i64>, pending: bool) -> Select<model::relation::Entity> {
|
||||
let mut condition = Condition::all();
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ impl RichHashtag {
|
|||
}
|
||||
|
||||
pub struct RichActivity {
|
||||
pub activity: crate::model::activity::Model,
|
||||
pub activity: Option<crate::model::activity::Model>,
|
||||
pub object: Option<crate::model::object::Model>,
|
||||
pub liked: Option<i64>,
|
||||
pub attachments: Option<Vec<crate::model::attachment::Model>>,
|
||||
|
@ -42,21 +42,27 @@ pub struct RichActivity {
|
|||
impl FromQueryResult for RichActivity {
|
||||
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
|
||||
Ok(RichActivity {
|
||||
activity: crate::model::activity::Model::from_query_result(res, crate::model::activity::Entity.table_name())?,
|
||||
object: crate::model::object::Model::from_query_result(res, crate::model::object::Entity.table_name()).ok(),
|
||||
liked: res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok(),
|
||||
attachments: None, hashtags: None, mentions: None,
|
||||
liked: res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok(),
|
||||
object: crate::model::object::Model::from_query_result(res, crate::model::object::Entity.table_name()).ok(),
|
||||
activity: crate::model::activity::Model::from_query_result(res, crate::model::activity::Entity.table_name()).ok(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO avoid repeating the tags code twice
|
||||
impl RichActivity {
|
||||
pub fn ap(self) -> serde_json::Value {
|
||||
use apb::ObjectMut;
|
||||
let object = match self.object {
|
||||
None => apb::Node::maybe_link(self.activity.object.clone()),
|
||||
Some(o) => {
|
||||
// TODO can we avoid repeating this tags code?
|
||||
match (self.activity, self.object) {
|
||||
(None, None) => serde_json::Value::Null,
|
||||
|
||||
(Some(activity), None) => {
|
||||
let obj = apb::Node::maybe_link(activity.object.clone());
|
||||
activity.ap().set_object(obj)
|
||||
},
|
||||
|
||||
(None, Some(object)) => {
|
||||
let mut tags = Vec::new();
|
||||
if let Some(mentions) = self.mentions {
|
||||
for mention in mentions {
|
||||
|
@ -68,64 +74,44 @@ impl RichActivity {
|
|||
tags.push(hash.ap());
|
||||
}
|
||||
}
|
||||
object.ap()
|
||||
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
|
||||
.set_tag(apb::Node::maybe_array(tags))
|
||||
.set_attachment(match self.attachments {
|
||||
None => apb::Node::Empty,
|
||||
Some(vec) => apb::Node::array(
|
||||
vec.into_iter().map(|x| x.ap()).collect()
|
||||
),
|
||||
})
|
||||
},
|
||||
|
||||
(Some(activity), Some(object)) => {
|
||||
let mut tags = Vec::new();
|
||||
if let Some(mentions) = self.mentions {
|
||||
for mention in mentions {
|
||||
tags.push(mention.ap());
|
||||
}
|
||||
}
|
||||
if let Some(hashtags) = self.hashtags {
|
||||
for hash in hashtags {
|
||||
tags.push(hash.ap());
|
||||
}
|
||||
}
|
||||
activity.ap()
|
||||
.set_object(
|
||||
apb::Node::object(
|
||||
o.ap()
|
||||
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
|
||||
.set_tag(apb::Node::array(tags))
|
||||
.set_attachment(match self.attachments {
|
||||
None => apb::Node::Empty,
|
||||
Some(vec) => apb::Node::array(
|
||||
vec.into_iter().map(|x| x.ap()).collect()
|
||||
),
|
||||
})
|
||||
object.ap()
|
||||
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
|
||||
.set_tag(apb::Node::maybe_array(tags))
|
||||
.set_attachment(match self.attachments {
|
||||
None => apb::Node::Empty,
|
||||
Some(vec) => apb::Node::array(
|
||||
vec.into_iter().map(|x| x.ap()).collect()
|
||||
),
|
||||
})
|
||||
)
|
||||
)
|
||||
},
|
||||
};
|
||||
self.activity.ap().set_object(object)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RichObject {
|
||||
pub object: crate::model::object::Model,
|
||||
pub liked: Option<i64>,
|
||||
pub attachments: Option<Vec<crate::model::attachment::Model>>,
|
||||
pub hashtags: Option<Vec<RichHashtag>>,
|
||||
pub mentions: Option<Vec<RichMention>>,
|
||||
}
|
||||
|
||||
impl FromQueryResult for RichObject {
|
||||
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
|
||||
Ok(RichObject {
|
||||
object: crate::model::object::Model::from_query_result(res, crate::model::object::Entity.table_name())?,
|
||||
liked: res.try_get(crate::model::like::Entity.table_name(), &crate::model::like::Column::Actor.to_string()).ok(),
|
||||
attachments: None, hashtags: None, mentions: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RichObject {
|
||||
pub fn ap(self) -> serde_json::Value {
|
||||
use apb::ObjectMut;
|
||||
// TODO can we avoid repeating this tags code?
|
||||
let mut tags = Vec::new();
|
||||
if let Some(mentions) = self.mentions {
|
||||
for mention in mentions {
|
||||
tags.push(mention.ap());
|
||||
}
|
||||
}
|
||||
if let Some(hashtags) = self.hashtags {
|
||||
for hash in hashtags {
|
||||
tags.push(hash.ap());
|
||||
}
|
||||
}
|
||||
self.object.ap()
|
||||
.set_liked_by_me(if self.liked.is_some() { Some(true) } else { None })
|
||||
.set_tag(apb::Node::array(tags))
|
||||
.set_attachment(match self.attachments {
|
||||
None => apb::Node::Empty,
|
||||
Some(vec) => apb::Node::array(
|
||||
vec.into_iter().map(|x| x.ap()).collect()
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ pub async fn view(
|
|||
}
|
||||
}
|
||||
|
||||
let row = upub::Query::activities(auth.my_id())
|
||||
let row = upub::Query::feed(auth.my_id())
|
||||
.filter(model::activity::Column::Id.eq(&aid))
|
||||
.filter(auth.filter_activities())
|
||||
.into_model::<RichActivity>()
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
use axum::extract::{Path, Query, State};
|
||||
use sea_orm::{sea_query::IntoCondition, ColumnTrait};
|
||||
|
||||
use upub::Context;
|
||||
|
||||
use crate::{activitypub::Pagination, builders::JsonLD, AuthIdentity, Identity};
|
||||
|
||||
pub async fn get(
|
||||
State(ctx): State<Context>,
|
||||
Path(id): Path<String>,
|
||||
AuthIdentity(auth): AuthIdentity,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
match auth {
|
||||
Identity::Anonymous => Err(crate::ApiError::forbidden()),
|
||||
Identity::Remote { .. } => Err(crate::ApiError::forbidden()),
|
||||
Identity::Local { id: user, .. } => if ctx.uid(&id) == user {
|
||||
crate::builders::collection(&upub::url!(ctx, "/actors/{id}/feed"), None)
|
||||
} else {
|
||||
Err(crate::ApiError::forbidden())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn page(
|
||||
State(ctx): State<Context>,
|
||||
Path(id): Path<String>,
|
||||
AuthIdentity(auth): AuthIdentity,
|
||||
Query(page): Query<Pagination>,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
let Identity::Local { id: uid, internal } = &auth else {
|
||||
// local inbox is only for local users
|
||||
return Err(crate::ApiError::forbidden());
|
||||
};
|
||||
if uid != &ctx.uid(&id) {
|
||||
return Err(crate::ApiError::forbidden());
|
||||
}
|
||||
|
||||
crate::builders::paginate_objects(
|
||||
upub::url!(ctx, "/actors/{id}/feed/page"),
|
||||
upub::model::addressing::Column::Actor.eq(*internal).into_condition(),
|
||||
ctx.db(),
|
||||
page,
|
||||
auth.my_id(),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
}
|
|
@ -35,7 +35,7 @@ pub async fn page(
|
|||
return Err(crate::ApiError::forbidden());
|
||||
}
|
||||
|
||||
crate::builders::paginate_activities(
|
||||
crate::builders::paginate_feed(
|
||||
upub::url!(ctx, "/actors/{id}/inbox/page"),
|
||||
upub::model::addressing::Column::Actor.eq(*internal).into_condition(),
|
||||
ctx.db(),
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
pub mod inbox;
|
||||
pub mod outbox;
|
||||
pub mod following;
|
||||
pub mod feed;
|
||||
pub mod streams;
|
||||
|
||||
use axum::extract::{Path, Query, State};
|
||||
|
||||
|
@ -58,7 +56,6 @@ pub async fn view(
|
|||
let mut user = user_model.ap()
|
||||
.set_inbox(Node::link(upub::url!(ctx, "/actors/{id}/inbox")))
|
||||
.set_outbox(Node::link(upub::url!(ctx, "/actors/{id}/outbox")))
|
||||
.set_streams(Node::link(upub::url!(ctx, "/actors/{id}/streams")))
|
||||
.set_following(Node::link(upub::url!(ctx, "/actors/{id}/following")))
|
||||
.set_followers(Node::link(upub::url!(ctx, "/actors/{id}/followers")))
|
||||
.set_following_me(following_me)
|
||||
|
@ -93,8 +90,6 @@ pub async fn view(
|
|||
user_model.ap()
|
||||
.set_following_me(following_me)
|
||||
.set_followed_by_me(followed_by_me)
|
||||
// TODO should we set streams?? we offer this collection but actor is remote
|
||||
.set_streams(Node::link(upub::url!(ctx, "/actors/{id}/streams")))
|
||||
.ld_context()
|
||||
)),
|
||||
None => Err(crate::ApiError::not_found()),
|
||||
|
|
|
@ -19,7 +19,7 @@ pub async fn page(
|
|||
AuthIdentity(auth): AuthIdentity,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
let uid = ctx.uid(&id);
|
||||
crate::builders::paginate_activities(
|
||||
crate::builders::paginate_feed(
|
||||
upub::url!(ctx, "/actors/{id}/outbox/page"),
|
||||
Condition::all()
|
||||
.add(auth.filter_activities())
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
use axum::extract::{Path, Query, State};
|
||||
use sea_orm::{ColumnTrait, Condition};
|
||||
|
||||
use upub::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>> {
|
||||
crate::builders::collection(&upub::url!(ctx, "/actors/{id}/streams"), None)
|
||||
}
|
||||
|
||||
pub async fn page(
|
||||
State(ctx): State<Context>,
|
||||
Path(id): Path<String>,
|
||||
AuthIdentity(auth): AuthIdentity,
|
||||
Query(page): Query<Pagination>,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
crate::builders::paginate_objects(
|
||||
upub::url!(ctx, "/actors/{id}/streams/page"),
|
||||
Condition::all()
|
||||
.add(auth.filter_objects())
|
||||
.add(upub::model::object::Column::AttributedTo.eq(ctx.uid(&id))),
|
||||
ctx.db(),
|
||||
page,
|
||||
auth.my_id(),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
}
|
|
@ -25,10 +25,6 @@ pub async fn view(
|
|||
.set_actor_type(Some(apb::ActorType::Application))
|
||||
.set_name(Some(&ctx.cfg().instance.name))
|
||||
.set_summary(Some(&ctx.cfg().instance.description))
|
||||
.set_streams(apb::Node::links(vec![
|
||||
upub::url!(ctx, "/feed"),
|
||||
upub::url!(ctx, "/local"),
|
||||
]))
|
||||
.set_inbox(apb::Node::link(upub::url!(ctx, "/inbox")))
|
||||
.set_outbox(apb::Node::link(upub::url!(ctx, "/outbox")))
|
||||
.set_published(Some(ctx.actor().published))
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
use axum::extract::{Query, State};
|
||||
use upub::Context;
|
||||
|
||||
use crate::{AuthIdentity, builders::JsonLD};
|
||||
|
||||
use super::Pagination;
|
||||
|
||||
|
||||
pub async fn get(
|
||||
State(ctx): State<Context>,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
crate::builders::collection(&upub::url!(ctx, "/feed"), None)
|
||||
}
|
||||
|
||||
pub async fn page(
|
||||
State(ctx): State<Context>,
|
||||
AuthIdentity(auth): AuthIdentity,
|
||||
Query(page): Query<Pagination>,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
crate::builders::paginate_objects(
|
||||
upub::url!(ctx, "/feed/page"),
|
||||
auth.filter_objects(),
|
||||
ctx.db(),
|
||||
page,
|
||||
auth.my_id(),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
}
|
|
@ -19,7 +19,7 @@ pub async fn page(
|
|||
AuthIdentity(auth): AuthIdentity,
|
||||
Query(page): Query<Pagination>,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
crate::builders::paginate_activities(
|
||||
crate::builders::paginate_feed(
|
||||
upub::url!(ctx, "/inbox/page"),
|
||||
auth.filter_activities(),
|
||||
ctx.db(),
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
pub mod actor;
|
||||
pub mod inbox;
|
||||
pub mod outbox;
|
||||
pub mod feed;
|
||||
pub mod object;
|
||||
pub mod activity;
|
||||
pub mod application;
|
||||
|
@ -32,8 +31,6 @@ impl ActivityPubRouter for Router<upub::Context> {
|
|||
.route("/outbox", post(ap::outbox::post))
|
||||
.route("/outbox", get(ap::outbox::get))
|
||||
.route("/outbox/page", get(ap::outbox::page))
|
||||
.route("/feed", get(ap::feed::get))
|
||||
.route("/feed/page", get(ap::feed::page))
|
||||
// AUTH routes
|
||||
.route("/auth", put(ap::auth::register))
|
||||
.route("/auth", post(ap::auth::login))
|
||||
|
@ -52,10 +49,6 @@ impl ActivityPubRouter for Router<upub::Context> {
|
|||
.route("/actors/:id/outbox", post(ap::actor::outbox::post))
|
||||
.route("/actors/:id/outbox", get(ap::actor::outbox::get))
|
||||
.route("/actors/:id/outbox/page", get(ap::actor::outbox::page))
|
||||
.route("/actors/:id/streams", get(ap::actor::streams::get))
|
||||
.route("/actors/:id/streams/page", get(ap::actor::streams::page))
|
||||
.route("/actors/:id/feed", get(ap::actor::feed::get))
|
||||
.route("/actors/:id/feed/page", get(ap::actor::feed::page))
|
||||
.route("/actors/:id/followers", get(ap::actor::following::get::<false>))
|
||||
.route("/actors/:id/followers/page", get(ap::actor::following::page::<false>))
|
||||
.route("/actors/:id/following", get(ap::actor::following::get::<true>))
|
||||
|
|
|
@ -11,7 +11,7 @@ pub async fn get(
|
|||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
let context = ctx.oid(&id);
|
||||
|
||||
let count = upub::Query::objects(auth.my_id())
|
||||
let count = upub::Query::feed(auth.my_id())
|
||||
.filter(auth.filter_objects())
|
||||
.filter(model::object::Column::Context.eq(&context))
|
||||
.count(ctx.db())
|
||||
|
@ -28,7 +28,7 @@ pub async fn page(
|
|||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
let context = ctx.oid(&id);
|
||||
|
||||
crate::builders::paginate_objects(
|
||||
crate::builders::paginate_feed(
|
||||
upub::url!(ctx, "/objects/{id}/context/page"),
|
||||
Condition::all()
|
||||
.add(auth.filter_objects())
|
||||
|
|
|
@ -4,7 +4,7 @@ pub mod context;
|
|||
use apb::{BaseMut, CollectionMut, ObjectMut, LD};
|
||||
use axum::extract::{Path, Query, State};
|
||||
use sea_orm::{ColumnTrait, QueryFilter, QuerySelect, SelectColumns, TransactionTrait};
|
||||
use upub::{model, selector::{BatchFillable, RichObject}, traits::Fetcher, Context};
|
||||
use upub::{model, selector::{BatchFillable, RichActivity}, traits::Fetcher, Context};
|
||||
|
||||
use crate::{builders::JsonLD, AuthIdentity};
|
||||
|
||||
|
@ -27,10 +27,10 @@ pub async fn view(
|
|||
}
|
||||
}
|
||||
|
||||
let item = upub::Query::objects(auth.my_id())
|
||||
let item = upub::Query::feed(auth.my_id())
|
||||
.filter(model::object::Column::Id.eq(&oid))
|
||||
.filter(auth.filter_objects())
|
||||
.into_model::<RichObject>()
|
||||
.into_model::<RichActivity>()
|
||||
.one(ctx.db())
|
||||
.await?
|
||||
.ok_or_else(crate::ApiError::not_found)?
|
||||
|
@ -45,7 +45,7 @@ pub async fn view(
|
|||
let mut replies = apb::Node::Empty;
|
||||
|
||||
if ctx.cfg().security.show_reply_ids {
|
||||
let replies_ids = upub::Query::objects(auth.my_id())
|
||||
let replies_ids = upub::Query::feed(auth.my_id())
|
||||
.filter(model::object::Column::InReplyTo.eq(oid))
|
||||
.filter(auth.filter_objects())
|
||||
.select_only()
|
||||
|
@ -59,7 +59,7 @@ pub async fn view(
|
|||
.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(Some(item.object.replies as u64))
|
||||
.set_total_items(item.object.as_ref().map(|x| x.replies as u64))
|
||||
.set_items(apb::Node::links(replies_ids))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub async fn get(
|
|||
// ctx.fetch_thread(&oid).await?;
|
||||
// }
|
||||
|
||||
let replies_ids = upub::Query::objects(auth.my_id())
|
||||
let replies_ids = upub::Query::feed(auth.my_id())
|
||||
.filter(model::object::Column::InReplyTo.eq(ctx.oid(&id)))
|
||||
.filter(auth.filter_objects())
|
||||
.select_only()
|
||||
|
@ -44,7 +44,7 @@ pub async fn page(
|
|||
let page_id = upub::url!(ctx, "/objects/{id}/replies/page");
|
||||
let oid = ctx.oid(&id);
|
||||
|
||||
crate::builders::paginate_objects(
|
||||
crate::builders::paginate_feed(
|
||||
page_id,
|
||||
Condition::all()
|
||||
.add(auth.filter_objects())
|
||||
|
|
|
@ -13,7 +13,7 @@ pub async fn page(
|
|||
Query(page): Query<Pagination>,
|
||||
AuthIdentity(auth): AuthIdentity,
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
crate::builders::paginate_activities(
|
||||
crate::builders::paginate_feed(
|
||||
upub::url!(ctx, "/outbox/page"),
|
||||
Condition::all()
|
||||
.add(auth.filter_activities())
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use apb::{BaseMut, CollectionMut, CollectionPageMut, LD};
|
||||
use sea_orm::{Condition, ConnectionTrait, QueryFilter, QuerySelect, RelationTrait};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use upub::selector::{BatchFillable, RichActivity, RichObject};
|
||||
use upub::selector::{BatchFillable, RichActivity};
|
||||
|
||||
use crate::activitypub::Pagination;
|
||||
|
||||
pub async fn paginate_activities(
|
||||
pub async fn paginate_feed(
|
||||
id: String,
|
||||
filter: Condition,
|
||||
db: &impl ConnectionTrait,
|
||||
|
@ -16,7 +16,7 @@ pub async fn paginate_activities(
|
|||
let limit = page.batch.unwrap_or(20).min(50);
|
||||
let offset = page.offset.unwrap_or(0);
|
||||
|
||||
let mut select = upub::Query::activities(my_id);
|
||||
let mut select = upub::Query::feed(my_id);
|
||||
|
||||
if with_users {
|
||||
select = select
|
||||
|
@ -46,52 +46,6 @@ pub async fn paginate_activities(
|
|||
collection_page(&id, offset, limit, items)
|
||||
}
|
||||
|
||||
// TODO can we merge these two??? there are basically only three differences
|
||||
|
||||
pub async fn paginate_objects(
|
||||
id: String,
|
||||
filter: Condition,
|
||||
db: &impl ConnectionTrait,
|
||||
page: Pagination,
|
||||
my_id: Option<i64>,
|
||||
with_users: bool, // TODO ewww too many arguments for this weird function...
|
||||
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
let limit = page.batch.unwrap_or(20).min(50);
|
||||
let offset = page.offset.unwrap_or(0);
|
||||
|
||||
let mut select = upub::Query::objects(my_id); // <--- difference one
|
||||
|
||||
if with_users {
|
||||
select = select
|
||||
.join(
|
||||
sea_orm::JoinType::InnerJoin,
|
||||
upub::model::object::Relation::Actors.def() // <--- difference two
|
||||
);
|
||||
}
|
||||
|
||||
let items = select
|
||||
.filter(filter)
|
||||
// TODO also limit to only local activities
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.into_model::<RichObject>() // <--- difference three
|
||||
.all(db)
|
||||
.await?
|
||||
.with_batched::<upub::model::attachment::Entity>(db)
|
||||
.await?
|
||||
.with_batched::<upub::model::mention::Entity>(db)
|
||||
.await?
|
||||
.with_batched::<upub::model::hashtag::Entity>(db)
|
||||
.await?;
|
||||
|
||||
let items : Vec<serde_json::Value> = items
|
||||
.into_iter()
|
||||
.map(|item| item.ap())
|
||||
.collect();
|
||||
|
||||
collection_page(&id, offset, limit, items)
|
||||
}
|
||||
|
||||
pub fn collection_page(id: &str, offset: u64, limit: u64, items: Vec<serde_json::Value>) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||
let next = if items.len() < limit as usize {
|
||||
apb::Node::Empty
|
||||
|
|
Loading…
Reference in a new issue