feat: implement TASK-WP1-05 account deletion

This commit is contained in:
suyx
2026-07-28 19:07:11 +08:00
parent 03f1509de7
commit 2358f97e5c
20 changed files with 2565 additions and 13 deletions
+46
View File
@@ -0,0 +1,46 @@
import { createRequire } from "node:module";
import type BetterSqlite3 from "better-sqlite3";
const require = createRequire(import.meta.url);
const Database = require("better-sqlite3") as typeof BetterSqlite3;
export class RetentionCleanup {
private readonly clock: () => number;
private readonly database: BetterSqlite3.Database;
private retentionPurgeActive = false;
constructor(input: { clock?: () => number; databasePath: string }) {
this.clock = input.clock ?? Date.now;
const nativeBinding = process.env.DADA_SQLITE_NATIVE_BINDING;
this.database = new Database(input.databasePath, nativeBinding ? { nativeBinding } : undefined);
this.database.pragma("foreign_keys = ON");
this.database.pragma("busy_timeout = 5000");
this.database.function("dada_allow_privacy_purge", { deterministic: false }, () => 0);
this.database.function("dada_allow_retention_purge", { deterministic: false }, () => this.retentionPurgeActive ? 1 : 0);
}
purgeExpired() {
const now = this.clock();
this.database.exec("BEGIN IMMEDIATE");
try {
this.retentionPurgeActive = true;
const privateAccess = this.database.prepare("DELETE FROM private_content_access_logs WHERE expires_at <= ?").run(now);
const anonymous = this.database.prepare("DELETE FROM anonymous_retained_events WHERE expires_at <= ?").run(now);
this.retentionPurgeActive = false;
this.database.exec("COMMIT");
return {
anonymous_events: anonymous.changes,
private_access_logs: privateAccess.changes,
};
} catch (error) {
this.retentionPurgeActive = false;
if (this.database.inTransaction) this.database.exec("ROLLBACK");
throw error;
}
}
close() {
this.database.close();
}
}