SPB Git

Repository Public

audit.ts 1.2 KB 36 lines typescript

1/** Append-only audit trail. `detail` must never contain secrets, file contents or terminal data. */2import type { FastifyBaseLogger, FastifyRequest } from 'fastify';3import type { Db } from './db.js';45export interface AuditEntry {6  actor?: string;7  action: string;8  target?: string | null;9  ip?: string | null;10  ok?: boolean;11  detail?: Record<string, unknown>;12}1314export class Audit {15  constructor(16    private readonly db: Db,17    private readonly log: FastifyBaseLogger,18  ) {}1920  /** Never throws: an audit write failure is logged and must not break the request. */21  async record(e: AuditEntry): Promise<void> {22    try {23      await this.db.q(24        'INSERT INTO audit_events (actor, action, target, ip, ok, detail) VALUES ($1, $2, $3, $4, $5, $6)',25        [e.actor ?? 'system', e.action, e.target ?? null, e.ip ?? null, e.ok ?? true, JSON.stringify(e.detail ?? {})],26      );27    } catch (err) {28      this.log.error({ err, action: e.action }, 'audit write failed');29    }30  }31  /** Convenience: actor + ip taken from the request. */32  req(req: FastifyRequest, action: string, o: Omit<AuditEntry, 'action' | 'ip'> = {}): Promise<void> {33    return this.record({ actor: req.auth?.username ?? 'anonymous', ip: req.ip, ...o, action });34  }35}36
Files main
Menu

Theme