chore: refactor

moved into models converters to ap objects, single conditions are now
direct column checks
This commit is contained in:
əlemi 2024-04-19 03:28:39 +02:00
parent 2073015b7f
commit dfe116506a
Signed by: alemi
GPG key ID: A4895B84D311642C
14 changed files with 97 additions and 127 deletions

View file

@ -1,5 +1,8 @@
use apb::{ActivityMut, BaseMut, ObjectMut};
use sea_orm::entity::prelude::*;
use crate::routes::activitypub::jsonld::LD;
use super::Audience;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
@ -37,6 +40,20 @@ impl Model {
bcc: activity.bcc().into(),
})
}
pub fn ap(self) -> serde_json::Value {
serde_json::Value::new_object()
.set_id(Some(&self.id))
.set_activity_type(Some(self.activity_type))
.set_actor(apb::Node::link(self.actor))
.set_object(apb::Node::maybe_link(self.object))
.set_target(apb::Node::maybe_link(self.target))
.set_published(Some(self.published))
.set_to(apb::Node::links(self.to.0.clone()))
.set_bto(apb::Node::Empty)
.set_cc(apb::Node::links(self.cc.0.clone()))
.set_bcc(apb::Node::Empty)
}
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View file

@ -1,8 +1,6 @@
use apb::{ActivityMut, Node};
use sea_orm::{entity::prelude::*, FromQueryResult, Iterable, QuerySelect, SelectColumns};
use crate::routes::activitypub::{activity::ap_activity, object::ap_object};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "addressing")]
pub struct Model {
@ -67,9 +65,10 @@ pub struct EmbeddedActivity {
impl From<EmbeddedActivity> for serde_json::Value {
fn from(value: EmbeddedActivity) -> Self {
let a = value.activity.ap();
match value.object {
Some(o) => ap_activity(value.activity).set_object(Node::object(ap_object(o))),
None => ap_activity(value.activity)
None => a,
Some(o) => a.set_object(Node::object(o.ap())),
}
}
}

View file

@ -1,5 +1,8 @@
use apb::{BaseMut, ObjectMut};
use sea_orm::entity::prelude::*;
use crate::routes::activitypub::jsonld::LD;
use super::Audience;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
@ -45,6 +48,23 @@ impl Model {
bcc: object.bcc().into(),
})
}
// TODO this is used outside /routes, maybe move in model?
pub fn ap(self) -> serde_json::Value {
serde_json::Value::new_object()
.set_id(Some(&self.id))
.set_object_type(Some(self.object_type))
.set_attributed_to(apb::Node::maybe_link(self.attributed_to))
.set_name(self.name.as_deref())
.set_summary(self.summary.as_deref())
.set_content(self.content.as_deref())
.set_context(apb::Node::maybe_link(self.context.clone()))
.set_in_reply_to(apb::Node::maybe_link(self.in_reply_to.clone()))
.set_published(Some(self.published))
.set_to(apb::Node::links(self.to.0.clone()))
.set_bto(apb::Node::Empty)
.set_cc(apb::Node::links(self.cc.0.clone()))
.set_bcc(apb::Node::Empty)
}
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View file

@ -1,6 +1,8 @@
use sea_orm::entity::prelude::*;
use apb::{Collection, Object, Actor, PublicKey, ActorType};
use apb::{Actor, ActorMut, ActorType, BaseMut, Collection, DocumentMut, Object, ObjectMut, PublicKey, PublicKeyMut};
use crate::routes::activitypub::jsonld::LD;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "users")]
@ -62,6 +64,38 @@ impl Model {
private_key: None, // there's no way to transport privkey over AP json, must come from DB
})
}
pub fn ap(self) -> serde_json::Value {
serde_json::Value::new_object()
.set_id(Some(&self.id))
.set_actor_type(Some(self.actor_type))
.set_name(self.name.as_deref())
.set_summary(self.summary.as_deref())
.set_icon(apb::Node::maybe_object(self.icon.map(|i|
serde_json::Value::new_object()
.set_document_type(Some(apb::DocumentType::Image))
.set_url(apb::Node::link(i.clone()))
)))
.set_image(apb::Node::maybe_object(self.image.map(|i|
serde_json::Value::new_object()
.set_document_type(Some(apb::DocumentType::Image))
.set_url(apb::Node::link(i.clone()))
)))
.set_published(Some(self.created))
.set_preferred_username(Some(&self.preferred_username))
.set_inbox(apb::Node::maybe_link(self.inbox))
.set_outbox(apb::Node::maybe_link(self.outbox))
.set_following(apb::Node::maybe_link(self.following))
.set_followers(apb::Node::maybe_link(self.followers))
.set_public_key(apb::Node::object(
serde_json::Value::new_object()
.set_id(Some(&format!("{}#main-key", self.id)))
.set_owner(Some(&self.id))
.set_public_key_pem(&self.public_key)
))
.set_discoverable(Some(true))
.set_endpoints(apb::Node::Empty)
}
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View file

@ -1,25 +1,9 @@
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, QueryFilter};
use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}};
use apb::{ActivityMut, ObjectMut, BaseMut, Node};
use super::{jsonld::LD, JsonLD, TryFetch};
// TODO this is used outside /routes, maybe move in model?
pub fn ap_activity(activity: model::activity::Model) -> serde_json::Value {
serde_json::Value::new_object()
.set_id(Some(&activity.id))
.set_activity_type(Some(activity.activity_type))
.set_actor(Node::link(activity.actor))
.set_object(Node::maybe_link(activity.object))
.set_target(Node::maybe_link(activity.target))
.set_published(Some(activity.published))
.set_to(Node::links(activity.to.0.clone()))
.set_bto(Node::Empty)
.set_cc(Node::links(activity.cc.0.clone()))
.set_bcc(Node::Empty)
}
pub async fn view(
State(ctx): State<Context>,
Path(id): Path<String>,
@ -40,7 +24,7 @@ pub async fn view(
{
Some(activity) => Ok(JsonLD(serde_json::Value::from(activity).ld_context())),
None => if auth.is_local() && query.fetch && !ctx.is_local(&aid) {
Ok(JsonLD(ap_activity(ctx.fetch_activity(&aid).await?).ld_context()))
Ok(JsonLD(ctx.fetch_activity(&aid).await?.ap().ld_context()))
} else {
Err(UpubError::not_found())
},

View file

@ -1,53 +0,0 @@
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, QueryFilter};
use apb::{ObjectMut, BaseMut, Node};
use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}};
use super::{jsonld::LD, JsonLD, TryFetch};
// TODO this is used outside /routes, maybe move in model?
pub fn ap_object(object: model::object::Model) -> serde_json::Value {
serde_json::Value::new_object()
.set_id(Some(&object.id))
.set_object_type(Some(object.object_type))
.set_attributed_to(Node::maybe_link(object.attributed_to))
.set_name(object.name.as_deref())
.set_summary(object.summary.as_deref())
.set_content(object.content.as_deref())
.set_context(Node::maybe_link(object.context.clone()))
.set_in_reply_to(Node::maybe_link(object.in_reply_to.clone()))
.set_published(Some(object.published))
.set_to(Node::links(object.to.0.clone()))
.set_bto(Node::Empty)
.set_cc(Node::links(object.cc.0.clone()))
.set_bcc(Node::Empty)
}
pub async fn view(
State(ctx): State<Context>,
Path(id): Path<String>,
AuthIdentity(auth): AuthIdentity,
Query(query): Query<TryFetch>,
) -> crate::Result<JsonLD<serde_json::Value>> {
let oid = if id.starts_with('+') {
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
} else {
ctx.oid(id.clone())
};
match model::addressing::Entity::find_activities()
.filter(model::object::Column::Id.eq(&oid))
.filter(auth.filter_condition())
.into_model::<EmbeddedActivity>()
.one(ctx.db())
.await?
{
Some(EmbeddedActivity { activity: _, object: Some(object) }) => Ok(JsonLD(ap_object(object).ld_context())),
Some(EmbeddedActivity { activity: _, object: None }) => Err(UpubError::not_found()),
None => if auth.is_local() && query.fetch && !ctx.is_local(&oid) {
Ok(JsonLD(ap_object(ctx.fetch_object(&oid).await?).ld_context()))
} else {
Err(UpubError::not_found())
},
}
}

View file

@ -1,5 +1,5 @@
use axum::{extract::{Path, Query, State}, http::StatusCode};
use sea_orm::{ColumnTrait, Condition, EntityTrait, PaginatorTrait, QueryFilter, QuerySelect, SelectColumns};
use sea_orm::{ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QuerySelect, SelectColumns};
use crate::{routes::activitypub::{jsonld::LD, JsonLD, Pagination}, model, server::Context, url};
@ -11,7 +11,7 @@ pub async fn get<const OUTGOING: bool>(
) -> Result<JsonLD<serde_json::Value>, StatusCode> {
let follow___ = if OUTGOING { "following" } else { "followers" };
let count = model::relation::Entity::find()
.filter(Condition::all().add(if OUTGOING { Follower } else { Following }.eq(ctx.uid(id.clone()))))
.filter(if OUTGOING { Follower } else { Following }.eq(ctx.uid(id.clone())))
.count(ctx.db()).await.unwrap_or_else(|e| {
tracing::error!("failed counting {follow___} for {id}: {e}");
0
@ -33,7 +33,7 @@ pub async fn page<const OUTGOING: bool>(
let limit = page.batch.unwrap_or(20).min(50);
let offset = page.offset.unwrap_or(0);
match model::relation::Entity::find()
.filter(Condition::all().add(if OUTGOING { Follower } else { Following }.eq(ctx.uid(id.clone()))))
.filter(if OUTGOING { Follower } else { Following }.eq(ctx.uid(id.clone())))
.select_only()
.select_column(if OUTGOING { Following } else { Follower })
.limit(limit)

View file

@ -1,6 +1,6 @@
use axum::{extract::{Path, Query, State}, http::StatusCode, Json};
use sea_orm::{ColumnTrait, Condition, Order, QueryFilter, QueryOrder, QuerySelect};
use sea_orm::{ColumnTrait, Order, QueryFilter, QueryOrder, QuerySelect};
use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, routes::activitypub::{jsonld::LD, JsonLD, Pagination}, server::{auth::{AuthIdentity, Identity}, Context}, url};
pub async fn get(
@ -35,7 +35,7 @@ pub async fn page(
let limit = page.batch.unwrap_or(20).min(50);
let offset = page.offset.unwrap_or(0);
let activities = model::addressing::Entity::find_activities()
.filter(Condition::all().add(model::addressing::Column::Actor.eq(&uid)))
.filter(model::addressing::Column::Actor.eq(&uid))
.order_by(model::addressing::Column::Published, Order::Desc)
.offset(offset)
.limit(limit)

View file

@ -7,42 +7,11 @@ pub mod following;
use axum::extract::{Path, Query, State};
use sea_orm::EntityTrait;
use apb::{ActorMut, BaseMut, CollectionMut, DocumentMut, DocumentType, Node, ObjectMut, PublicKeyMut};
use apb::{ActorMut, BaseMut, CollectionMut, Node};
use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url};
use super::{jsonld::LD, JsonLD, TryFetch};
pub fn ap_user(user: model::user::Model) -> serde_json::Value {
serde_json::Value::new_object()
.set_id(Some(&user.id))
.set_actor_type(Some(user.actor_type))
.set_name(user.name.as_deref())
.set_summary(user.summary.as_deref())
.set_icon(Node::maybe_object(user.icon.map(|i|
serde_json::Value::new_object()
.set_document_type(Some(DocumentType::Image))
.set_url(Node::link(i.clone()))
)))
.set_image(Node::maybe_object(user.image.map(|i|
serde_json::Value::new_object()
.set_document_type(Some(DocumentType::Image))
.set_url(Node::link(i.clone()))
)))
.set_published(Some(user.created))
.set_preferred_username(Some(&user.preferred_username))
.set_inbox(Node::maybe_link(user.inbox))
.set_outbox(Node::maybe_link(user.outbox))
.set_following(Node::maybe_link(user.following))
.set_followers(Node::maybe_link(user.followers))
.set_public_key(Node::object(
serde_json::Value::new_object()
.set_id(Some(&format!("{}#main-key", user.id)))
.set_owner(Some(&user.id))
.set_public_key_pem(&user.public_key)
))
.set_discoverable(Some(true))
.set_endpoints(Node::Empty)
}
pub async fn view(
State(ctx) : State<Context>,
@ -61,7 +30,7 @@ pub async fn view(
{
// local user
Some((user, Some(cfg))) => {
Ok(JsonLD(ap_user(user.clone()) // ew ugly clone TODO
Ok(JsonLD(user.clone().ap() // ew ugly clone TODO
.set_inbox(Node::link(url!(ctx, "/users/{id}/inbox"))) // TODO unread activities as count
.set_outbox(Node::object(
serde_json::Value::new_object()
@ -101,9 +70,9 @@ pub async fn view(
))
},
// remote user TODDO doesn't work?
Some((user, None)) => Ok(JsonLD(ap_user(user).ld_context())),
Some((user, None)) => Ok(JsonLD(user.ap().ld_context())),
None => if auth.is_local() && query.fetch && !ctx.is_local(&uid) {
Ok(JsonLD(ap_user(ctx.fetch_user(&uid).await?).ld_context()))
Ok(JsonLD(ctx.fetch_user(&uid).await?.ap().ld_context()))
} else {
Err(UpubError::not_found())
},

View file

@ -1,5 +1,5 @@
use axum::{extract::{Path, Query, State}, http::StatusCode, Json};
use sea_orm::{ColumnTrait, Condition, Order, QueryFilter, QueryOrder, QuerySelect};
use sea_orm::{ColumnTrait, Order, QueryFilter, QueryOrder, QuerySelect};
use apb::{server::Outbox, AcceptType, ActivityType, Base, BaseType, ObjectType, RejectType};
use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, routes::activitypub::{jsonld::LD, CreationResult, JsonLD, Pagination}, server::{auth::{AuthIdentity, Identity}, Context}, url};
@ -28,7 +28,7 @@ pub async fn page(
let offset = page.offset.unwrap_or(0);
match model::addressing::Entity::find_activities()
.filter(Condition::all().add(model::activity::Column::Actor.eq(&uid)))
.filter(model::activity::Column::Actor.eq(&uid))
.filter(auth.filter_condition())
.order_by(model::addressing::Column::Published, Order::Desc)
.limit(limit)

View file

@ -1,6 +1,6 @@
use axum::{extract::{Path, Query, State}, http::StatusCode, Json};
use mastodon_async_entities::{account::{Account, AccountId}, status::Status};
use sea_orm::{ColumnTrait, Condition, EntityTrait, Order, QueryFilter, QueryOrder};
use sea_orm::{ColumnTrait, EntityTrait, Order, QueryFilter, QueryOrder};
use crate::{model, server::{auth::AuthIdentity, Context}};
@ -71,7 +71,7 @@ pub async fn statuses(
) -> Result<Json<Vec<Status>>, StatusCode> {
let uid = ctx.uid(id);
model::addressing::Entity::find_activities()
.filter(Condition::all().add(model::activity::Column::Actor.eq(uid)))
.filter(model::activity::Column::Actor.eq(uid))
.filter(auth.filter_condition())
.order_by(model::addressing::Column::Published, Order::Desc);

View file

@ -77,7 +77,7 @@ where
if auth_header.starts_with("Bearer ") {
match model::session::Entity::find_by_id(auth_header.replace("Bearer ", ""))
.filter(Condition::all().add(model::session::Column::Expires.gt(chrono::Utc::now())))
.filter(model::session::Column::Expires.gt(chrono::Utc::now()))
.one(ctx.db())
.await
{

View file

@ -2,7 +2,7 @@ use std::sync::Arc;
use apb::{BaseMut, CollectionMut, CollectionPageMut};
use openssl::rsa::Rsa;
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, SelectColumns, Set};
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, SelectColumns, Set};
use crate::{model, routes::activitypub::jsonld::LD};
@ -134,7 +134,7 @@ impl Context {
if target.ends_with("/followers") {
let target_id = target.replace("/followers", "");
model::relation::Entity::find()
.filter(Condition::all().add(model::relation::Column::Following.eq(target_id)))
.filter(model::relation::Column::Following.eq(target_id))
.select_only()
.select_column(model::relation::Column::Follower)
.into_tuple::<String>()

View file

@ -1,9 +1,9 @@
use reqwest::Method;
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, Order, QueryFilter, QueryOrder};
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, Order, QueryFilter, QueryOrder};
use tokio::{sync::broadcast, task::JoinHandle};
use apb::{ActivityMut, Node};
use crate::{errors::UpubError, model, routes::activitypub::{activity::ap_activity, object::ap_object}, server::{fetcher::Fetcher, Context}};
use crate::{errors::UpubError, model, server::{fetcher::Fetcher, Context}};
pub struct Dispatcher {
waker: broadcast::Sender<()>,
@ -39,7 +39,7 @@ impl Dispatcher {
async fn worker(db: DatabaseConnection, domain: String, poll_interval: u64, mut waker: broadcast::Receiver<()>) -> Result<(), UpubError> {
loop {
let Some(delivery) = model::delivery::Entity::find()
.filter(Condition::all().add(model::delivery::Column::NotBefore.lte(chrono::Utc::now())))
.filter(model::delivery::Column::NotBefore.lte(chrono::Utc::now()))
.order_by(model::delivery::Column::NotBefore, Order::Asc)
.one(&db)
.await?
@ -76,8 +76,8 @@ async fn worker(db: DatabaseConnection, domain: String, poll_interval: u64, mut
.one(&db)
.await? // TODO probably should not fail here and at least re-insert the delivery
{
Some((activity, Some(object))) => ap_activity(activity).set_object(Node::object(ap_object(object))),
Some((activity, None)) => ap_activity(activity),
Some((activity, Some(object))) => activity.ap().set_object(Node::object(object.ap())),
Some((activity, None)) => activity.ap(),
None => {
tracing::warn!("skipping dispatch for deleted object {}", delivery.activity);
continue;