kundencenter/migrations
Kundencenter 3240e6df79 feat(audit): Aufbewahrungs-Policy mit Checkpoint-Löschung und CSV-Export
Zweiter Teil von #34: Aufbewahrungsfristen und Export waren offen. Die
DSGVO selbst schreibt keine feste Zahl vor (nur den Grundsatz der
Speicherbegrenzung, Art. 5 Abs. 1 lit. e) - daher eine eigene, änderbare
Policy statt eines hart codierten Gesetzeswerts. Voreinstellung 180 Tage
als verbreitete Praxis-Richtgröße für sicherheitsrelevante Protokolldaten.

- Migration 034: audit_settings (retention_days, Default 180) und
  audit_retention_checkpoints.
- purgeAuditRetention() (@kc/platform/audit): löscht Einträge jenseits
  der Frist. Da jede Zeile die vorherige mit hasht, würde einfaches
  Löschen die Kette brechen - ein Checkpoint (Hash der zuletzt gelöschten
  Zeile) macht sie trotzdem weiter überprüfbar. Bricht nur ab, wenn die
  Kette vorher schon fehlerhaft ist, oder bei einer Lücke zwischen
  gelöschten und verbleibenden Zeilen (unerwartete Zeitstempel-
  Reihenfolge). audit_events ist per Trigger unveränderlich (Migration
  033) - der DELETE-Trigger wird dafür kurz entfernt und sofort wieder
  angelegt, alles unter derselben Sperre wie audit() (re-entrant über
  dieselbe Verbindung, sonst Deadlock). Die Aufräumung selbst wird als
  eigener Audit-Eintrag protokolliert.
- verifyAuditChain() beginnt nach einer Aufräumung beim Checkpoint-Hash
  statt der Genesis-Null - nur wenn die neue erste Zeile auch wirklich
  genau darauf verweist, sonst bliebe eine echte Manipulation unentdeckt.
- Worker: täglicher automatischer Lauf. CLI: audit-purge zum manuellen
  Anstoßen. API: GET/PUT /admin/audit/settings, POST /admin/audit/purge
  (settings.write), GET /admin/audit/export.csv (audit.read, mit
  demselben Formel-Injection-Schutz wie der Rechnungs-Export). Web:
  Einstellungs-Card unter Audit-Protokoll mit Erklärtext zur DSGVO-
  Rechtsgrundlage, Frist-Eingabe, "Jetzt aufräumen" und Export-Link.

Verifiziert: Testsuite (59/59), echter Lauf mit künstlich vordatierten
(aber korrekt verketteten) Testzeilen gegen die Testdatenbank - Kette vor
und nach der Aufräumung intakt, Checkpoint korrekt genutzt, zweiter Lauf
idempotent. Echte HTTP-Endpunkte (inkl. CSRF) gegen Produktion getestet.
Volles Backup+Wiederherstellungstest danach weiterhin grün (201
Einträge).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 12:01:00 +02:00
..
001_core.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
002_connectors_resources.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
003_customer_type.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
004_catalog_orders_contracts.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
005_product_external_ref.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
006_price_basis.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
007_backup_settings_targets.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
008_org_legacy_ref.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
009_connector_error_time.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
010_domains.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
011_domain_rounding.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
012_domain_records.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
013_domain_cost_basis.sql Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00
014_tickets.sql Support-Tickets: Modul, Oberfläche, Discord-Benachrichtigung 2026-09-27 08:36:18 +02:00
015_ticket_attachments.sql Ticket-Anhänge: Bilder/PDF hochladen und anzeigen (geprüft anhand der Dateikennung) 2026-09-27 08:53:27 +02:00
016_invoices.sql Rechnungsmodul: Entwurf/Ausstellen/Bezahlt/Storno, PDF, Firmenstammdaten 2026-09-27 09:05:13 +02:00
017_ticket_domain_link.sql Tickets: Bezug zu Ressource oder Domain wählen, Verknüpfung direkt aus der Domain-Aufstellung 2026-09-27 16:40:14 +02:00
018_product_term_prices.sql Hosting-Produkte: Staffelpreise nach Laufzeit (1/3/6/12/24 Monate) 2026-09-27 18:35:34 +02:00
019_panel_credentials.sql KeyHelp: Panel-Passwort neu vergeben und verschlüsselt hinterlegen (verblurrt wie Lizenzschlüssel) 2026-09-27 21:35:15 +02:00
020_invoice_party_snapshots.sql Rechnungen: Absender-/Empfängerdaten beim Ausstellen einfrieren, Statuswechsel atomar (behebt Codex-Fund #95) 2026-09-28 10:29:00 +02:00
021_invoice_item_discount.sql Rechnungen: Rabatt je Position 2026-09-28 11:01:19 +02:00
022_mail_templates.sql feat: SMTP-Stack mit konfigurierbaren Mailvorlagen 2026-09-28 12:53:29 +02:00
023_mail_sender_profiles.sql feat: separate Absenderprofile für Kundencenter/Störungsticket/Rechnungen 2026-09-28 13:16:42 +02:00
024_imap_inbox.sql feat: IMAP-Posteingang fürs Ticketsystem (Mail-Antworten -> Tickets) 2026-09-28 17:35:58 +02:00
025_mail_link_buttons.sql feat(mail): HTML-Mails mit Link zum Ticket/zur Rechnung als Schaltfläche 2026-09-28 23:00:31 +02:00
026_discord_settings.sql feat: Discord-Bot über die Oberfläche konfigurierbar 2026-09-28 23:28:24 +02:00
027_discord_linking.sql feat(discord): Kunden-Kontoverknüpfung per Discord-OAuth 2026-09-28 23:41:04 +02:00
028_welcome_mail.sql feat(mail): Begrüßungsmail mit Zugang, Produkten, Preisen und Discord 2026-09-28 23:50:53 +02:00
029_discord_drop_staff_ids.sql refactor(discord): einheitliche Berechtigung über verknüpfte Konten 2026-09-29 00:12:10 +02:00
030_audit_hash_v2.sql fix(audit): GET_LOCK-Ergebnis prüfen, connector/ip in Hash-Kette 2026-09-29 09:56:46 +02:00
031_datev_export.sql feat(rechnungen): Export für den Steuerberater (CSV, PDF-ZIP, DATEV-Buchungsstapel) 2026-09-29 11:05:56 +02:00
032_jobs_lease.sql fix(worker): Lease/Heartbeat statt starrer 5-Minuten-Grenze bei Jobwiederaufnahme 2026-09-29 11:41:43 +02:00
033_audit_immutable.sql fix(audit): audit_events auf DB-Ebene unveränderlich machen (append-only) 2026-09-29 11:49:26 +02:00
034_audit_retention.sql feat(audit): Aufbewahrungs-Policy mit Checkpoint-Löschung und CSV-Export 2026-09-29 12:01:00 +02:00