diff --git a/crates/adapters/activitypub/src/service.rs b/crates/adapters/activitypub/src/service.rs index aebe322..51ada90 100644 --- a/crates/adapters/activitypub/src/service.rs +++ b/crates/adapters/activitypub/src/service.rs @@ -736,6 +736,17 @@ impl FederationFollowPort for ApFederationAdapter { .map(|v| v.into_iter().map(k_ap_actor_to_domain).collect()) .map_err(|e| DomainError::ExternalService(e.to_string())) } + + async fn broadcast_move( + &self, + user_id: &UserId, + new_actor_url: url::Url, + ) -> Result<(), DomainError> { + self.inner + .broadcast_move(user_id.as_uuid(), new_actor_url) + .await + .map_err(|e| DomainError::Internal(e.to_string())) + } } // ── FederationFollowRequestPort ─────────────────────────────────────────────── diff --git a/crates/domain/src/ports.rs b/crates/domain/src/ports.rs index 63c5f4a..29da537 100644 --- a/crates/domain/src/ports.rs +++ b/crates/domain/src/ports.rs @@ -287,6 +287,11 @@ pub trait FederationFollowPort: Send + Sync { ) -> Result<(), DomainError>; async fn get_remote_following(&self, user_id: &UserId) -> Result, DomainError>; + async fn broadcast_move( + &self, + user_id: &UserId, + new_actor_url: url::Url, + ) -> Result<(), DomainError>; } #[async_trait] diff --git a/crates/presentation/src/handlers/federation_management.rs b/crates/presentation/src/handlers/federation_management.rs index f090c2f..1d54e9c 100644 --- a/crates/presentation/src/handlers/federation_management.rs +++ b/crates/presentation/src/handlers/federation_management.rs @@ -22,6 +22,11 @@ pub struct HandleBody { pub handle: String, } +#[derive(Deserialize)] +pub struct MoveBody { + pub new_actor_url: String, +} + deps_struct!(FederationManagementDeps { federation: FederationActionPort, follows: FollowRepository, @@ -107,3 +112,17 @@ pub async fn delete_following( .await?; Ok(StatusCode::NO_CONTENT) } + +pub async fn post_move_account( + Deps(d): Deps, + AuthUser(uid): AuthUser, + Json(body): Json, +) -> Result { + let new_url = url::Url::parse(&body.new_actor_url) + .map_err(|_| ApiError::BadRequest("invalid new_actor_url".into()))?; + d.federation + .broadcast_move(&uid, new_url) + .await + .map_err(ApiError::from)?; + Ok(StatusCode::NO_CONTENT) +} diff --git a/crates/presentation/src/routes.rs b/crates/presentation/src/routes.rs index 1c83f78..e9ac3c6 100644 --- a/crates/presentation/src/routes.rs +++ b/crates/presentation/src/routes.rs @@ -104,6 +104,10 @@ pub fn router() -> Router { get(federation_management::get_remote_following) .delete(federation_management::delete_following), ) + .route( + "/federation/me/move", + post(federation_management::post_move_account), + ) .route("/tags/popular", get(feed::get_popular_tags)) .route("/tags/{name}", get(feed::tag_thoughts_handler)) // notifications