import type { Prisma } from "@/generated/prisma/client"; import type { ProtokollErgebnis } from "@/generated/prisma/enums"; import { prisma } from "@/lib/db"; import { schwaerzen } from "@/lib/protokoll-schwaerzen"; /** * Schreibt einen Eintrag ins revisionssichere Protokoll (#005). * * Hash und Verkettung berechnet die Datenbank (Trigger), hier wird nur eingefuegt. * `details` laeuft vor dem Schreiben durch `schwaerzen()` (#006), damit kein Private Key * und kein Passwort hineingeraet – auch nicht aus Versehen ueber ein Fehlerobjekt. */ export type ProtokollEintrag = { readonly akteur: { readonly id: string; readonly name: string } | "System"; readonly aktion: string; readonly ergebnis: ProtokollErgebnis; readonly hostName?: string | null; readonly zielbenutzer?: string | null; readonly fingerprint?: string | null; readonly diff?: string | null; readonly details?: Record; }; export async function protokollieren( eintrag: ProtokollEintrag, client: Prisma.TransactionClient = prisma, ): Promise { const akteur = eintrag.akteur === "System" ? { id: null, name: "System" } : eintrag.akteur; await client.protokoll.create({ data: { akteurId: akteur.id, akteurName: akteur.name, aktion: eintrag.aktion, ergebnis: eintrag.ergebnis, hostName: eintrag.hostName ?? null, zielbenutzer: eintrag.zielbenutzer ?? null, fingerprint: eintrag.fingerprint ?? null, // Auch der Diff: er stammt aus Dateien auf den Hosts, und eine authorized_keys-Datei // kann versehentlich einen eingefuegten Private Key enthalten. diff: eintrag.diff ? (schwaerzen(eintrag.diff) as string) : null, details: eintrag.details ? (schwaerzen(eintrag.details) as Prisma.InputJsonValue) : undefined, }, }); } /** Prueft die Hash-Kette in der Datenbank; liefert die erste fehlerhafte ID oder null. */ export async function protokollKettePruefen(): Promise { const [zeile] = await prisma.$queryRaw< { ergebnis: bigint | null }[] >`SELECT protokoll_kette_pruefen() AS ergebnis`; return zeile?.ergebnis ?? null; }