diff --git a/worker/src/outbound.rs b/worker/src/outbound.rs index fb928cb..15530e3 100644 --- a/worker/src/outbound.rs +++ b/worker/src/outbound.rs @@ -1,5 +1,5 @@ use apb::{target::Addressed, Activity, ActivityMut, ActorMut, Base, BaseMut, Object, ObjectMut, Shortcuts}; -use sea_orm::{prelude::Expr, ColumnTrait, DbErr, EntityTrait, QueryFilter, QueryOrder, QuerySelect, SelectColumns, TransactionTrait}; +use sea_orm::{prelude::Expr, ColumnTrait, DbErr, EntityTrait, QueryFilter, QuerySelect, SelectColumns, TransactionTrait}; use upub::{model::{self, actor::Field}, traits::{process::ProcessorError, Addresser, Processor}, Context}; @@ -46,13 +46,46 @@ pub async fn process(ctx: Context, job: &model::job::Model) -> crate::JobResult< .set_published(Some(now)); if matches!(t, apb::ObjectType::Activity(apb::ActivityType::Undo)) { - let undone = activity.object().id()?; - let activity = upub::model::activity::Entity::find_by_ap_id(&undone) - .one(&tx) - .await? - .ok_or_else(|| DbErr::RecordNotFound(undone))?; - if activity.actor != job.actor { - return Err(crate::JobError::Forbidden); + match activity.object().id() { + Ok(undone) => { + let activity = upub::model::activity::Entity::find_by_ap_id(&undone) + .one(&tx) + .await? + .ok_or_else(|| DbErr::RecordNotFound(undone))?; + if activity.actor != job.actor { + return Err(crate::JobError::Forbidden); + } + }, + Err(_) => { + // frontend doesn't know the activity id, so we have to look it up + let undone = activity.object().into_inner()?; // if even this is missing, malformed + match undone.activity_type()? { + apb::ActivityType::Follow => { + let follower = undone.actor().id().unwrap_or(job.actor.clone()); + let follower_internal = upub::model::actor::Entity::ap_to_internal(&follower, &tx) + .await? + .ok_or(sea_orm::DbErr::RecordNotFound(follower))?; + let following = undone.object().id()?; + let following_internal = upub::model::actor::Entity::ap_to_internal(&following, &tx) + .await? + .ok_or(sea_orm::DbErr::RecordNotFound(following))?; + let activity_id = upub::model::relation::Entity::find() + .filter(upub::model::relation::Column::Follower.eq(follower_internal)) + .filter(upub::model::relation::Column::Following.eq(following_internal)) + .select_only() + .select_column(upub::model::relation::Column::Activity) + .into_tuple::<String>() + .one(&tx) + .await? + .ok_or(crate::JobError::Forbidden)?; + + activity = activity.set_object(apb::Node::link(activity_id)); + }, + t => return Err(crate::JobError::ProcessorError( + upub::traits::process::ProcessorError::Unprocessable(format!("can't normalize Undo({t})")) + )), + } + }, } }