Commit graph

4 commits

Author SHA1 Message Date
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
Kundencenter
c8da9362a7 feat: Discord-Bot über die Oberfläche konfigurierbar
Bisher kam die Discord-Konfiguration (Token, Server-ID, Kanal-ID,
erlaubte Nutzer-IDs) ausschließlich aus Umgebungsvariablen. Jetzt
unter Einstellungen > Discord einstellbar, inkl. Schritt-für-Schritt-
Anleitung zum Einrichten einer Discord-Anwendung/eines Bots (Token
erzeugen, einladen, IDs ermitteln).

- Migration 026: discord_settings (Token verschlüsselt, wie SMTP/IMAP).
- apps/worker/src/discord.ts liest die Einstellungen jetzt aus der DB
  statt aus process.env; syncDiscord() läuft periodisch (60s) und
  verbindet automatisch neu, wenn sich Token/Server/Aktivierung
  geändert haben (Trennung von Verbindungs- und Laufzeitdaten: Kanal-ID
  und erlaubte Nutzer werden bei jeder Nutzung frisch aus der DB
  gelesen, ohne Neuverbindung).
- Neues API-Modul apps/api/src/modules/discord: Einstellungen lesen/
  schreiben, Token- und Testnachricht-Prüfung direkt über die Discord-
  REST-API (unabhängig vom laufenden Bot-Prozess im Worker).
- Rechte discord.read (Admin+) / discord.write (Superadmin), wie beim
  E-Mail-Modul.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 23:28:24 +02:00
Kundencenter
fe18bf0bce feat: IMAP-Posteingang fürs Ticketsystem (Mail-Antworten -> Tickets)
Neues Postfach lässt sich unter Einstellungen > E-Mail > IMAP-Posteingang
einrichten (Host/Port/Verschlüsselung/Zugangsdaten/Ordner, An/Aus,
Verbindungstest). Der Worker ruft es alle 2 Minuten ab (mailbox.ts) und
ordnet eingehende Mails zu:

1. Betreff enthält eine Ticketnummer (T-123) UND Absender gehört
   nachweislich zum Kunden dieses Tickets -> wird als Kundennachricht
   angehängt (Status pending_staff, Discord-Benachrichtigung).
2. Sonst, wenn der Absender ein bekannter aktiver Kunde ist -> neues
   Ticket wird automatisch unter seinem Kundenkonto angelegt.
3. Sonst (unbekannter/nicht verifizierbarer Absender) -> landet im
   neuen Posteingang (/admin/inbox, UI unter /admin/posteingang) zur
   manuellen Zuordnung: Personal wählt einen Kunden (legt Ticket an),
   hängt die Mail an ein bestehendes Ticket, oder ignoriert sie.

Beim ersten Aktivieren wird kein Mailbox-Bestand verarbeitet, nur ab
dann neu Eingehendes (last_uid wird auf den aktuellen Stand gesetzt).

Migration 024: imap_settings (Verbindung, verschlüsseltes Passwort,
last_uid/last_error), inbox_messages (Warteschlange). Rechte:
email.read/write für die IMAP-Verbindung (wie SMTP), tickets.read/write
für den Posteingang (Zuordnung ist eine Ticket-Aktion, daher im
Tickets-Modul statt eines eigenen Moduls, um keine Modulgrenzen zu
verletzen).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 17:35:58 +02:00
Kundencenter
4763548bfb Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00