/** * Append-only audit trail for Registratura entries. * Server-side only — uses Prisma directly. */ import { prisma } from "@/core/storage/prisma"; import { Prisma } from "@prisma/client"; import type { RegistryAuditAction, RegistryAuditEvent } from "../types"; /** Log a single audit event (append-only, never updated or deleted) */ export async function logAuditEvent(params: { entryId: string; entryNumber: string; action: RegistryAuditAction; actor: string; actorName?: string; company: string; detail?: Record; }): Promise { await prisma.registryAudit.create({ data: { entryId: params.entryId, entryNumber: params.entryNumber, action: params.action, actor: params.actor, actorName: params.actorName ?? null, company: params.company, detail: (params.detail as Prisma.InputJsonValue) ?? Prisma.JsonNull, }, }); } /** Get chronological audit history for a single entry */ export async function getAuditHistory( entryId: string, ): Promise { const rows = await prisma.registryAudit.findMany({ where: { entryId }, orderBy: { createdAt: "asc" }, }); return rows.map(mapRow); } /** Get audit events for a company, optionally filtered by date range */ export async function getAuditByCompany( company: string, options?: { from?: string; to?: string; limit?: number }, ): Promise { const where: Prisma.RegistryAuditWhereInput = { company }; if (options?.from || options?.to) { const createdAt: Prisma.DateTimeFilter = {}; if (options.from) createdAt.gte = new Date(options.from); if (options.to) createdAt.lte = new Date(options.to); where.createdAt = createdAt; } const rows = await prisma.registryAudit.findMany({ where, orderBy: { createdAt: "desc" }, take: options?.limit ?? 100, }); return rows.map(mapRow); } /** Compute diff between old and new entry for "updated" audit events */ export function computeEntryDiff( oldEntry: Record, newEntry: Record, fieldsToTrack: string[], ): Record | null { const changes: Record = {}; for (const field of fieldsToTrack) { const oldVal = oldEntry[field]; const newVal = newEntry[field]; if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) { changes[field] = { old: oldVal, new: newVal }; } } return Object.keys(changes).length > 0 ? changes : null; } // ── Internal helpers ── function mapRow(r: { id: string; entryId: string; entryNumber: string; action: string; actor: string; actorName: string | null; company: string; detail: Prisma.JsonValue; createdAt: Date; }): RegistryAuditEvent { return { id: r.id, entryId: r.entryId, entryNumber: r.entryNumber, action: r.action as RegistryAuditAction, actor: r.actor, actorName: r.actorName ?? undefined, company: r.company, detail: (r.detail as Record) ?? undefined, createdAt: r.createdAt.toISOString(), }; } /** Fields tracked for update diffs */ export const TRACKED_FIELDS = [ "subject", "sender", "recipient", "direction", "documentType", "status", "company", "deadline", "assignee", "date", "notes", "tags", "expiryDate", ];