Files
planer/src/server/audit.ts
T
Florian WildeandClaude Opus 5 e9a2b4b463 feat(bereiche): Bereiche verwalten mit Rollenprüfung und Audit
Bereiche mit Farbe, Arbeitsfenster und Tageskapazität – die erste
schreibende Funktion und damit der Beleg, dass die Kette trägt: zod prüft
jede Eingabe, die Rolle wird in der Seite und noch einmal in der Server
Action geprüft, jede Änderung hinterlässt einen Audit-Eintrag.

Die Audit-Details werden vor dem Speichern gesäubert: Felder, deren Name
nach Passwort, Token, Betreff oder Mailinhalt aussieht, werden ersetzt.
Das gilt auch in verschachtelten Objekten und Listen.

Dazu die Protokollseite für die Verwaltung und die Startseite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 13:02:49 +02:00

45 lines
1.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'server-only'
import type { Prisma } from '@/generated/prisma/client'
import { auditDetails } from '@/lib/audit'
import { db } from '@/server/db'
import { aktuelleInstanz } from '@/server/instanz'
/**
* Nachweis jeder schreibenden Aktion. Die Details werden vorher gesaeubert
* (siehe @/lib/audit), damit keine Geheimnisse in der Datenbank landen.
*/
export type AuditAkteur = {
id: string | null
email: string
}
export type AuditEingabe = {
akteur: AuditAkteur
/** Was geschah, in der Form "objekt.verb", z. B. "bereich.angelegt". */
aktion: string
objektTyp: string
objektId?: string | null
details?: Record<string, unknown> | undefined
}
/**
* Schreibt einen Audit-Eintrag. Schlaegt das fehl, darf die eigentliche
* Aktion nicht als geglueckt gelten – deshalb wird der Fehler weitergereicht
* und nicht verschluckt.
*/
export async function auditSchreiben(eingabe: AuditEingabe): Promise<void> {
const details = auditDetails(eingabe.details)
await db.auditEintrag.create({
data: {
benutzerId: eingabe.akteur.id,
benutzerEmail: eingabe.akteur.email,
aktion: eingabe.aktion,
objektTyp: eingabe.objektTyp,
objektId: eingabe.objektId ?? null,
...(details ? { details: details as Prisma.InputJsonObject } : {}),
instanz: aktuelleInstanz(),
},
})
}