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> |
||
|---|---|---|
| apps | ||
| bundles | ||
| data | ||
| docs | ||
| migrations | ||
| ops | ||
| packages | ||
| .env.example | ||
| .gitignore | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.base.json | ||
Kundencenter
Modulares Kundencenter für Hosting, Server und Lizenzen (Modularer Monolith, TypeScript).
| Prozess | Pfad | Port (nur 127.0.0.1) | Aufgabe |
|---|---|---|---|
kc-api |
apps/api |
4100 | Fastify-API (/v1/*), Module, Policy, Audit |
kc-worker |
apps/worker |
4102 (Health) | Persistente Aufträge, Discord-Bot |
kc-web |
apps/web |
4101 | Next.js-Oberfläche, Proxy /api/* → API |
Stand (Grundsystem)
Login mit Passwort + TOTP, Wiederherstellungscodes, Sitzungsverwaltung, Passwort-Reset, Einladungen, Benutzerverwaltung (Mitarbeiterrollen),
Kunden/Organisationen anlegen und verwalten, Rechnungsanschrift, Audit-Protokoll mit Hash-Kette, persistente Job-Queue, Discord-Bot (optional).
Produkte (versioniert), Bestellungen mit Freigabe und unveränderlichem Preis-Snapshot, Verträge mit Kündigung/Verlängerung, Provisionierung über Connectoren (docs/produkte-bestellungen-vertraege.md), Connector-Framework (docs/connector-vertrag.md).
Support-Tickets (je Kunde, mit internen Notizen für Personal, Dateianhänge als Bild/PDF, Discord-Benachrichtigung).
Rechnungen (docs/rechnungen.md): Entwurf → Ausstellen (unveränderlich) → Bezahlt/Storno, PDF-Erzeugung, Firmenstammdaten unter Einstellungen → Firma.
Noch nicht vorhanden: Zahlungsanbieter-Anbindung, automatische wiederkehrende Rechnungsstellung, E-Mail-Versand von Rechnungen, Selbstregistrierung, Plesk-Connector, Domain-Registrierung über eine Registrar-API (aktuell manuell über die Domain-Aufstellung).
Modularität
API-Module liegen in apps/api/src/modules/<name> und implementieren KcModule (core/module.ts).
Sie werden in modules/index.ts eingetragen, registrieren Routen und Rechte und sprechen nur über core/* und Jobs miteinander.
Neue Fähigkeiten (Connectoren, Rechnungen, Tickets) kommen als weitere Module hinzu.
Konfiguration
Geheimnisse liegen nicht im Repo, sondern in /etc/kundencenter/*.env (chmod 600): db.env, app.env, discord.env, optional SMTP in app.env.
Vorlage: .env.example. Wichtig: KC_BASE_URL muss exakt der Browser-URL entsprechen (Origin-Prüfung gegen CSRF).
Betrieb
systemctl status kc-api kc-worker kc-web # Dienste (Autostart, Restart=always)
journalctl -u kc-api -f # Logs
pnpm install && pnpm build # Update: danach chown -R kundencenter und systemctl restart kc-*
pnpm migrate # Migrationen (migrations/*.sql, einmalig je Datei)
pnpm cli create-superadmin --email=… --name=… --password=…
Health: GET :4100/v1/health, GET :4100/v1/ready, GET :4102/health.
Tests
cd apps/api && pnpm test – Integrationstests gegen eine separate Datenbank kundencenter_test (wird bei jedem Lauf neu erstellt).
Abgedeckt: Login, Sperre, CSRF, 2FA-Pflicht, Sitzungen, Kundenanlage, Mandantentrennung, Rechte, Audit-Kette/Maskierung.
Discord-Bot
In /etc/kundencenter/discord.env setzen: DISCORD_BOT_TOKEN, DISCORD_GUILD_ID, DISCORD_ADMIN_CHANNEL_ID, DISCORD_STAFF_USER_IDS (kommagetrennt), dann systemctl restart kc-worker.
Befehle: /kc-status, /kc-kunde suche:<Text> (nur für freigegebene Discord-User, Antworten nur für sie sichtbar). In Kanäle gehen nur Ereignisse ohne personenbezogene Daten (z. B. „Neuer Kunde angelegt: K-10001“).
Backup / Restore (Datenbank)
set -a; . /etc/kundencenter/db.env; set +a
MYSQL_PWD=$DB_PASSWORD mysqldump -h127.0.0.1 -u$DB_USER --single-transaction --routines $DB_NAME | gzip > kundencenter-$(date +%F).sql.gz
gunzip -c kundencenter-DATUM.sql.gz | MYSQL_PWD=$DB_PASSWORD mysql -h127.0.0.1 -u$DB_USER $DB_NAME # Restore in leere DB
Automatisiertes, verschlüsseltes Backup mit Restore-Test ist noch offen (siehe Plane).