import type { Kysely } from 'kysely'; import { uuidv7 } from 'uuidv7'; import type { Database } from '../../db/schema'; /** * The audit log is append only. This module exports an insert and nothing else: there is * no update or delete anywhere in the codebase, which is the whole guarantee. */ export type AuditAction = | 'profile.create' | 'profile.update' | 'consent.grant' | 'consent.revoke' | 'dependent.create' | 'dependent.delete' | 'notification_prefs.update' | 'data.export' | 'account.delete' | 'admin.user_lookup' | 'admin.user_view' | 'admin.role_change' | 'admin.file_access'; export interface AuditEntry { actorUserId: string; actorRole: string; action: AuditAction; /** Whose data this touched. Equal to the actor for a user acting on themselves. */ subjectUserId?: string | null; resource: string; detail?: Record | undefined; ip?: string | null; } export async function writeAudit(db: Kysely, entry: AuditEntry): Promise { await db .insertInto('audit_log') .values({ id: uuidv7(), actor_user_id: entry.actorUserId, actor_role: entry.actorRole, action: entry.action, subject_user_id: entry.subjectUserId ?? entry.actorUserId, resource: entry.resource, detail: entry.detail === undefined ? null : JSON.stringify(entry.detail), ip: entry.ip ?? null, created_at: new Date().toISOString(), }) .execute(); }