[Privacy] Complete the audit log: coverage, campaign scoping, admin UI, pruning #120
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context / Motivation
An audit log already exists but is narrow. Current state (verified):
AuditLog(webapp/backend/app/models/audit_log.py):id,created_at(indexed),actor_id(nullable UUID, indexed, no FK — None for system/bot),event(String(100), indexed),description(Text),context(JSONB).webapp/backend/app/services/audit_service.py):log_event(db, event, description, *, actor_id, context)— flush-only, one-line call sites — andget_recent_events(limit, event_filter, actor_id).GET /admin/audit-log(admin_get_audit_log,routers/users.py:267, admin-only, event-prefix + actor filters, limit ≤ 1000).admin.role.{granted|revoked}(users.py:217),admin.settings.notifications.updated(:359),admin.settings.bot.updated(:482),admin.bot_key.regenerated(:511),campaign.archived(campaigns.py:797),campaign.restored(:821),campaign.member.removed(:1148).Missing: campaign scoping, target identification, most destructive actions, any frontend, and pruning.
Spec
Model extension (migration): add nullable
campaign_id(UUID, indexed),target_type(String, e.g. "campaign"/"user"/"session"/"member"),target_id(UUID/Text). Composite index(campaign_id, created_at);actor_idalready indexed. Keep the existingeventstring convention (domain.action), no enum table.Coverage — wire these call sites (one
log_eventline each; keep the list tight — destructive/administrative only, explicitly NOT a general activity feed):campaign.deletedadmin.settings.backup.updatedadmin.settings.ai.updatedadmin.platform_link.recovereduser.deleted(from the account-deletion issue in this milestone)member.recordings_erased,retention.enforced(from the sibling issues)Admin UI: new Admin section "Audit log" — table over
GET /admin/audit-logwith filters (actor, event prefix, date range — extend the endpoint withsince/untilandcampaign_idparams) and pagination. Rendercontextas an expandable JSON blob.Retention/pruning: keep entries 2 years (instance setting, range 1-2y); a Beat task (pattern:
cleanup_trashed_audio,tasks/reminder_tasks.py:1715) deletes older rows monthly and logs a count — the pruning run itself is NOT audit-logged (avoid self-noise), just app-logged.Out of scope
Acceptance criteria
admin.settings.bot.updatedpractice).References
webapp/backend/app/models/audit_log.py(existing model)webapp/backend/app/services/audit_service.py(log_event,get_recent_events)webapp/backend/app/routers/users.py:267(admin_get_audit_log),:217/:359/:482/:511(existing write sites)webapp/backend/app/routers/campaigns.py:797/:821/:1148(existing write sites)webapp/backend/app/tasks/reminder_tasks.py:1715(Beat-task pruning pattern)Filed from the July 2026 full-project review.
Done — merged in PR #200 (backend
b2633b8+ frontend + admin UI). CI green (first run was a flaky/slow backend job; verified locally twice at 541 pass incl. the exact CI command, re-run went green).Shipped:
AuditLoggains nullablecampaign_id(indexed),target_type,target_id+ composite index(campaign_id, created_at); migrationa1c2d3e4f5a6(round-trips, backward-compatible).log_event/get_recent_eventsgain campaign/target/since/until/offset; newcount_events.GET /admin/audit-loggains filters +X-Total-Count; new Audit log admin tab (filter by event/actor/campaign/date, expandable context JSON, pagination).campaign.deleted,admin.settings.backup.updated, + campaign/target backfilled on existing sites. (AI settings share the already-logged bot handler; no admin platform-link-recovery endpoint exists — both left alone.)audit_log_retention_years(default 2, clamp 1–2) + monthlyprune_audit_logBeat task (idempotent, not self-audited).Tests: backend 541 pass (+
test_audit_log.py); frontend 276 (+13).The three sibling v3.8.0 issues (#117/#118/#119) now have the campaign-scoped
log_eventto writeuser.deleted/member.recordings_erased/retention.enforcedinto. Closing.