feat: account deletion request and active-user check

This commit is contained in:
2026-10-26 02:11:44 +01:00
parent 83bb5d9847
commit 0b5d18c4ee
27 changed files with 577 additions and 151 deletions

View File

@@ -26,6 +26,8 @@ pub fn create_image_ref(pool: SqlitePool) -> (Arc<dyn ImageRefCommand>, Arc<dyn
#[async_trait]
impl ImageRefCommand for SqliteImageRefAdapter {
// Tombstoned users (`deleted_at` set) are intentionally included: their
// avatar stays referenced, and so protected from GC, until the purge.
async fn swap(&self, old_key: &str, new_key: &str) -> Result<(), DomainError> {
let mut tx = self
.pool
@@ -52,6 +54,8 @@ impl ImageRefCommand for SqliteImageRefAdapter {
#[async_trait]
impl ImageRefQuery for SqliteImageRefAdapter {
// Tombstoned users are intentionally visible here until the purge removes
// the row, so their avatar is not garbage-collected early.
async fn list_keys(&self) -> Result<Vec<String>, DomainError> {
let rows: Vec<(String,)> = sqlx::query_as(
"SELECT avatar_path FROM users WHERE avatar_path IS NOT NULL

View File

@@ -171,7 +171,7 @@ impl UserRepository for SqliteUserRepository {
.map_err(crate::shared::map_sqlx_error)?;
let stamped =
sqlx::query("UPDATE users SET deleted_at = ? WHERE id = ? AND deleted_at IS NULL")
.bind(Utc::now().to_rfc3339())
.bind(Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string())
.bind(&id_str)
.execute(&mut *tx)
.await