import { pool } from '../pool.js'; const REDACT_KEYS = new Set([ 'token','token_hash','password','api_key','secret','authorization' ]); function isSensitiveKey(k) { return REDACT_KEYS.has(String(k).toLowerCase()); } function redactValueForKey(k, v) { if (isSensitiveKey(k)) return '[REDACTED]'; return redact(v); } function redact(obj) { if (!obj || typeof obj !== 'object') return obj; if (Array.isArray(obj)) return obj.map(redact); const out = {}; for (const [k, v] of Object.entries(obj)) { out[k] = redactValueForKey(k, v); } return out; } function diff(before, after) { if (before === null && after === null) return null; if (before === null || before === undefined) return { kind: 'create', after: redact(after) }; if (after === null || after === undefined) return { kind: 'delete', before: redact(before) }; const changed = {}; for (const k of new Set([...Object.keys(before), ...Object.keys(after)])) { if (JSON.stringify(before[k]) !== JSON.stringify(after[k])) { changed[k] = { before: redactValueForKey(k, before[k]), after: redactValueForKey(k, after[k]) }; } } return Object.keys(changed).length ? { kind: 'update', changes: changed } : null; } export async function recordAudit(actor, action, entity_type, entity_id, before, after) { const d = diff(before, after); await pool.query( `INSERT INTO audit_log(actor_kind, actor_id, entity_type, entity_id, action, diff) VALUES($1,$2,$3,$4,$5,$6)`, [actor?.kind || 'system', actor?.id || null, entity_type, entity_id, action, d] ); } export async function listForEntity(entity_type, entity_id, { limit = 100 } = {}) { const { rows } = await pool.query( `SELECT * FROM audit_log WHERE entity_type=$1 AND entity_id=$2 ORDER BY occurred_at DESC LIMIT $3`, [entity_type, entity_id, limit] ); return rows; } export async function listByActor({ actor_kind, actor_id, limit = 100 } = {}) { const where = [], vals = []; let i = 1; if (actor_kind) { where.push(`actor_kind=$${i++}`); vals.push(actor_kind); } if (actor_id) { where.push(`actor_id=$${i++}`); vals.push(actor_id); } vals.push(limit); const w = where.length ? `WHERE ${where.join(' AND ')}` : ''; const { rows } = await pool.query( `SELECT * FROM audit_log ${w} ORDER BY occurred_at DESC LIMIT $${i}`, vals ); return rows; }