feat(domain): RemoteActor fields, RemoteNote, FetchRemoteActorPosts event, fetch_outbox_page port

This commit is contained in:
2026-05-14 22:08:26 +02:00
parent cbfaeb95ac
commit 70fc4fbcd0
9 changed files with 78 additions and 1 deletions

View File

@@ -1394,6 +1394,11 @@ impl domain::ports::FederationActionPort for ActivityPubService {
public_key: actor.public_key_pem.clone(),
avatar_url: actor.avatar_url.as_ref().map(|u| u.to_string()),
last_fetched_at: actor.last_refreshed_at,
bio: None,
banner_url: None,
also_known_as: None,
outbox_url: None,
attachment: vec![],
})
}
@@ -1511,6 +1516,16 @@ impl domain::ports::FederationActionPort for ActivityPubService {
serde_json::to_string(&obj)
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))
}
async fn fetch_outbox_page(
&self,
_outbox_url: &str,
_page: u32,
) -> Result<Vec<domain::models::remote_note::RemoteNote>, domain::errors::DomainError> {
Err(domain::errors::DomainError::Internal(
"not implemented".into(),
))
}
}
#[cfg(test)]

View File

@@ -68,6 +68,10 @@ pub enum EventPayload {
UserRegistered {
user_id: String,
},
FetchRemoteActorPosts {
actor_ap_url: String,
outbox_url: String,
},
}
impl EventPayload {
@@ -88,6 +92,7 @@ impl EventPayload {
Self::UserBlocked { .. } => "users.blocked",
Self::UserUnblocked { .. } => "users.unblocked",
Self::UserRegistered { .. } => "users.registered",
Self::FetchRemoteActorPosts { .. } => "federation.fetch_outbox",
}
}
}
@@ -197,6 +202,13 @@ impl From<&DomainEvent> for EventPayload {
DomainEvent::UserRegistered { user_id } => Self::UserRegistered {
user_id: user_id.to_string(),
},
DomainEvent::FetchRemoteActorPosts {
actor_ap_url,
outbox_url,
} => Self::FetchRemoteActorPosts {
actor_ap_url: actor_ap_url.clone(),
outbox_url: outbox_url.clone(),
},
}
}
}
@@ -315,6 +327,13 @@ impl TryFrom<EventPayload> for DomainEvent {
EventPayload::UserRegistered { user_id } => DomainEvent::UserRegistered {
user_id: UserId::from_uuid(parse_uuid(&user_id, "user_id")?),
},
EventPayload::FetchRemoteActorPosts {
actor_ap_url,
outbox_url,
} => DomainEvent::FetchRemoteActorPosts {
actor_ap_url,
outbox_url,
},
})
}
}

View File

@@ -45,6 +45,6 @@ impl RemoteActorRepository for PgRemoteActorRepository {
"SELECT url,handle,display_name,inbox_url,shared_inbox_url,public_key,avatar_url,last_fetched_at FROM remote_actors WHERE url=$1"
).bind(url).fetch_optional(&self.pool).await
.map_err(|e| DomainError::Internal(e.to_string()))
.map(|o| o.map(|r| RemoteActor { url: r.url, handle: r.handle, display_name: r.display_name, inbox_url: r.inbox_url, shared_inbox_url: r.shared_inbox_url, public_key: r.public_key, avatar_url: r.avatar_url, last_fetched_at: r.last_fetched_at }))
.map(|o| o.map(|r| RemoteActor { url: r.url, handle: r.handle, display_name: r.display_name, inbox_url: r.inbox_url, shared_inbox_url: r.shared_inbox_url, public_key: r.public_key, avatar_url: r.avatar_url, last_fetched_at: r.last_fetched_at, bio: None, banner_url: None, also_known_as: None, outbox_url: None, attachment: vec![] }))
}
}

View File

@@ -60,6 +60,10 @@ pub enum DomainEvent {
UserRegistered {
user_id: UserId,
},
FetchRemoteActorPosts {
actor_ap_url: String,
outbox_url: String,
},
}
pub struct EventEnvelope {

View File

@@ -2,6 +2,7 @@ pub mod api_key;
pub mod feed;
pub mod notification;
pub mod remote_actor;
pub mod remote_note;
pub mod social;
pub mod tag;
pub mod thought;

View File

@@ -10,4 +10,9 @@ pub struct RemoteActor {
pub public_key: String,
pub avatar_url: Option<String>,
pub last_fetched_at: DateTime<Utc>,
pub bio: Option<String>,
pub banner_url: Option<String>,
pub also_known_as: Option<String>,
pub outbox_url: Option<String>,
pub attachment: Vec<(String, String)>,
}

View File

@@ -0,0 +1,10 @@
use chrono::{DateTime, Utc};
#[derive(Debug, Clone)]
pub struct RemoteNote {
pub ap_id: String,
pub content: String,
pub published: DateTime<Utc>,
pub sensitive: bool,
pub content_warning: Option<String>,
}

View File

@@ -209,6 +209,11 @@ pub trait FederationActionPort: Send + Sync {
user_id: &UserId,
page: Option<u32>,
) -> Result<String, DomainError>;
async fn fetch_outbox_page(
&self,
outbox_url: &str,
page: u32,
) -> Result<Vec<crate::models::remote_note::RemoteNote>, DomainError>;
}
#[async_trait]

View File

@@ -567,6 +567,14 @@ impl FederationActionPort for TestStore {
) -> Result<String, DomainError> {
Err(DomainError::NotFound)
}
async fn fetch_outbox_page(
&self,
_outbox_url: &str,
_page: u32,
) -> Result<Vec<crate::models::remote_note::RemoteNote>, DomainError> {
Ok(vec![])
}
}
#[async_trait]
@@ -833,6 +841,16 @@ mod federation_port_tests {
let err = store.actor_json(&UserId::new()).await.unwrap_err();
assert!(matches!(err, DomainError::NotFound));
}
#[tokio::test]
async fn test_store_fetch_outbox_returns_empty() {
let store = TestStore::default();
let notes = store
.fetch_outbox_page("https://example.com/outbox", 1)
.await
.unwrap();
assert!(notes.is_empty());
}
}
#[cfg(test)]