feat: account purge handler, tombstone cleanup, AP tombstone signing + 410
This commit is contained in:
44
crates/domain/src/ports/account_purge.rs
Normal file
44
crates/domain/src/ports/account_purge.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{errors::DomainError, value_objects::UserId};
|
||||
|
||||
/// Tells the fediverse that a local account is gone (an ActivityPub `Delete`
|
||||
/// to followers). Federation-off builds wire a no-op.
|
||||
///
|
||||
/// Delivery is asynchronous: the implementation may only enqueue deliveries.
|
||||
/// Those are signed later with the actor's keypair and the tombstoned user row,
|
||||
/// which is why the purge keeps both until the tombstone cleanup.
|
||||
#[async_trait]
|
||||
pub trait FederationDeletionNotifier: Send + Sync {
|
||||
async fn notify_deleted(&self, user: &UserId) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
/// Removes everything a deleted account owns, in one transaction, and strips
|
||||
/// the user row down to a data-free tombstone.
|
||||
///
|
||||
/// What stays: the username (retired for good), role, `deleted_at`, the
|
||||
/// `purged_at` stamp and the signing keypair, so queued federation deliveries
|
||||
/// can still be signed. `purge_expired_tombstones` removes those later.
|
||||
#[async_trait]
|
||||
pub trait AccountPurger: Send + Sync {
|
||||
/// Whether the account's data has already been purged.
|
||||
async fn is_purged(&self, user: &UserId) -> Result<bool, DomainError>;
|
||||
|
||||
/// Object-storage keys the account owns (avatar, banner). Still readable
|
||||
/// after `purge`, until `forget_images`.
|
||||
async fn owned_image_keys(&self, user: &UserId) -> Result<Vec<String>, DomainError>;
|
||||
|
||||
/// One transaction; every delete is "if present", so a re-run is a no-op.
|
||||
/// Frees the email. Does nothing for an account that is gone or purged.
|
||||
async fn purge(&self, user: &UserId) -> Result<(), DomainError>;
|
||||
|
||||
/// Drops the image references once their objects are deleted.
|
||||
async fn forget_images(&self, user: &UserId) -> Result<(), DomainError>;
|
||||
|
||||
/// Deletes tombstones (user row and keypair) purged before `cutoff`.
|
||||
/// Returns how many were removed.
|
||||
async fn purge_expired_tombstones(
|
||||
&self,
|
||||
cutoff: chrono::NaiveDateTime,
|
||||
) -> Result<u64, DomainError>;
|
||||
}
|
||||
@@ -8,6 +8,11 @@ use crate::{
|
||||
|
||||
#[async_trait]
|
||||
pub trait ApDocumentPort: Send + Sync {
|
||||
/// Whether the actor belonged to a deleted account: its documents are
|
||||
/// answered `410 Gone` instead of being served.
|
||||
async fn actor_is_gone(&self, _user_id: Uuid) -> Result<bool, DomainError> {
|
||||
Ok(false)
|
||||
}
|
||||
async fn actor_json(&self, user_id: &str) -> Result<String, DomainError>;
|
||||
async fn followers_collection_json(
|
||||
&self,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod account_purge;
|
||||
pub mod auth;
|
||||
pub mod diary;
|
||||
pub mod events;
|
||||
@@ -21,6 +22,7 @@ pub mod social;
|
||||
pub mod watchlist;
|
||||
pub mod wrapup;
|
||||
|
||||
pub use account_purge::*;
|
||||
pub use auth::*;
|
||||
pub use diary::*;
|
||||
pub use events::*;
|
||||
|
||||
@@ -34,6 +34,18 @@ impl super::RemoteWatchlistRepository for NoopRemoteWatchlistRepository {
|
||||
}
|
||||
}
|
||||
|
||||
// ── NoopFederationDeletionNotifier ───────────────────────────────────────────
|
||||
|
||||
/// Federation compiled out: nobody remote to tell.
|
||||
pub struct NoopFederationDeletionNotifier;
|
||||
|
||||
#[async_trait]
|
||||
impl super::FederationDeletionNotifier for NoopFederationDeletionNotifier {
|
||||
async fn notify_deleted(&self, _: &UserId) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ── NoopSocialCommand ────────────────────────────────────────────────────────
|
||||
|
||||
pub struct NoopSocialCommand;
|
||||
|
||||
Reference in New Issue
Block a user