purge: skip broadcasts of deleted authors, purge outbox log, guards, 410 inbox test
This commit is contained in:
@@ -34,9 +34,19 @@ pub const PURGED_USER_TABLES: &[(&str, &str)] = &[
|
||||
/// the keypair is needed to sign queued federation deliveries.
|
||||
pub const RETAINED_USER_TABLES: &[&str] = &["ap_local_actors"];
|
||||
|
||||
/// Tables with no user column whose rows hang off the user's content or actor
|
||||
/// URL; the purge clears them by URL.
|
||||
pub const URL_KEYED_USER_TABLES: &[&str] = &["ap_announces", "ap_followers", "ap_following"];
|
||||
/// Columns that identify the user by actor or content URL rather than by id.
|
||||
/// The purge matches rows on them; the guard test asserts each still exists so
|
||||
/// a rename cannot silently stop the purge from clearing them.
|
||||
pub const URL_KEYED_USER_COLUMNS: &[(&str, &str)] = &[
|
||||
("ap_announces", "object_url"),
|
||||
("ap_announces", "actor_url"),
|
||||
("ap_followers", "remote_actor_url"),
|
||||
("ap_following", "remote_actor_url"),
|
||||
];
|
||||
|
||||
/// Columns holding serialized events and activities that carry the user's id
|
||||
/// (the outbox log keeps finished rows, including authored activity JSON).
|
||||
pub const PAYLOAD_KEYED_USER_COLUMNS: &[(&str, &str)] = &[("event_queue", "payload")];
|
||||
|
||||
pub struct SqliteAccountPurger {
|
||||
pool: SqlitePool,
|
||||
@@ -116,6 +126,22 @@ impl AccountPurger for SqliteAccountPurger {
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
|
||||
// The outbox log: events and signed activities authored as this user.
|
||||
// The `UserDeleted` row (which triggered this run) and the pending
|
||||
// `Delete` deliveries must stay; both go with the tombstone.
|
||||
sqlx::query(
|
||||
"DELETE FROM event_queue
|
||||
WHERE instr(payload, ?1) > 0
|
||||
AND event_type != 'UserDeleted'
|
||||
AND NOT (event_type = 'FederationDeliveryRequested'
|
||||
AND status IN ('pending', 'processing')
|
||||
AND instr(payload, '\"type\":\"Delete\"') > 0)",
|
||||
)
|
||||
.bind(&id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
|
||||
// Other accounts' follows of this one.
|
||||
for table in ["ap_followers", "ap_following"] {
|
||||
sqlx::query(&format!("DELETE FROM {table} WHERE remote_actor_url = ?"))
|
||||
@@ -158,6 +184,22 @@ impl AccountPurger for SqliteAccountPurger {
|
||||
) -> Result<u64, DomainError> {
|
||||
let cutoff = cutoff.format(TIMESTAMP_FORMAT).to_string();
|
||||
let mut tx = self.pool.begin().await.map_err(map_sqlx_error)?;
|
||||
let expiring: Vec<String> = sqlx::query_scalar(
|
||||
"SELECT id FROM users WHERE purged_at IS NOT NULL AND purged_at < ?",
|
||||
)
|
||||
.bind(&cutoff)
|
||||
.fetch_all(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
for id in &expiring {
|
||||
// What the purge had to leave: the `UserDeleted` row and the
|
||||
// `Delete` deliveries.
|
||||
sqlx::query("DELETE FROM event_queue WHERE instr(payload, ?) > 0")
|
||||
.bind(id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
}
|
||||
for table in RETAINED_USER_TABLES {
|
||||
sqlx::query(&format!(
|
||||
"DELETE FROM {table} WHERE user_id IN (
|
||||
|
||||
Reference in New Issue
Block a user