Audit log (admin Activity list) and passkey recovery codes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c8399ccaa7
commit
561b6181a0
8 files changed
+101
-14
No files matched your search
@@ -0,0 +1,26 @@
|
||||
// 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 };
|
||||
Reference in new issue
Block a user