Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Failing after 5m2s
test / unit (pull_request) Successful in 16m19s
test / integration (pull_request) Failing after 17m15s
- feat(domain): Hashtag value object with canonical extract() — unifies two divergent private implementations; fields pre-compute raw/normalized/url_slug/ap_name - feat(presentation): Deps<S: FromAppState> extractor — each handler now declares its exact dependency surface; AppState unchanged; handlers become unit-testable without mocking all 20 deps - refactor(feed): replace 5 flat FeedRepository methods with FeedQuery/FeedScope — single query() method; SQL shared logic lives once; adding feed types no longer requires 5 edits - refactor(activitypub): ActivityPubRepository + OutboundFederationPort moved out of domain::ports into activitypub-base::ap_ports — domain crate no longer knows about AP IDs, inboxes, or actor URLs - fix(outbox): OutboxRelay now opens a per-row transaction so FOR UPDATE SKIP LOCKED actually holds the lock during publish + mark_delivered
122 lines
3.6 KiB
Rust
122 lines
3.6 KiB
Rust
use crate::{
|
|
errors::ApiError,
|
|
extractors::{AuthUser, Deps, FromAppState},
|
|
state::AppState,
|
|
};
|
|
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;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct ActorUrlBody {
|
|
pub actor_url: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct HandleBody {
|
|
pub handle: String,
|
|
}
|
|
|
|
pub struct FederationManagementDeps {
|
|
pub federation: Arc<dyn FederationActionPort>,
|
|
pub follows: Arc<dyn FollowRepository>,
|
|
pub users: Arc<dyn UserRepository>,
|
|
pub events: Arc<dyn EventPublisher>,
|
|
}
|
|
|
|
impl FromAppState for FederationManagementDeps {
|
|
fn from_state(s: &AppState) -> Self {
|
|
Self {
|
|
federation: s.federation.clone(),
|
|
follows: s.follows.clone(),
|
|
users: s.users.clone(),
|
|
events: s.events.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
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<FederationManagementDeps>,
|
|
AuthUser(uid): AuthUser,
|
|
) -> Result<Json<Vec<RemoteActorResponse>>, 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<FederationManagementDeps>,
|
|
AuthUser(uid): AuthUser,
|
|
Json(body): Json<ActorUrlBody>,
|
|
) -> Result<StatusCode, ApiError> {
|
|
accept_follow_request(&*d.federation, &uid, &body.actor_url).await?;
|
|
Ok(StatusCode::NO_CONTENT)
|
|
}
|
|
|
|
pub async fn delete_follower(
|
|
Deps(d): Deps<FederationManagementDeps>,
|
|
AuthUser(uid): AuthUser,
|
|
Json(body): Json<ActorUrlBody>,
|
|
) -> Result<StatusCode, ApiError> {
|
|
reject_follow_request(&*d.federation, &uid, &body.actor_url).await?;
|
|
Ok(StatusCode::NO_CONTENT)
|
|
}
|
|
|
|
pub async fn get_remote_followers(
|
|
Deps(d): Deps<FederationManagementDeps>,
|
|
AuthUser(uid): AuthUser,
|
|
) -> Result<Json<Vec<RemoteActorResponse>>, 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<FederationManagementDeps>,
|
|
AuthUser(uid): AuthUser,
|
|
) -> Result<Json<Vec<RemoteActorResponse>>, 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<FederationManagementDeps>,
|
|
AuthUser(uid): AuthUser,
|
|
Json(body): Json<HandleBody>,
|
|
) -> Result<StatusCode, ApiError> {
|
|
remove_remote_following(
|
|
&*d.follows,
|
|
&*d.users,
|
|
&*d.federation,
|
|
&*d.events,
|
|
&uid,
|
|
&body.handle,
|
|
)
|
|
.await?;
|
|
Ok(StatusCode::NO_CONTENT)
|
|
}
|