"use strict"; /** Phase 6 — Owner-only audit log. * Persisted to local-db/audit/audit.enc (AES-256-GCM + XChaCha20-Poly1305 * via SecureStore). Legacy audit.json is migrated on first load. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.appendAudit = appendAudit; exports.listAudit = listAudit; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const secureStore_1 = require("./secureStore"); const LOCAL_DIR = path_1.default.join(__dirname, "../../local-db"); const DATA_DIR = path_1.default.join(LOCAL_DIR, "audit"); const FILE = path_1.default.join(DATA_DIR, "audit.enc"); const LEGACY = path_1.default.join(LOCAL_DIR, "audit.json"); const RING_LIMIT = 5000; const FLUSH_MS = 5000; let buffer = []; let loaded = false; let dirty = false; let secure = null; function getSecure() { if (!secure) { fs_1.default.mkdirSync(LOCAL_DIR, { recursive: true }); secure = new secureStore_1.SecureStore(LOCAL_DIR); } return secure; } function persistNow() { try { fs_1.default.mkdirSync(DATA_DIR, { recursive: true }); getSecure().encryptToFile(JSON.stringify(buffer.slice(-RING_LIMIT)), FILE); } catch (e) { console.warn("[audit] persist failed:", e?.message); } } function ensureLoaded() { if (loaded) return; loaded = true; try { fs_1.default.mkdirSync(DATA_DIR, { recursive: true }); // One-time migration from legacy audit.json if (fs_1.default.existsSync(LEGACY) && !fs_1.default.existsSync(FILE)) { try { const arr = JSON.parse(fs_1.default.readFileSync(LEGACY, "utf-8")); if (Array.isArray(arr)) buffer = arr.slice(-RING_LIMIT); persistNow(); fs_1.default.unlinkSync(LEGACY); console.log(`[audit] migrated audit.json → audit/audit.enc (${buffer.length} entries)`); return; } catch (e) { console.warn("[audit] migration failed:", e?.message); } } if (fs_1.default.existsSync(FILE)) { const json = getSecure().decryptFromFile(FILE); const arr = JSON.parse(json); if (Array.isArray(arr)) buffer = arr.slice(-RING_LIMIT); } } catch (e) { console.warn("[audit] load failed:", e?.message); } } // Eagerly migrate / load at module startup so the audit/ dir exists before // the first appendAudit or listAudit call. ensureLoaded(); setInterval(() => { if (!dirty) return; dirty = false; persistNow(); }, FLUSH_MS).unref?.(); function appendAudit(e) { ensureLoaded(); const entry = { id: `aud_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`, at: e.at ?? Date.now(), by: e.by, action: e.action, target: e.target, meta: e.meta, }; buffer.push(entry); if (buffer.length > RING_LIMIT) buffer = buffer.slice(-RING_LIMIT); dirty = true; return entry; } function listAudit(limit = 200, action) { ensureLoaded(); const filtered = action ? buffer.filter(e => e.action.startsWith(action)) : buffer; return filtered.slice(-limit).reverse(); }