This commit is contained in:
2026-07-10 16:03:36 +02:00
parent 44d7df33a2
commit 2484f1e603
26 changed files with 217 additions and 224 deletions

View File

@@ -1,5 +1,4 @@
pub mod composite_handler;
pub mod social_adapter;
pub mod event_handler;
pub mod federation_event_bridge;
pub mod goal_handler;
@@ -7,6 +6,7 @@ pub mod objects;
pub mod port;
pub mod remote_review_repository;
pub mod review_handler;
pub mod social_adapter;
pub(crate) mod urls;
pub mod user_adapter;
pub mod watchlist_handler;

View File

@@ -87,11 +87,7 @@ fn ap_err(e: anyhow::Error) -> DomainError {
#[async_trait]
impl SocialCommand for CompositeSocialAdapter {
async fn follow(
&self,
follower: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError> {
async fn follow(&self, follower: &UserId, target: &SocialIdentity) -> Result<(), DomainError> {
if let SocialIdentity::Local(target_id) = target
&& follower == target_id
{
@@ -154,11 +150,7 @@ impl SocialCommand for CompositeSocialAdapter {
.map_err(ap_err)
}
async fn block(
&self,
blocker: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError> {
async fn block(&self, blocker: &UserId, target: &SocialIdentity) -> Result<(), DomainError> {
let actor_url = self.actor_url_from_identity(target);
self.ap_service
.block_actor(blocker.value(), &actor_url)
@@ -166,11 +158,7 @@ impl SocialCommand for CompositeSocialAdapter {
.map_err(ap_err)
}
async fn unblock(
&self,
blocker: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError> {
async fn unblock(&self, blocker: &UserId, target: &SocialIdentity) -> Result<(), DomainError> {
let actor_url = self.actor_url_from_identity(target);
self.ap_service
.unblock_actor(blocker.value(), &actor_url)
@@ -181,10 +169,7 @@ impl SocialCommand for CompositeSocialAdapter {
#[async_trait]
impl SocialQuery for CompositeSocialAdapter {
async fn get_following(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_following(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let actors = self
.ap_service
.get_following(user.value())
@@ -196,10 +181,7 @@ impl SocialQuery for CompositeSocialAdapter {
.collect())
}
async fn get_followers(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_followers(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let actors = self
.ap_service
.get_accepted_followers(user.value())
@@ -211,10 +193,7 @@ impl SocialQuery for CompositeSocialAdapter {
.collect())
}
async fn get_pending_followers(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_pending_followers(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let actors = self
.ap_service
.get_pending_followers(user.value())
@@ -240,10 +219,7 @@ impl SocialQuery for CompositeSocialAdapter {
.map_err(ap_err)
}
async fn get_blocked(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_blocked(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let actors = self
.ap_service
.get_blocked_actors(user.value())

View File

@@ -1,9 +1,5 @@
use async_trait::async_trait;
use domain::{
errors::DomainError,
models::RemoteActorInfo,
ports::FederationAdminQuery,
};
use domain::{errors::DomainError, models::RemoteActorInfo, ports::FederationAdminQuery};
use super::PostgresFederationRepository;

View File

@@ -1,9 +1,5 @@
use async_trait::async_trait;
use domain::{
errors::DomainError,
models::RemoteActorInfo,
ports::FederationAdminQuery,
};
use domain::{errors::DomainError, models::RemoteActorInfo, ports::FederationAdminQuery};
use super::SqliteFederationRepository;

View File

@@ -1,4 +1,7 @@
use domain::{errors::DomainError, value_objects::{SocialActor, UserId}};
use domain::{
errors::DomainError,
value_objects::{SocialActor, UserId},
};
use super::{deps::SocialQueryDeps, queries::GetBlockedQuery};

View File

@@ -1,4 +1,7 @@
use domain::{errors::DomainError, value_objects::{SocialActor, UserId}};
use domain::{
errors::DomainError,
value_objects::{SocialActor, UserId},
};
use super::{deps::SocialQueryDeps, queries::GetFollowersQuery};

View File

@@ -1,4 +1,7 @@
use domain::{errors::DomainError, value_objects::{SocialActor, UserId}};
use domain::{
errors::DomainError,
value_objects::{SocialActor, UserId},
};
use super::{deps::SocialQueryDeps, queries::GetFollowingQuery};

View File

@@ -1,4 +1,7 @@
use domain::{errors::DomainError, value_objects::{SocialActor, UserId}};
use domain::{
errors::DomainError,
value_objects::{SocialActor, UserId},
};
use super::{deps::SocialQueryDeps, queries::GetPendingFollowersQuery};

View File

@@ -14,7 +14,11 @@ use crate::social::{
follow,
};
fn make_deps() -> (Arc<InMemorySocialRepository>, Arc<NoopEventPublisher>, SocialCommandDeps) {
fn make_deps() -> (
Arc<InMemorySocialRepository>,
Arc<NoopEventPublisher>,
SocialCommandDeps,
) {
let social = InMemorySocialRepository::new();
let events = NoopEventPublisher::new();
let deps = SocialCommandDeps {
@@ -53,7 +57,9 @@ async fn accept_follow_emits_follow_accepted_event() {
.unwrap();
let published = events.published();
assert!(published
.iter()
.any(|e| matches!(e, DomainEvent::FollowAccepted { .. })));
assert!(
published
.iter()
.any(|e| matches!(e, DomainEvent::FollowAccepted { .. }))
);
}

View File

@@ -9,7 +9,11 @@ use uuid::Uuid;
use crate::social::{block, commands::BlockCommand, deps::SocialCommandDeps};
fn make_deps() -> (Arc<InMemorySocialRepository>, Arc<NoopEventPublisher>, SocialCommandDeps) {
fn make_deps() -> (
Arc<InMemorySocialRepository>,
Arc<NoopEventPublisher>,
SocialCommandDeps,
) {
let social = InMemorySocialRepository::new();
let events = NoopEventPublisher::new();
let deps = SocialCommandDeps {
@@ -35,7 +39,9 @@ async fn block_emits_actor_blocked_event() {
.unwrap();
let published = events.published();
assert!(published
.iter()
.any(|e| matches!(e, DomainEvent::ActorBlocked { .. })));
assert!(
published
.iter()
.any(|e| matches!(e, DomainEvent::ActorBlocked { .. }))
);
}

View File

@@ -9,7 +9,11 @@ use uuid::Uuid;
use crate::social::{commands::FollowCommand, deps::SocialCommandDeps, follow};
fn make_deps() -> (Arc<InMemorySocialRepository>, Arc<NoopEventPublisher>, SocialCommandDeps) {
fn make_deps() -> (
Arc<InMemorySocialRepository>,
Arc<NoopEventPublisher>,
SocialCommandDeps,
) {
let social = InMemorySocialRepository::new();
let events = NoopEventPublisher::new();
let deps = SocialCommandDeps {
@@ -35,9 +39,11 @@ async fn follow_emits_follow_requested_event() {
.unwrap();
let published = events.published();
assert!(published
.iter()
.any(|e| matches!(e, DomainEvent::FollowRequested { .. })));
assert!(
published
.iter()
.any(|e| matches!(e, DomainEvent::FollowRequested { .. }))
);
}
#[tokio::test]

View File

@@ -50,13 +50,8 @@ async fn returns_accepted_followers() {
.await
.unwrap();
let followers = get_followers::execute(
&query_deps,
GetFollowersQuery {
user_id: owner_id,
},
)
.await
.unwrap();
let followers = get_followers::execute(&query_deps, GetFollowersQuery { user_id: owner_id })
.await
.unwrap();
assert_eq!(followers.len(), 1);
}

View File

@@ -39,13 +39,8 @@ async fn returns_only_pending_followers() {
.await
.unwrap();
let pending = get_pending::execute(
&query_deps,
GetPendingFollowersQuery {
user_id: owner_id,
},
)
.await
.unwrap();
let pending = get_pending::execute(&query_deps, GetPendingFollowersQuery { user_id: owner_id })
.await
.unwrap();
assert_eq!(pending.len(), 1);
}

View File

@@ -12,7 +12,11 @@ use crate::social::{
follow, reject,
};
fn make_deps() -> (Arc<InMemorySocialRepository>, Arc<NoopEventPublisher>, SocialCommandDeps) {
fn make_deps() -> (
Arc<InMemorySocialRepository>,
Arc<NoopEventPublisher>,
SocialCommandDeps,
) {
let social = InMemorySocialRepository::new();
let events = NoopEventPublisher::new();
let deps = SocialCommandDeps {

View File

@@ -14,7 +14,11 @@ use crate::social::{
follow, remove_follower,
};
fn make_deps() -> (Arc<InMemorySocialRepository>, Arc<NoopEventPublisher>, SocialCommandDeps) {
fn make_deps() -> (
Arc<InMemorySocialRepository>,
Arc<NoopEventPublisher>,
SocialCommandDeps,
) {
let social = InMemorySocialRepository::new();
let events = NoopEventPublisher::new();
let deps = SocialCommandDeps {
@@ -62,7 +66,9 @@ async fn remove_follower_emits_follower_removed_event() {
.unwrap();
let published = events.published();
assert!(published
.iter()
.any(|e| matches!(e, DomainEvent::FollowerRemoved { .. })));
assert!(
published
.iter()
.any(|e| matches!(e, DomainEvent::FollowerRemoved { .. }))
);
}

View File

@@ -8,12 +8,17 @@ use domain::{
use uuid::Uuid;
use crate::social::{
block,
commands::{BlockCommand, UnblockCommand},
deps::SocialCommandDeps,
block, unblock,
unblock,
};
fn make_deps() -> (Arc<InMemorySocialRepository>, Arc<NoopEventPublisher>, SocialCommandDeps) {
fn make_deps() -> (
Arc<InMemorySocialRepository>,
Arc<NoopEventPublisher>,
SocialCommandDeps,
) {
let social = InMemorySocialRepository::new();
let events = NoopEventPublisher::new();
let deps = SocialCommandDeps {
@@ -40,18 +45,14 @@ async fn unblock_emits_actor_unblocked_event() {
.await
.unwrap();
unblock::execute(
&deps,
UnblockCommand {
blocker_id,
target,
},
)
.await
.unwrap();
unblock::execute(&deps, UnblockCommand { blocker_id, target })
.await
.unwrap();
let published = events.published();
assert!(published
.iter()
.any(|e| matches!(e, DomainEvent::ActorUnblocked { .. })));
assert!(
published
.iter()
.any(|e| matches!(e, DomainEvent::ActorUnblocked { .. }))
);
}

View File

@@ -13,7 +13,11 @@ use crate::social::{
follow, unfollow,
};
fn make_deps() -> (Arc<InMemorySocialRepository>, Arc<NoopEventPublisher>, SocialCommandDeps) {
fn make_deps() -> (
Arc<InMemorySocialRepository>,
Arc<NoopEventPublisher>,
SocialCommandDeps,
) {
let social = InMemorySocialRepository::new();
let events = NoopEventPublisher::new();
let deps = SocialCommandDeps {
@@ -51,7 +55,9 @@ async fn unfollow_emits_unfollowed_event() {
.unwrap();
let published = events.published();
assert!(published
.iter()
.any(|e| matches!(e, DomainEvent::Unfollowed { .. })));
assert!(
published
.iter()
.any(|e| matches!(e, DomainEvent::Unfollowed { .. }))
);
}

View File

@@ -4,9 +4,7 @@ use super::{commands::UnfollowCommand, deps::SocialCommandDeps};
pub async fn execute(deps: &SocialCommandDeps, cmd: UnfollowCommand) -> Result<(), DomainError> {
let follower = UserId::from_uuid(cmd.follower_id);
deps.social_command
.unfollow(&follower, &cmd.target)
.await?;
deps.social_command.unfollow(&follower, &cmd.target).await?;
deps.event_publisher
.publish(&DomainEvent::Unfollowed {
follower,

View File

@@ -4,8 +4,8 @@ use chrono::NaiveDateTime;
use crate::{
errors::DomainError,
models::{
DiaryEntry, FederationFlags, RemoteActorInfo, RemoteGoalEntry,
RemoteWatchlistEntry, WatchlistWithMovie,
DiaryEntry, FederationFlags, RemoteActorInfo, RemoteGoalEntry, RemoteWatchlistEntry,
WatchlistWithMovie,
},
value_objects::{MovieId, SocialActor, SocialIdentity, UserId},
};
@@ -14,17 +14,10 @@ use crate::{
#[async_trait]
pub trait SocialCommand: Send + Sync {
async fn follow(
&self,
follower: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError>;
async fn follow(&self, follower: &UserId, target: &SocialIdentity) -> Result<(), DomainError>;
async fn unfollow(
&self,
follower: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError>;
async fn unfollow(&self, follower: &UserId, target: &SocialIdentity)
-> Result<(), DomainError>;
async fn accept_follow(
&self,
@@ -44,44 +37,24 @@ pub trait SocialCommand: Send + Sync {
follower: &SocialIdentity,
) -> Result<(), DomainError>;
async fn block(
&self,
blocker: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError>;
async fn block(&self, blocker: &UserId, target: &SocialIdentity) -> Result<(), DomainError>;
async fn unblock(
&self,
blocker: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError>;
async fn unblock(&self, blocker: &UserId, target: &SocialIdentity) -> Result<(), DomainError>;
}
#[async_trait]
pub trait SocialQuery: Send + Sync {
async fn get_following(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError>;
async fn get_following(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError>;
async fn get_followers(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError>;
async fn get_followers(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError>;
async fn get_pending_followers(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError>;
async fn get_pending_followers(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError>;
async fn count_following(&self, user: &UserId) -> Result<usize, DomainError>;
async fn count_followers(&self, user: &UserId) -> Result<usize, DomainError>;
async fn get_blocked(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError>;
async fn get_blocked(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError>;
async fn is_following(
&self,
@@ -93,7 +66,6 @@ pub trait SocialQuery: Send + Sync {
&self,
user_id: &UserId,
) -> Result<Vec<String>, DomainError>;
}
#[async_trait]

View File

@@ -893,11 +893,7 @@ impl InMemorySocialRepository {
#[async_trait]
impl SocialCommand for InMemorySocialRepository {
async fn follow(
&self,
follower: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError> {
async fn follow(&self, follower: &UserId, target: &SocialIdentity) -> Result<(), DomainError> {
if let SocialIdentity::Local(target_id) = target {
if follower == target_id {
return Err(DomainError::ValidationError(
@@ -925,7 +921,9 @@ impl SocialCommand for InMemorySocialRepository {
let before = store.len();
store.retain(|(f, t, _)| !(*f == follower.value() && t == target));
if store.len() == before {
return Err(DomainError::NotFound("Follow relationship not found".into()));
return Err(DomainError::NotFound(
"Follow relationship not found".into(),
));
}
Ok(())
}
@@ -1008,11 +1006,7 @@ impl SocialCommand for InMemorySocialRepository {
Ok(())
}
async fn block(
&self,
blocker: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError> {
async fn block(&self, blocker: &UserId, target: &SocialIdentity) -> Result<(), DomainError> {
let mut store = self.blocked.lock().unwrap();
store.push((blocker.value(), target.clone()));
// Also remove any existing follow relationships
@@ -1021,11 +1015,7 @@ impl SocialCommand for InMemorySocialRepository {
Ok(())
}
async fn unblock(
&self,
blocker: &UserId,
target: &SocialIdentity,
) -> Result<(), DomainError> {
async fn unblock(&self, blocker: &UserId, target: &SocialIdentity) -> Result<(), DomainError> {
let mut store = self.blocked.lock().unwrap();
store.retain(|(b, t)| !(*b == blocker.value() && t == target));
Ok(())
@@ -1034,10 +1024,7 @@ impl SocialCommand for InMemorySocialRepository {
#[async_trait]
impl SocialQuery for InMemorySocialRepository {
async fn get_following(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_following(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let store = self.follows.lock().unwrap();
Ok(store
.iter()
@@ -1046,10 +1033,7 @@ impl SocialQuery for InMemorySocialRepository {
.collect())
}
async fn get_followers(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_followers(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let store = self.follows.lock().unwrap();
let target = SocialIdentity::Local(user.clone());
Ok(store
@@ -1062,10 +1046,7 @@ impl SocialQuery for InMemorySocialRepository {
.collect())
}
async fn get_pending_followers(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_pending_followers(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let store = self.follows.lock().unwrap();
let target = SocialIdentity::Local(user.clone());
Ok(store
@@ -1095,10 +1076,7 @@ impl SocialQuery for InMemorySocialRepository {
.count())
}
async fn get_blocked(
&self,
user: &UserId,
) -> Result<Vec<SocialActor>, DomainError> {
async fn get_blocked(&self, user: &UserId) -> Result<Vec<SocialActor>, DomainError> {
let store = self.blocked.lock().unwrap();
Ok(store
.iter()
@@ -1113,9 +1091,9 @@ impl SocialQuery for InMemorySocialRepository {
target: &SocialIdentity,
) -> Result<bool, DomainError> {
let store = self.follows.lock().unwrap();
Ok(store
.iter()
.any(|(f, t, state)| *f == follower.value() && t == target && *state == FollowState::Accepted))
Ok(store.iter().any(|(f, t, state)| {
*f == follower.value() && t == target && *state == FollowState::Accepted
}))
}
async fn get_accepted_following_urls(

View File

@@ -64,8 +64,8 @@ impl ObjectStorage for NoopObjectStorage {
// Re-export production noop types so test code that imports from
// `domain::testing` keeps compiling without changes.
pub use crate::ports::noop::NoopRemoteWatchlistRepository;
pub use crate::ports::noop::NoopFederationAdminQuery;
pub use crate::ports::noop::NoopRemoteWatchlistRepository;
// ── NoopGoalCommand ───────────────────────────────────────────────────────────

View File

@@ -6,8 +6,8 @@ use crate::{
AnnotatedRow, DiaryEntry, DiaryFilter, EntityType, ExportFormat, ExternalPersonId,
FeedEntry, FeedSortBy, FieldMapping, FileFormat, FollowingFilter, ImportError,
ImportProfile, ImportSession, IndexableDocument, MovieProfile, MovieStats, ParsedFile,
Person, PersonCredits, PersonEnrichmentData, PersonId, RefreshSession,
RemoteActorInfo, ReviewHistory, SearchQuery, SearchResults, UserStats, UserTrends,
Person, PersonCredits, PersonEnrichmentData, PersonId, RefreshSession, RemoteActorInfo,
ReviewHistory, SearchQuery, SearchResults, UserStats, UserTrends,
collections::{PageParams, Paginated},
},
ports::{

View File

@@ -2,13 +2,14 @@ use std::sync::Arc;
use domain::ports::{
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher, FederatedProfileQuery,
GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository, MetadataClient,
MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage, PasswordHasher, PersonCommand,
PersonEnrichmentClient, PersonQuery, PosterFetcherClient, RefreshSessionRepository,
RemoteGoalRepository, RemoteWatchlistRepository, ReviewRepository, SearchCommand, SearchPort,
FederationAdminQuery, SocialCommand, SocialQuery, StatsRepository, UserProfileFieldsRepository,
UserRepository, UserSettingsRepository, WatchEventCommand, WatchEventQuery,
WatchlistRepository, WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
FederationAdminQuery, GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository,
MetadataClient, MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage,
PasswordHasher, PersonCommand, PersonEnrichmentClient, PersonQuery, PosterFetcherClient,
RefreshSessionRepository, RemoteGoalRepository, RemoteWatchlistRepository, ReviewRepository,
SearchCommand, SearchPort, SocialCommand, SocialQuery, StatsRepository,
UserProfileFieldsRepository, UserRepository, UserSettingsRepository, WatchEventCommand,
WatchEventQuery, WatchlistRepository, WebhookTokenRepository, WrapUpRepository,
WrapUpStatsQuery,
};
use application::config::AppConfig;

View File

@@ -84,7 +84,9 @@ pub async fn get_blocked_domains_admin(
_admin: AdminApiUser,
) -> Result<Json<Vec<BlockedDomainResponse>>, ApiError> {
let domains = state
.app_ctx.services.ap_service
.app_ctx
.services
.ap_service
.get_blocked_domains()
.await
.map_err(ap_to_domain)?;
@@ -116,7 +118,9 @@ pub async fn add_blocked_domain_admin(
axum::Json(body): axum::Json<AddBlockedDomainRequest>,
) -> Result<impl IntoResponse, ApiError> {
state
.app_ctx.services.ap_service
.app_ctx
.services
.ap_service
.add_blocked_domain(&body.domain, body.reason.as_deref())
.await
.map_err(ap_to_domain)?;
@@ -139,7 +143,9 @@ pub async fn remove_blocked_domain_admin(
axum::extract::Path(domain): axum::extract::Path<String>,
) -> Result<impl IntoResponse, ApiError> {
state
.app_ctx.services.ap_service
.app_ctx
.services
.ap_service
.remove_blocked_domain(&domain)
.await
.map_err(ap_to_domain)?;
@@ -263,10 +269,7 @@ pub async fn get_following(
)
.await?;
Ok(Json(ActorListResponse {
actors: identities
.into_iter()
.map(social_actor_to_dto)
.collect(),
actors: identities.into_iter().map(social_actor_to_dto).collect(),
}))
}
@@ -293,10 +296,7 @@ pub async fn get_followers(
)
.await?;
Ok(Json(ActorListResponse {
actors: identities
.into_iter()
.map(social_actor_to_dto)
.collect(),
actors: identities.into_iter().map(social_actor_to_dto).collect(),
}))
}
@@ -314,10 +314,7 @@ pub async fn get_user_following(
)
.await?;
Ok(Json(ActorListResponse {
actors: identities
.into_iter()
.map(social_actor_to_dto)
.collect(),
actors: identities.into_iter().map(social_actor_to_dto).collect(),
}))
}
@@ -335,10 +332,7 @@ pub async fn get_user_followers(
)
.await?;
Ok(Json(ActorListResponse {
actors: identities
.into_iter()
.map(social_actor_to_dto)
.collect(),
actors: identities.into_iter().map(social_actor_to_dto).collect(),
}))
}
@@ -525,10 +519,7 @@ pub async fn get_pending_followers(
)
.await?;
Ok(Json(ActorListResponse {
actors: identities
.into_iter()
.map(social_actor_to_dto)
.collect(),
actors: identities.into_iter().map(social_actor_to_dto).collect(),
}))
}
@@ -563,7 +554,9 @@ pub async fn follow_remote_user(
&deps,
application::social::commands::FollowCommand {
follower_id: user_id.value(),
target: SocialIdentity::Remote { actor_url: form.handle },
target: SocialIdentity::Remote {
actor_url: form.handle,
},
},
)
.await
@@ -604,7 +597,9 @@ pub async fn unfollow_remote_user(
&deps,
application::social::commands::UnfollowCommand {
follower_id: user_id.value(),
target: SocialIdentity::Remote { actor_url: form.actor_url },
target: SocialIdentity::Remote {
actor_url: form.actor_url,
},
},
)
.await
@@ -645,7 +640,9 @@ pub async fn accept_follower_html(
&deps,
application::social::commands::AcceptFollowCommand {
owner_id: user_id.value(),
requester: SocialIdentity::Remote { actor_url: form.actor_url },
requester: SocialIdentity::Remote {
actor_url: form.actor_url,
},
},
)
.await
@@ -680,7 +677,9 @@ pub async fn reject_follower_html(
&deps,
application::social::commands::RejectFollowCommand {
owner_id: user_id.value(),
requester: SocialIdentity::Remote { actor_url: form.actor_url },
requester: SocialIdentity::Remote {
actor_url: form.actor_url,
},
},
)
.await
@@ -706,7 +705,9 @@ pub async fn get_followers_collection(
if accept.contains("application/activity+json") || accept.contains("application/ld+json") {
let page = params.get("page").and_then(|p| p.parse::<u32>().ok());
return match state
.app_ctx.services.ap_service
.app_ctx
.services
.ap_service
.followers_collection_json(user_id, page)
.await
{
@@ -737,7 +738,9 @@ pub async fn get_following_collection(
if accept.contains("application/activity+json") || accept.contains("application/ld+json") {
let page = params.get("page").and_then(|p| p.parse::<u32>().ok());
return match state
.app_ctx.services.ap_service
.app_ctx
.services
.ap_service
.following_collection_json(user_id, page)
.await
{
@@ -776,7 +779,9 @@ pub async fn get_following_page(
};
match application::social::get_following::execute(
&deps,
application::social::queries::GetFollowingQuery { user_id: user_id.value() },
application::social::queries::GetFollowingQuery {
user_id: user_id.value(),
},
)
.await
{
@@ -825,7 +830,9 @@ pub async fn get_followers_page(
};
match application::social::get_followers::execute(
&deps,
application::social::queries::GetFollowersQuery { user_id: user_id.value() },
application::social::queries::GetFollowersQuery {
user_id: user_id.value(),
},
)
.await
{
@@ -875,7 +882,9 @@ pub async fn remove_follower_html(
&deps,
application::social::commands::RemoveFollowerCommand {
owner_id: user_id.value(),
follower: SocialIdentity::Remote { actor_url: form.actor_url },
follower: SocialIdentity::Remote {
actor_url: form.actor_url,
},
},
)
.await
@@ -902,7 +911,13 @@ pub async fn get_blocked_domains_page(
let mut ctx = build_page_context(&state, Some(user_id), csrf.0).await;
ctx.page_title = "Blocked Domains — Movies Diary".to_string();
ctx.canonical_url = format!("{}/admin/blocked-domains", state.app_ctx.config.base_url);
match state.app_ctx.services.ap_service.get_blocked_domains().await {
match state
.app_ctx
.services
.ap_service
.get_blocked_domains()
.await
{
Ok(domains) => {
let entries: Vec<template_askama::BlockedDomainEntry> = domains
.into_iter()
@@ -940,7 +955,9 @@ pub async fn post_blocked_domain(
}
let reason = form.reason.as_deref().filter(|s| !s.trim().is_empty());
match state
.app_ctx.services.ap_service
.app_ctx
.services
.ap_service
.add_blocked_domain(&form.domain, reason)
.await
{
@@ -961,7 +978,13 @@ pub async fn post_remove_blocked_domain(
if crate::csrf::mismatch(&csrf, &form.csrf_token) {
return StatusCode::FORBIDDEN.into_response();
}
match state.app_ctx.services.ap_service.remove_blocked_domain(&form.domain).await {
match state
.app_ctx
.services
.ap_service
.remove_blocked_domain(&form.domain)
.await
{
Ok(()) => Redirect::to("/admin/blocked-domains").into_response(),
Err(e) => {
tracing::error!("remove_blocked_domain error: {:?}", e);
@@ -983,7 +1006,9 @@ pub async fn get_blocked_actors_page(
};
match application::social::get_blocked::execute(
&deps,
application::social::queries::GetBlockedQuery { user_id: user_id.value() },
application::social::queries::GetBlockedQuery {
user_id: user_id.value(),
},
)
.await
{
@@ -1032,7 +1057,9 @@ pub async fn post_block_actor_html(
&deps,
application::social::commands::BlockCommand {
blocker_id: user_id.value(),
target: SocialIdentity::Remote { actor_url: form.actor_url },
target: SocialIdentity::Remote {
actor_url: form.actor_url,
},
},
)
.await
@@ -1063,7 +1090,9 @@ pub async fn post_unblock_actor(
&deps,
application::social::commands::UnblockCommand {
blocker_id: user_id.value(),
target: SocialIdentity::Remote { actor_url: form.actor_url },
target: SocialIdentity::Remote {
actor_url: form.actor_url,
},
},
)
.await

View File

@@ -66,7 +66,15 @@ async fn wire_dependencies() -> anyhow::Result<(AppState, axum::Router)> {
let event_bus = EventBusBackend::from_env()?;
#[cfg(feature = "federation")]
let (event_publisher_arc, ap_router, ap_service, social_query, remote_watchlist_repo, social_command_arc, social_query_unified_arc) = {
let (
event_publisher_arc,
ap_router,
ap_service,
social_query,
remote_watchlist_repo,
social_command_arc,
social_query_unified_arc,
) = {
let (
activity_repo,
follow_repo,

View File

@@ -1,7 +1,9 @@
use std::sync::Arc;
use async_trait::async_trait;
use domain::{errors::DomainError, events::DomainEvent, ports::EventHandler, value_objects::SocialIdentity};
use domain::{
errors::DomainError, events::DomainEvent, ports::EventHandler, value_objects::SocialIdentity,
};
pub struct FollowBackfillHandler {
pub ap_service: Arc<dyn activitypub::ActivityPubPort>,