fix(admin): repair system maintenance controls

Implement server-side searchable user filtering for usage records, including backend search parameters and a shared frontend selector with loading, empty, and pinned-selected states.

Fix announcement deletion by cascading announcement read rows through a Postgres migration and defensive repository cleanup across supported backends.

Wire admin system purge and cleanup endpoints to real data deletion/maintenance flows, improve DataManagement messages, rebuild stats after stats purge, and add runtime OAuth token refresh maintenance when enabled.

Verified with rust-ci equivalent checks: cargo fmt, split clippy, split cargo tests, SQLite/Postgres/MySQL smoke tests, plus frontend npm ci, build, pages build, type-check, and targeted usage selector tests.
This commit is contained in:
Entropy.Xu
2026-05-07 21:26:40 +08:00
parent 2b439094c5
commit dd8c2ebec6
37 changed files with 2836 additions and 103 deletions
@@ -218,7 +218,14 @@ impl AnnouncementWriteRepository for InMemoryAnnouncementReadRepository {
.expect("announcement repository lock");
let original_len = announcements.len();
announcements.retain(|announcement| announcement.id != announcement_id);
Ok(announcements.len() != original_len)
let deleted = announcements.len() != original_len;
if deleted {
self.announcement_reads
.write()
.expect("announcement reads repository lock")
.retain(|(_, read_announcement_id)| read_announcement_id != announcement_id);
}
Ok(deleted)
}
async fn mark_announcement_as_read(
@@ -233,12 +233,19 @@ WHERE id = ?
}
async fn delete_announcement(&self, announcement_id: &str) -> Result<bool, DataLayerError> {
let mut tx = self.pool.begin().await.map_sql_err()?;
sqlx::query("DELETE FROM announcement_reads WHERE announcement_id = ?")
.bind(announcement_id)
.execute(&mut *tx)
.await
.map_sql_err()?;
let rows_affected = sqlx::query("DELETE FROM announcements WHERE id = ?")
.bind(announcement_id)
.execute(&self.pool)
.execute(&mut *tx)
.await
.map_sql_err()?
.rows_affected();
tx.commit().await.map_sql_err()?;
Ok(rows_affected > 0)
}
@@ -163,6 +163,10 @@ const DELETE_ANNOUNCEMENT_SQL: &str = r#"
DELETE FROM announcements
WHERE id = $1
"#;
const DELETE_ANNOUNCEMENT_READS_SQL: &str = r#"
DELETE FROM announcement_reads
WHERE announcement_id = $1
"#;
const MARK_ANNOUNCEMENT_AS_READ_SQL: &str = r#"
INSERT INTO announcement_reads (
@@ -295,11 +299,18 @@ impl AnnouncementWriteRepository for SqlxAnnouncementReadRepository {
}
async fn delete_announcement(&self, announcement_id: &str) -> Result<bool, DataLayerError> {
let result = sqlx::query(DELETE_ANNOUNCEMENT_SQL)
let mut tx = self.pool.begin().await.map_postgres_err()?;
sqlx::query(DELETE_ANNOUNCEMENT_READS_SQL)
.bind(announcement_id)
.execute(&self.pool)
.execute(&mut *tx)
.await
.map_postgres_err()?;
let result = sqlx::query(DELETE_ANNOUNCEMENT_SQL)
.bind(announcement_id)
.execute(&mut *tx)
.await
.map_postgres_err()?;
tx.commit().await.map_postgres_err()?;
Ok(result.rows_affected() > 0)
}
@@ -233,12 +233,19 @@ WHERE id = ?
}
async fn delete_announcement(&self, announcement_id: &str) -> Result<bool, DataLayerError> {
let mut tx = self.pool.begin().await.map_sql_err()?;
sqlx::query("DELETE FROM announcement_reads WHERE announcement_id = ?")
.bind(announcement_id)
.execute(&mut *tx)
.await
.map_sql_err()?;
let rows_affected = sqlx::query("DELETE FROM announcements WHERE id = ?")
.bind(announcement_id)
.execute(&self.pool)
.execute(&mut *tx)
.await
.map_sql_err()?
.rows_affected();
tx.commit().await.map_sql_err()?;
Ok(rows_affected > 0)
}