archivmail purge ist ein neuer CLI-Befehl, der Mails mit abgelaufener
retain_until löscht, aus dem Suchindex entfernt und pro Mail einen
Audit-Eintrag (mail_purged) schreibt — analog zu Pilers purge.sh, nachts
03:40 Uhr über deploy/cron.d/archivmail. Nur Mails mit explizit gesetztem
und abgelaufenem retain_until werden angefasst; ohne retain_until bleibt
alles unberührt, die Löschsperre (PROJ-34) greift weiterhin.
Beim Testen aufgedeckt: Store.Delete() entfernte die Datei vor dem
DB-Delete und verschluckte den Fehler, wenn email_refs/email_attachments
per Fremdschlüssel die Löschung blockierten — Ergebnis war ein DB-Eintrag
ohne zugehörige Datei. Jetzt läuft die DB-Löschung (inkl. abhängiger
Zeilen) zuerst in einer Transaktion, die Datei wird erst nach
erfolgreichem Commit entfernt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fuehrt archiving_rules ein (PROJ-43-Basis: Tabelle + CRUD-API + Admin-UI) und
erweitert die Retention-Logik (PROJ-34) um Regel-basierte Fristen, eine
globale Mindestfrist (min_retention_days) sowie Nachvollziehbarkeit der
Frist-Quelle (retain_until_source) in API und Mail-Detailansicht.
storage.loadKey() startet bei fehlendem/unlesbarem/ungültigem Keyfile
weiterhin unverschlüsselt (kein Hard-Fail), aber:
- einmalige WARN-Logzeile beim Start mit konkretem Grund
- neuer Healthcheck-Prüfpunkt "Encryption" in archivmail status
- Dashboard-API liefert encryption.enabled
- README: GoBD-Hinweis zu storage.keyfile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mailparser: weitere Layouts (Timezone +02:00 mit Doppelpunkt, ohne Sekunden)
storage: GetReceivedAts() für Batch-Lookup von received_at
search_handlers: received_at als Fallback wenn pm.Date.IsZero()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DB-Schema bekommt eine idempotente ocr_chars BIGINT-Spalte (Default 0).
SetOCRResult schreibt status und chars atomar; GetOCRMeta liest beide
mit COALESCE-Defaults. Der OCR-Worker ersetzt jeden SetOCRStatus-Call
durch SetOCRResult und uebergibt die extrahierte Zeichenzahl bei 'done'.
Sprint 1: Emails werden vor AES-256-GCM optional gzip-komprimiert (compress: true).
Magic-Byte 0x01 als Prefix ermöglicht backward-kompatibles Load() für Legacy-Dateien.
Neue DB-Tabelle storage_objects trackt Kompressions-Metadaten.
Sprint 2: Attachments werden via SHA-256 dedupliziert — gleicher Anhang in N Mails
wird nur einmal gespeichert. Neue Tabellen: attachments, email_attachments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- auditor-Rolle sieht jetzt Mails wo tenant_id IS NULL und kein
email_refs-Eintrag existiert (statt nur eigene Mails)
- Neues storage.IsWithoutTenant() für effizienten Direktzugriff
- Neues storage.GetAllIDsWithoutTenant() für Suche + ZIP-Export
- Konsistente Prüfung in Search, GetMail, GetAttachment, GetRaw, Export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
storage.Save(): retention_days eines Mandanten muss explizit > 0 sein.
Globale config greift nie automatisch auf Mandanten-Mails.
Mails ohne Mandant: globale config als Fallback (unveraendert).
Frontend: Hinweis und Labels klargestellt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Systemauslastungs-Sektion wird immer gerendert (nicht nur bei Erfolg)
- Fehlermeldung wenn /api/admin/system/stats nicht erreichbar ist
- Feature-Status auf In Review gesetzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>