fix(federation): fix 27 AP bugs, gaps, and inconsistencies
Round 1 — 18 bug fixes:
- remote likes/boosts now persist in engagement tables
- intern_remote_actor uses name@domain, expanded username to VARCHAR(255)
- PgRemoteActorRepository upsert/find now handles all fields
- update_following_status no longer a no-op, count_followers counts all
- accept/reject follow publishes event before DB mark (atomicity)
- fetch_outbox_page follows pagination via next links
- actor URL canonicalized to /users/{uuid}
- content_to_html escapes single quotes
- WebFinger accepts application/ld+json type
- try_from_ap accepts Article and Page object types
- feed SQL uses parameterized viewer UUID instead of format!
- content cap raised from 500 to 5000 chars
- also_known_as changed from Option<String> to Vec<String>
- connections fetch always triggers from page 1
Round 2 — 9 gap fixes:
- on_announce_removed handler deletes boost row on Undo(Announce)
- on_update handles Person/Service/Group actor profile updates
- sync_remote_actor_to_user syncs remote_actors → users on create/update
- FederationBlockPort: block_by_username sends Block activity to remote
- domain RemoteActor gains inbox_url, shared_inbox_url fields
- remote_actors attachment column (JSONB) with read/write
- .well-known/host-meta endpoint
- 256KB body limit on AP inbox routes
- outbox cleanup job (7-day retention, hourly sweep)
This commit is contained in:
@@ -50,6 +50,8 @@ pub async fn build(database_url: &str, base_url: &str, nats_url: &str) -> Worker
|
||||
base_url,
|
||||
None,
|
||||
Arc::new(postgres::tag::PgTagRepository::new(pool.clone())),
|
||||
Arc::new(postgres::like::PgLikeRepository::new(pool.clone())),
|
||||
Arc::new(postgres::boost::PgBoostRepository::new(pool.clone())),
|
||||
));
|
||||
let raw_ap_service = Arc::new(
|
||||
ActivityPubService::builder(base_url.to_string())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
mod dlq;
|
||||
mod factory;
|
||||
mod handlers;
|
||||
mod outbox_cleanup;
|
||||
mod outbox_relay;
|
||||
|
||||
use domain::{errors::DomainError, events::DomainEvent};
|
||||
@@ -39,6 +40,15 @@ async fn main() {
|
||||
.run(),
|
||||
);
|
||||
|
||||
tokio::spawn(
|
||||
outbox_cleanup::OutboxCleanup {
|
||||
pool: infra.pool.clone(),
|
||||
retention_days: 7,
|
||||
interval: std::time::Duration::from_secs(3600),
|
||||
}
|
||||
.run(),
|
||||
);
|
||||
|
||||
tracing::info!("Worker started, consuming events...");
|
||||
let mut stream = infra.message_source.messages();
|
||||
while let Some(result) = stream.next().await {
|
||||
|
||||
31
crates/worker/src/outbox_cleanup.rs
Normal file
31
crates/worker/src/outbox_cleanup.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use sqlx::PgPool;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct OutboxCleanup {
|
||||
pub pool: PgPool,
|
||||
pub retention_days: i64,
|
||||
pub interval: Duration,
|
||||
}
|
||||
|
||||
impl OutboxCleanup {
|
||||
pub async fn run(self) {
|
||||
loop {
|
||||
tokio::time::sleep(self.interval).await;
|
||||
match self.cleanup().await {
|
||||
Ok(n) if n > 0 => tracing::info!(deleted = n, "outbox cleanup: removed old events"),
|
||||
Err(e) => tracing::warn!(error = %e, "outbox cleanup failed"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn cleanup(&self) -> Result<u64, sqlx::Error> {
|
||||
let result = sqlx::query(
|
||||
"DELETE FROM outbox_events WHERE delivered = true AND delivered_at < NOW() - make_interval(days => $1)",
|
||||
)
|
||||
.bind(self.retention_days as i32)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
Ok(result.rows_affected())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user