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>
Two bugs in the abort mechanism:
1. POST /api/scan/stop only set state._scan_abort (M365/file abort event)
but never touched state._google_scan_abort. Now sets both.
2. _check_abort() inside _run_google_scan imported gdpr_scanner._scan_abort
(= state._scan_abort, the M365 event) instead of using the module-level
_scan_abort alias (= state._google_scan_abort). This meant the dedicated
/api/google/scan/cancel endpoint — which correctly sets _google_scan_abort
— was silently ignored by the scan loop. Fixed to use the module-level
alias consistently. Also aligned the end-of-scan checkpoint-clear check.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>