// Audit log: who signed in, who was added or changed, what was downloaded. One JSON line per event in /data/audit.log // (rotated at about 1 MB, keeping one older file). Contains no passwords, codes or message text. const fs = require('fs'); const path = require('path'); const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, 'data'); const FILE = path.join(DATA_DIR, 'audit.log'), MAX = 1024 * 1024; const ipOf = req => String((req && (req.headers['x-forwarded-for'] || (req.socket && req.socket.remoteAddress))) || '').split(',')[0].trim(); /** log('sign-in', { who: 'Nate', ip, detail: 'iPhone' }) */ function log(event, o = {}, req) { try { const line = JSON.stringify({ t: Date.now(), event, who: o.who || '', detail: o.detail || '', ip: o.ip || ipOf(req) }) + '\n'; try { if (fs.statSync(FILE).size > MAX) fs.renameSync(FILE, FILE + '.1'); } catch {} fs.appendFileSync(FILE, line, { mode: 0o600 }); } catch (e) { console.error('[audit] could not write:', e.message); } } /** Newest first. */ function recent(n = 200) { const rows = []; for (const f of [FILE, FILE + '.1']) { let text = ''; try { text = fs.readFileSync(f, 'utf8'); } catch { continue; } for (const l of text.split('\n').reverse()) { if (!l) continue; try { rows.push(JSON.parse(l)); } catch {} if (rows.length >= n) return rows; } } return rows; } module.exports = { log, recent };