use crate::{ deps_struct, errors::ApiError, extractors::{AuthUser, Deps}, }; use api_types::responses::{ProfileField, RemoteActorResponse}; use application::use_cases::federation_management::{ accept_follow_request, list_pending_requests, list_remote_followers, list_remote_following, reject_follow_request, remove_remote_following, }; use axum::{http::StatusCode, Json}; use domain::ports::{EventPublisher, FederationActionPort, FollowRepository, UserRepository}; use serde::Deserialize; #[derive(Deserialize)] pub struct ActorUrlBody { pub actor_url: String, } #[derive(Deserialize)] pub struct HandleBody { pub handle: String, } #[derive(Deserialize)] pub struct MoveBody { pub new_actor_url: String, } deps_struct!(FederationManagementDeps { federation: FederationActionPort, follows: FollowRepository, users: UserRepository, events: EventPublisher, }); fn to_response(a: domain::models::remote_actor::RemoteActor) -> RemoteActorResponse { RemoteActorResponse { handle: a.handle, display_name: a.display_name, avatar_url: a.avatar_url, url: a.url, bio: a.bio, banner_url: a.banner_url, also_known_as: a.also_known_as, outbox_url: a.outbox_url, followers_url: a.followers_url, following_url: a.following_url, attachment: a .attachment .into_iter() .map(|(name, value)| ProfileField { name, value }) .collect(), } } pub async fn get_pending_requests( Deps(d): Deps, AuthUser(uid): AuthUser, ) -> Result>, ApiError> { let actors = list_pending_requests(&*d.federation, &uid).await?; Ok(Json(actors.into_iter().map(to_response).collect())) } pub async fn post_accept_request( Deps(d): Deps, AuthUser(uid): AuthUser, Json(body): Json, ) -> Result { accept_follow_request(&*d.federation, &*d.events, &uid, &body.actor_url).await?; Ok(StatusCode::NO_CONTENT) } pub async fn delete_follower( Deps(d): Deps, AuthUser(uid): AuthUser, Json(body): Json, ) -> Result { reject_follow_request(&*d.federation, &*d.events, &uid, &body.actor_url).await?; Ok(StatusCode::NO_CONTENT) } pub async fn get_remote_followers( Deps(d): Deps, AuthUser(uid): AuthUser, ) -> Result>, ApiError> { let actors = list_remote_followers(&*d.federation, &uid).await?; Ok(Json(actors.into_iter().map(to_response).collect())) } pub async fn get_remote_following( Deps(d): Deps, AuthUser(uid): AuthUser, ) -> Result>, ApiError> { let actors = list_remote_following(&*d.federation, &uid).await?; Ok(Json(actors.into_iter().map(to_response).collect())) } pub async fn delete_following( Deps(d): Deps, AuthUser(uid): AuthUser, Json(body): Json, ) -> Result { remove_remote_following( &*d.follows, &*d.users, &*d.federation, &*d.events, &uid, &body.handle, ) .await?; Ok(StatusCode::NO_CONTENT) } pub async fn post_move_account( Deps(d): Deps, AuthUser(uid): AuthUser, Json(body): Json, ) -> Result { url::Url::parse(&body.new_actor_url) .map_err(|_| ApiError::BadRequest("invalid new_actor_url".into()))?; d.events .publish(&domain::events::DomainEvent::ActorMoved { user_id: uid, new_actor_url: body.new_actor_url, }) .await .map_err(|e| ApiError::Domain(domain::errors::DomainError::Internal(e.to_string())))?; Ok(StatusCode::NO_CONTENT) } #[derive(Deserialize)] pub struct AlsoKnownAsBody { pub also_known_as: Option, } pub async fn patch_also_known_as( Deps(d): Deps, AuthUser(uid): AuthUser, Json(body): Json, ) -> Result { d.users.set_also_known_as(&uid, body.also_known_as).await?; Ok(StatusCode::NO_CONTENT) }