forked from alemi/upub
feat: show attachments in inbox, outbox and /object
attachments are lazy loaded, so it may be efficient if not all posts have media, but it should probably be eager loaded anyway eventually
This commit is contained in:
parent
c595f5f5e3
commit
cc287d3aa4
7 changed files with 82 additions and 60 deletions
|
@ -1,4 +1,4 @@
|
|||
use apb::{ActivityMut, Node};
|
||||
use apb::{ActivityMut, ObjectMut};
|
||||
use sea_orm::{entity::prelude::*, FromQueryResult, Iterable, QuerySelect, SelectColumns};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
|
@ -67,12 +67,22 @@ pub struct EmbeddedActivity {
|
|||
pub object: Option<crate::model::object::Model>,
|
||||
}
|
||||
|
||||
impl From<EmbeddedActivity> for serde_json::Value {
|
||||
fn from(value: EmbeddedActivity) -> Self {
|
||||
let a = value.activity.ap();
|
||||
match value.object {
|
||||
None => a,
|
||||
Some(o) => a.set_object(Node::object(o.ap())),
|
||||
impl EmbeddedActivity {
|
||||
pub async fn ap_filled(self, db: &DatabaseConnection) -> crate::Result<serde_json::Value> {
|
||||
let a = self.activity.ap();
|
||||
match self.object {
|
||||
None => Ok(a),
|
||||
Some(o) => {
|
||||
let attachments = o.find_related(crate::model::attachment::Entity)
|
||||
.all(db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| x.ap())
|
||||
.collect();
|
||||
Ok(a.set_object(
|
||||
apb::Node::object(o.ap().set_attachment(apb::Node::array(attachments)))
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,13 +101,20 @@ pub struct WrappedObject {
|
|||
pub object: crate::model::object::Model,
|
||||
}
|
||||
|
||||
impl From<WrappedObject> for serde_json::Value {
|
||||
fn from(value: WrappedObject) -> Self {
|
||||
match value.activity {
|
||||
None => value.object.ap(),
|
||||
Some(a) => a.ap().set_object(
|
||||
Node::object(value.object.ap())
|
||||
),
|
||||
|
||||
impl WrappedObject {
|
||||
pub async fn ap_filled(self, db: &DatabaseConnection) -> crate::Result<serde_json::Value> {
|
||||
let attachments = self.object.find_related(crate::model::attachment::Entity)
|
||||
.all(db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| x.ap())
|
||||
.collect();
|
||||
let o = self.object.ap()
|
||||
.set_attachment(apb::Node::Array(attachments));
|
||||
match self.activity {
|
||||
None => Ok(o),
|
||||
Some(a) => Ok(a.ap().set_object(apb::Node::object(o))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ pub async fn view(
|
|||
.one(ctx.db())
|
||||
.await?
|
||||
{
|
||||
Some(activity) => Ok(JsonLD(serde_json::Value::from(activity).ld_context())),
|
||||
Some(activity) => Ok(JsonLD(
|
||||
activity.ap_filled(ctx.db()).await?.ld_context()
|
||||
)),
|
||||
None => Err(UpubError::not_found()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,14 +28,14 @@ pub async fn page(
|
|||
.into_model::<EmbeddedActivity>()
|
||||
.all(ctx.db())
|
||||
.await?;
|
||||
let mut out = Vec::new();
|
||||
for activity in activities {
|
||||
out.push(activity.ap_filled(ctx.db()).await?);
|
||||
}
|
||||
Ok(JsonLD(
|
||||
ctx.ap_collection_page(
|
||||
&url!(ctx, "/inbox/page"),
|
||||
offset, limit,
|
||||
activities
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect()
|
||||
offset, limit, out,
|
||||
).ld_context()
|
||||
))
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ pub mod replies;
|
|||
|
||||
use apb::{BaseMut, CollectionMut, ObjectMut};
|
||||
use axum::extract::{Path, Query, State};
|
||||
use sea_orm::{ColumnTrait, QueryFilter};
|
||||
use sea_orm::{ColumnTrait, ModelTrait, QueryFilter};
|
||||
|
||||
use crate::{errors::UpubError, model::{self, addressing::WrappedObject}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}};
|
||||
|
||||
|
@ -33,6 +33,13 @@ pub async fn view(
|
|||
return Err(UpubError::not_found());
|
||||
};
|
||||
|
||||
let attachments = object.object.find_related(model::attachment::Entity)
|
||||
.all(ctx.db())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| x.ap())
|
||||
.collect::<Vec<serde_json::Value>>();
|
||||
|
||||
let replies =
|
||||
serde_json::Value::new_object()
|
||||
.set_id(Some(&crate::url!(ctx, "/objects/{id}/replies")))
|
||||
|
@ -43,6 +50,7 @@ pub async fn view(
|
|||
Ok(JsonLD(
|
||||
object.object.ap()
|
||||
.set_replies(apb::Node::object(replies))
|
||||
.set_attachment(apb::Node::Array(attachments))
|
||||
.ld_context()
|
||||
))
|
||||
}
|
||||
|
|
|
@ -11,33 +11,31 @@ pub async fn page(
|
|||
State(ctx): State<Context>,
|
||||
Query(page): Query<Pagination>,
|
||||
AuthIdentity(auth): AuthIdentity,
|
||||
) -> Result<JsonLD<serde_json::Value>, StatusCode> {
|
||||
) -> crate::Result<JsonLD<serde_json::Value>> {
|
||||
let limit = page.batch.unwrap_or(20).min(50);
|
||||
let offset = page.offset.unwrap_or(0);
|
||||
|
||||
match model::addressing::Entity::find_activities()
|
||||
let items = model::addressing::Entity::find_activities()
|
||||
.filter(auth.filter_condition())
|
||||
// TODO also limit to only local activities
|
||||
.order_by(model::addressing::Column::Published, Order::Desc)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.into_model::<EmbeddedActivity>()
|
||||
.all(ctx.db()).await
|
||||
{
|
||||
Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
Ok(items) => {
|
||||
Ok(JsonLD(
|
||||
ctx.ap_collection_page(
|
||||
&url!(ctx, "/outbox/page"),
|
||||
offset, limit,
|
||||
items
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect()
|
||||
).ld_context()
|
||||
))
|
||||
},
|
||||
.all(ctx.db()).await?;
|
||||
|
||||
let mut out = Vec::new();
|
||||
for item in items {
|
||||
out.push(item.ap_filled(ctx.db()).await?);
|
||||
}
|
||||
|
||||
Ok(JsonLD(
|
||||
ctx.ap_collection_page(
|
||||
&url!(ctx, "/outbox/page"),
|
||||
offset, limit,
|
||||
out,
|
||||
).ld_context()
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn post(
|
||||
|
|
|
@ -42,14 +42,14 @@ pub async fn page(
|
|||
.into_model::<EmbeddedActivity>()
|
||||
.all(ctx.db())
|
||||
.await?;
|
||||
let mut out = Vec::new();
|
||||
for activity in activities {
|
||||
out.push(activity.ap_filled(ctx.db()).await?);
|
||||
}
|
||||
Ok(JsonLD(
|
||||
ctx.ap_collection_page(
|
||||
&url!(ctx, "/users/{id}/inbox/page"),
|
||||
offset, limit,
|
||||
activities
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect()
|
||||
offset, limit, out,
|
||||
).ld_context()
|
||||
))
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ pub async fn page(
|
|||
Path(id): Path<String>,
|
||||
Query(page): Query<Pagination>,
|
||||
AuthIdentity(auth): AuthIdentity,
|
||||
) -> Result<JsonLD<serde_json::Value>, StatusCode> {
|
||||
) -> crate::Result<JsonLD<serde_json::Value>> {
|
||||
let uid = if id.starts_with('+') {
|
||||
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
|
||||
} else {
|
||||
|
@ -27,29 +27,26 @@ pub async fn page(
|
|||
let limit = page.batch.unwrap_or(20).min(50);
|
||||
let offset = page.offset.unwrap_or(0);
|
||||
|
||||
match model::addressing::Entity::find_activities()
|
||||
let activities = model::addressing::Entity::find_activities()
|
||||
.filter(model::activity::Column::Actor.eq(&uid))
|
||||
.filter(auth.filter_condition())
|
||||
.order_by(model::addressing::Column::Published, Order::Desc)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.into_model::<EmbeddedActivity>()
|
||||
.all(ctx.db()).await
|
||||
{
|
||||
Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
Ok(items) => {
|
||||
Ok(JsonLD(
|
||||
ctx.ap_collection_page(
|
||||
&url!(ctx, "/users/{id}/outbox/page"),
|
||||
offset, limit,
|
||||
items
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect()
|
||||
).ld_context()
|
||||
))
|
||||
},
|
||||
.all(ctx.db()).await?;
|
||||
|
||||
let mut out = Vec::new();
|
||||
for activity in activities {
|
||||
out.push(activity.ap_filled(ctx.db()).await?);
|
||||
}
|
||||
|
||||
Ok(JsonLD(
|
||||
ctx.ap_collection_page(
|
||||
&url!(ctx, "/users/{id}/outbox/page"),
|
||||
offset, limit, out,
|
||||
).ld_context()
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn post(
|
||||
|
|
Loading…
Reference in a new issue