feat(PROJ-56c): Purge-Cron löscht nur explizit markierte Mails

Der automatische Purge-Cron darf nicht allein anhand des abgelaufenen
retain_until löschen — eine Mail muss zusätzlich von einem Admin im UI
zur Löschung markiert worden sein. Dafür: neue Spalten
marked_for_deletion(_by/_at) auf emails, Store.ListExpiredMarkedMailIDs()
(retain_until abgelaufen UND markiert) für den Cron-Pfad, und
Store.SetMarkedForDeletion() zum Setzen/Löschen der Markierung.

Neue Endpoints (domain_admin+, tenant-scoped):
- GET  /api/admin/retention/expired      Metadaten abgelaufener Mails
                                          (kein Body-Zugriff, SEC-29)
- PUT  /api/admin/mails/{id}/mark-deletion  Markierung setzen/entfernen,
                                          mit Audit-Log-Eintrag

RetentionTab.tsx zeigt abgelaufene Mails mit Checkbox zum Markieren.
Der bestehende manuelle "Jetzt löschen"-Button (/api/admin/purge) bleibt
unverändert und löscht weiterhin alle abgelaufenen Mails auf einen Klick —
nur der unbeaufsichtigte Cron-Job ist jetzt auf markierte Mails beschränkt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-06-24 16:40:18 +02:00
co-authored by Claude Sonnet 4.6
parent 586af2478c
commit b3ff8e6cf9
6 changed files with 390 additions and 10 deletions
+4
View File
@@ -235,6 +235,10 @@ func (s *Server) routes() {
s.mux.HandleFunc("POST /api/admin/archiving-rules", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleCreateArchivingRule)))
s.mux.HandleFunc("PUT /api/admin/archiving-rules/{id}", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleUpdateArchivingRule)))
s.mux.HandleFunc("DELETE /api/admin/archiving-rules/{id}", s.auth(s.requireRole(userstore.RoleSuperAdmin, s.handleDeleteArchivingRule)))
// PROJ-56c: pro-Mail Löschmarkierung — domain_admin+, tenant-scoped (kein
// Mail-Lesezugriff nötig, daher requireRole statt requireMailAccess).
s.mux.HandleFunc("GET /api/admin/retention/expired", s.authAdmin(s.handleListExpiredMails))
s.mux.HandleFunc("PUT /api/admin/mails/{id}/mark-deletion", s.authAdmin(s.handleSetMarkedForDeletion))
// PROJ-50: DSGVO Löschersuchen — admin (manage) / auditor (read-only),
// role enforced inside the handlers.