feat: process some Undo activities, small refactor

This commit is contained in:
əlemi 2024-04-14 16:47:36 +02:00
parent b99ca9fa7e
commit b1b0aee2f9
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 59 additions and 38 deletions

View file

@ -1,4 +1,4 @@
use apb::{server::Inbox, ActivityType, Base, BaseType, ObjectType}; use apb::{server::Inbox, Activity, ActivityType, Base, BaseType, ObjectType};
use axum::{extract::{Query, State}, http::StatusCode, Json}; use axum::{extract::{Query, State}, http::StatusCode, Json};
use sea_orm::{Order, QueryFilter, QueryOrder, QuerySelect}; use sea_orm::{Order, QueryFilter, QueryOrder, QuerySelect};
@ -52,48 +52,27 @@ pub async fn post(
Identity::Local(_user) => return Err(UpubError::forbidden()), Identity::Local(_user) => return Err(UpubError::forbidden()),
Identity::Anonymous => return Err(UpubError::unauthorized()), Identity::Anonymous => return Err(UpubError::unauthorized()),
} }
match activity.base_type() {
None => { Err(StatusCode::BAD_REQUEST.into()) },
Some(BaseType::Link(_x)) => { // TODO we could process Links and bare Objects maybe, but probably out of AP spec?
tracing::warn!("skipping remote activity: {}", serde_json::to_string_pretty(&activity).unwrap()); match activity.activity_type().ok_or_else(UpubError::bad_request)? {
Err(StatusCode::UNPROCESSABLE_ENTITY.into()) // we could but not yet ActivityType::Activity => {
},
Some(BaseType::Object(ObjectType::Activity(ActivityType::Activity))) => {
tracing::warn!("skipping unprocessable base activity: {}", serde_json::to_string_pretty(&activity).unwrap()); tracing::warn!("skipping unprocessable base activity: {}", serde_json::to_string_pretty(&activity).unwrap());
Err(StatusCode::UNPROCESSABLE_ENTITY.into()) // won't ingest useless stuff Err(StatusCode::UNPROCESSABLE_ENTITY.into()) // won't ingest useless stuff
}, },
Some(BaseType::Object(ObjectType::Activity(ActivityType::Delete))) => ActivityType::Delete => Ok(ctx.delete(activity).await?),
Ok(ctx.delete(activity).await?), ActivityType::Follow => Ok(ctx.follow(activity).await?),
ActivityType::Accept(_) => Ok(ctx.accept(activity).await?),
ActivityType::Reject(_) => Ok(ctx.reject(activity).await?),
ActivityType::Like => Ok(ctx.like(activity).await?),
ActivityType::Create => Ok(ctx.create(activity).await?),
ActivityType::Update => Ok(ctx.update(activity).await?),
ActivityType::Undo => Ok(ctx.undo(activity).await?),
// ActivityType::Announce => Ok(ctx.announce(activity).await?),
Some(BaseType::Object(ObjectType::Activity(ActivityType::Follow))) => _x => {
Ok(ctx.follow(activity).await?),
Some(BaseType::Object(ObjectType::Activity(ActivityType::Accept(_)))) =>
Ok(ctx.accept(activity).await?),
Some(BaseType::Object(ObjectType::Activity(ActivityType::Reject(_)))) =>
Ok(ctx.reject(activity).await?),
Some(BaseType::Object(ObjectType::Activity(ActivityType::Like))) =>
Ok(ctx.like(activity).await?),
Some(BaseType::Object(ObjectType::Activity(ActivityType::Create))) =>
Ok(ctx.create(activity).await?),
Some(BaseType::Object(ObjectType::Activity(ActivityType::Update))) =>
Ok(ctx.update(activity).await?),
Some(BaseType::Object(ObjectType::Activity(_x))) => {
tracing::info!("received unimplemented activity on inbox: {}", serde_json::to_string_pretty(&activity).unwrap()); tracing::info!("received unimplemented activity on inbox: {}", serde_json::to_string_pretty(&activity).unwrap());
Err(StatusCode::NOT_IMPLEMENTED.into()) Err(StatusCode::NOT_IMPLEMENTED.into())
}, },
Some(_x) => {
tracing::warn!("ignoring non-activity object in inbox: {}", serde_json::to_string_pretty(&activity).unwrap());
Err(StatusCode::UNPROCESSABLE_ENTITY.into())
}
} }
} }

View file

@ -1,5 +1,6 @@
use apb::{target::Addressed, Activity, Base, Object}; use apb::{target::Addressed, Activity, Base, Object};
use sea_orm::{sea_query::Expr, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, Set}; use reqwest::StatusCode;
use sea_orm::{sea_query::Expr, ColumnTrait, Condition, EntityTrait, IntoActiveModel, QueryFilter, Set};
use crate::{errors::{LoggableError, UpubError}, model}; use crate::{errors::{LoggableError, UpubError}, model};
@ -181,7 +182,48 @@ impl apb::server::Inbox for Context {
Ok(()) Ok(())
} }
async fn undo(&self, _activity: serde_json::Value) -> crate::Result<()> { async fn undo(&self, activity: serde_json::Value) -> crate::Result<()> {
todo!() let uid = activity.actor().id().ok_or_else(UpubError::bad_request)?;
// TODO in theory we could work with just object_id but right now only accept embedded
let undone_activity = activity.object().extract().ok_or_else(UpubError::bad_request)?;
let undone_aid = undone_activity.id().ok_or_else(UpubError::bad_request)?;
let undone_object_id = undone_activity.object().id().ok_or_else(UpubError::bad_request)?;
let activity_type = undone_activity.activity_type().ok_or_else(UpubError::bad_request)?;
match activity_type {
apb::ActivityType::Like => {
model::like::Entity::delete_many()
.filter(
Condition::all()
.add(model::like::Column::Actor.eq(&uid))
.add(model::like::Column::Likes.eq(&undone_object_id))
)
.exec(self.db())
.await?;
model::object::Entity::update_many()
.filter(model::object::Column::Id.eq(&undone_object_id))
.col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).sub(1))
.exec(self.db())
.await?;
},
apb::ActivityType::Follow => {
model::relation::Entity::delete_many()
.filter(
Condition::all()
.add(model::relation::Column::Follower.eq(&uid))
.add(model::relation::Column::Following.eq(&undone_object_id))
)
.exec(self.db())
.await?;
},
_ => {
tracing::error!("received 'Undo' for unimplemented activity: {}", serde_json::to_string_pretty(&activity).unwrap());
return Err(StatusCode::NOT_IMPLEMENTED.into());
},
}
model::activity::Entity::delete_by_id(undone_aid).exec(self.db()).await?;
Ok(())
} }
} }