refactor(activities): split into per-activity files with check_guards DRY helper

This commit is contained in:
2026-05-29 00:17:31 +02:00
parent 7ccc18e85c
commit 90a0d91b39
15 changed files with 1040 additions and 1083 deletions

105
src/activities/move_act.rs Normal file
View File

@@ -0,0 +1,105 @@
use activitypub_federation::{
activity_sending::SendActivityTask,
config::Data,
fetch::object_id::ObjectId,
protocol::context::WithContext,
traits::Activity,
};
use serde::{Deserialize, Serialize};
use url::Url;
use crate::actors::DbActor;
use crate::data::FederationData;
use crate::error::Error;
use super::follow::FollowActivity;
use super::helpers::check_guards;
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[serde(rename = "Move")]
pub struct MoveType;
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MoveActivity {
pub(crate) id: Url,
#[serde(rename = "type", default)]
pub(crate) kind: MoveType,
pub(crate) actor: ObjectId<DbActor>,
pub(crate) object: Url,
pub(crate) target: Url,
}
#[async_trait::async_trait]
impl Activity for MoveActivity {
type DataType = FederationData;
type Error = Error;
fn id(&self) -> &Url { &self.id }
fn actor(&self) -> &Url { self.actor.inner() }
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
if &self.object != self.actor.inner() {
return Err(Error::bad_request(anyhow::anyhow!("Move object must be the actor itself")));
}
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
if check_guards(&self.id, self.actor.inner(), data).await? {
return Ok(());
}
let target = ObjectId::<DbActor>::from(self.target.clone())
.dereference(data)
.await
.map_err(|e| Error::from(anyhow::anyhow!("{e}")))?;
if target.also_known_as.as_deref() != Some(self.object.as_str()) {
return Err(Error::bad_request(anyhow::anyhow!(
"Move target alsoKnownAs does not reference old actor"
)));
}
let affected = data
.federation_repo
.migrate_follower_actor(self.object.as_str(), self.target.as_str())
.await
.map_err(|e| Error::from(anyhow::anyhow!("{e}")))?;
let affected_count = affected.len();
for local_user_id in &affected {
let local_actor = match crate::actors::get_local_actor(*local_user_id, data).await {
Ok(a) => a,
Err(e) => { tracing::warn!(error = %e, %local_user_id, "Move: failed to load local actor"); continue; }
};
let follow_id = match crate::urls::activity_url(&data.base_url) {
Ok(u) => u,
Err(e) => { tracing::warn!(error = %e, "Move: failed to generate follow activity URL"); continue; }
};
let follow = FollowActivity {
id: follow_id,
kind: Default::default(),
actor: ObjectId::from(local_actor.ap_id.clone()),
object: ObjectId::from(self.target.clone()),
};
let sends = match SendActivityTask::prepare(
&WithContext::new_default(follow),
&local_actor,
vec![target.inbox_url.clone()],
data,
).await {
Ok(s) => s,
Err(e) => { tracing::warn!(error = %e, "Move: failed to prepare re-follow"); continue; }
};
for send in sends {
if let Err(e) = send.sign_and_send(data).await {
tracing::warn!(error = %e, %local_user_id, "Move: re-follow delivery failed");
}
}
}
tracing::info!(
actor = %self.actor.inner(),
target = %self.target,
affected = affected_count,
"received Move — migrated follower relationships"
);
Ok(())
}
}