Add compliance audit log
Immutable audit_log table in the scanner DB records every significant admin action (profile save/delete, token create/revoke, PIN changes, source add/update/delete, scheduler job changes, scan start/stop, SMTP save, dispositions, item delete/redact). GET /api/audit_log exposes entries newest-first. New Audit Log tab in the Settings modal renders the table on demand. Settings modal widened 540→640 px and tab labels set to white-space:nowrap so the six-tab row fits on one line. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4ef2dfb352
commit
744813f4ac
18 files changed
+236
-11
No files matched your search
+28
-1
@@ -237,7 +237,7 @@ function closeSettings() {
|
||||
}
|
||||
|
||||
function switchSettingsTab(tab) {
|
||||
['general','security','scheduler','email','database'].forEach(function(t) {
|
||||
['general','security','scheduler','email','database','auditlog'].forEach(function(t) {
|
||||
var cap = t.charAt(0).toUpperCase() + t.slice(1);
|
||||
var pane = document.getElementById('stPane' + cap);
|
||||
var btn = document.getElementById('stTab' + cap);
|
||||
@@ -248,6 +248,32 @@ function switchSettingsTab(tab) {
|
||||
if (tab === 'email') stLoadSmtp();
|
||||
if (tab === 'database') stLoadDbStats();
|
||||
if (tab === 'scheduler') schedLoad();
|
||||
if (tab === 'auditlog') stLoadAuditLog();
|
||||
}
|
||||
|
||||
async function stLoadAuditLog() {
|
||||
const tbody = document.getElementById('stAuditTableBody');
|
||||
if (!tbody) return;
|
||||
tbody.innerHTML = `<tr><td colspan="4" style="padding:8px;color:var(--muted)">${t('m365_audit_loading')}</td></tr>`;
|
||||
try {
|
||||
const rows = await fetch('/api/audit_log?limit=200').then(r => r.json());
|
||||
if (!Array.isArray(rows) || !rows.length) {
|
||||
tbody.innerHTML = `<tr><td colspan="4" style="padding:8px;color:var(--muted)">${t('m365_audit_empty')}</td></tr>`;
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = rows.map(function(r) {
|
||||
const d = new Date(r.ts * 1000);
|
||||
const ts = d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
|
||||
return '<tr style="border-bottom:1px solid var(--border)">'
|
||||
+ '<td style="padding:4px 8px;white-space:nowrap;color:var(--muted);font-size:11px">' + window._escHtml(ts) + '</td>'
|
||||
+ '<td style="padding:4px 8px"><span style="font-family:monospace;background:var(--bg);border:1px solid var(--border);border-radius:3px;padding:1px 4px;font-size:11px">' + window._escHtml(r.action) + '</span></td>'
|
||||
+ '<td style="padding:4px 8px;color:var(--text);font-size:12px">' + window._escHtml(r.detail) + '</td>'
|
||||
+ '<td style="padding:4px 8px;color:var(--muted);font-size:11px">' + window._escHtml(r.ip) + '</td>'
|
||||
+ '</tr>';
|
||||
}).join('');
|
||||
} catch(e) {
|
||||
tbody.innerHTML = '<tr><td colspan="4" style="padding:8px;color:var(--danger)">' + window._escHtml(String(e)) + '</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Window exports (HTML handlers + cross-module calls) ─────────────────────
|
||||
@@ -266,5 +292,6 @@ window.confirmPinPrompt = confirmPinPrompt;
|
||||
window.openSettings = openSettings;
|
||||
window.closeSettings = closeSettings;
|
||||
window.switchSettingsTab = switchSettingsTab;
|
||||
window.stLoadAuditLog = stLoadAuditLog;
|
||||
window._M365_SOURCES = _M365_SOURCES;
|
||||
window._pinCallback = _pinCallback;
|
||||
+3
-3
@@ -361,17 +361,17 @@
|
||||
.settings-backdrop.open { display:flex; }
|
||||
.settings-modal {
|
||||
background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:10px; width:min(540px,96vw);
|
||||
border-radius:10px; width:min(640px,96vw);
|
||||
display:flex; flex-direction:column; overflow:hidden;
|
||||
font-size:12px; color:var(--text);
|
||||
}
|
||||
.settings-header { padding:16px 20px 0; display:flex; align-items:center; justify-content:space-between; }
|
||||
.settings-header h2 { font-size:14px; font-weight:700; margin:0; }
|
||||
.settings-tabs { display:flex; border-bottom:1px solid var(--border); padding:0 20px; margin-top:12px; }
|
||||
.settings-tabs { display:flex; border-bottom:1px solid var(--border); padding:0 20px; margin-top:12px; flex-wrap:wrap; }
|
||||
.settings-tab {
|
||||
height:36px; padding:0 14px; font-size:12px; cursor:pointer; border:none;
|
||||
background:none; color:var(--muted); border-bottom:2px solid transparent;
|
||||
margin-bottom:-1px; font-weight:500;
|
||||
margin-bottom:-1px; font-weight:500; white-space:nowrap;
|
||||
}
|
||||
.settings-tab.active { color:var(--accent); border-bottom-color:var(--accent); font-weight:600; }
|
||||
.settings-body { padding:16px 20px; overflow-y:auto; max-height:65vh; display:flex; flex-direction:column; gap:14px; }
|
||||
|
||||
Reference in new issue
Block a user