feat: audit log campaign scoping, coverage, admin UI, pruning (#120) #200

Merged
claude-bot merged 3 commits from feat/120-audit-log into main 2026-07-18 06:57:56 +00:00
Contributor

Closes #120. First issue of v3.8.0 Privacy & Data Lifecycle — the audit foundation the sibling issues (#117/#118/#119) log into.

Backend (b2633b8)

  • AuditLog gains nullable campaign_id (indexed), target_type, target_id + composite index (campaign_id, created_at). Migration a1c2d3e4f5a6 (round-trips). Backward-compatible — new columns nullable, the existing 7 log sites keep working.
  • log_event/get_recent_events gain campaign_id/target/since/until/offset; new count_events for pagination totals.
  • GET /admin/audit-log gains since/until/campaign_id/offset filters + X-Total-Count; response carries the new fields.
  • Coverage: campaign.deleted (hard delete) + admin.settings.backup.updated; campaign_id/target backfilled on the existing archive/restore/member-removed sites. (AI settings share the already-logged bot-settings handler; no admin platform-link-recovery endpoint exists — both correctly left alone rather than inventing sites.)
  • Pruning: audit_log_retention_years setting (default 2, clamp 1–2) + a monthly prune_audit_log Beat task — state-driven, idempotent, and not self-audited.

Frontend (<head>)

  • New "Audit log" admin tab: filterable table (event prefix, actor, campaign, date range) with X-Total-Count pagination (reuses the existing requestList helper); per-row expandable context JSON; "system" for null actors.

Tests

  • Backend: +test_audit_log.py (new columns, filters/pagination/X-Total-Count, coverage sites with secret redaction, idempotent non-self-auditing pruning). Full suite 541 pass; migration round-trip verified.
  • Frontend: +13 (wrapper 7, section 6). 276 pass, eslint clean, vite build green.

Notes

  • Scoped tight per the issue — destructive/administrative events only, not a general activity feed.
  • since/until come from datetime-local inputs (naive → treated as UTC by asyncpg for the timestamptz comparison); a minor admin filter.

🤖 Generated with Claude Code

Closes #120. First issue of **v3.8.0 Privacy & Data Lifecycle** — the audit foundation the sibling issues (#117/#118/#119) log into. ## Backend (`b2633b8`) - `AuditLog` gains nullable `campaign_id` (indexed), `target_type`, `target_id` + composite index `(campaign_id, created_at)`. Migration `a1c2d3e4f5a6` (round-trips). Backward-compatible — new columns nullable, the existing 7 log sites keep working. - `log_event`/`get_recent_events` gain `campaign_id`/`target`/`since`/`until`/`offset`; new `count_events` for pagination totals. - `GET /admin/audit-log` gains `since`/`until`/`campaign_id`/`offset` filters + `X-Total-Count`; response carries the new fields. - Coverage: `campaign.deleted` (hard delete) + `admin.settings.backup.updated`; `campaign_id`/`target` backfilled on the existing archive/restore/member-removed sites. *(AI settings share the already-logged bot-settings handler; no admin platform-link-recovery endpoint exists — both correctly left alone rather than inventing sites.)* - Pruning: `audit_log_retention_years` setting (default 2, clamp 1–2) + a monthly `prune_audit_log` Beat task — state-driven, idempotent, and **not** self-audited. ## Frontend (`<head>`) - New "Audit log" admin tab: filterable table (event prefix, actor, campaign, date range) with `X-Total-Count` pagination (reuses the existing `requestList` helper); per-row expandable `context` JSON; "system" for null actors. ## Tests - Backend: +`test_audit_log.py` (new columns, filters/pagination/`X-Total-Count`, coverage sites with secret redaction, idempotent non-self-auditing pruning). Full suite **541 pass**; migration round-trip verified. - Frontend: +13 (wrapper 7, section 6). **276 pass**, eslint clean, vite build green. ## Notes - Scoped tight per the issue — destructive/administrative events only, not a general activity feed. - `since`/`until` come from `datetime-local` inputs (naive → treated as UTC by asyncpg for the timestamptz comparison); a minor admin filter. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Extends the existing audit log toward the v3.8.0 privacy foundation the
sibling issues (#117/#118/#119) will log into.

- AuditLog gains nullable campaign_id (indexed), target_type, target_id +
  composite index (campaign_id, created_at). Migration a1c2d3e4f5a6
  (round-trips). Backward-compatible — new columns nullable, existing 7 call
  sites unchanged.
- log_event/get_recent_events gain campaign_id/target/since/until/offset;
  new count_events for pagination totals.
- GET /admin/audit-log gains since/until/campaign_id/offset filters +
  X-Total-Count; response carries the new fields.
- Coverage: campaign.deleted on hard delete + admin.settings.backup.updated;
  campaign_id/target backfilled on the existing archive/restore/member-removed
  sites. (AI settings share the already-logged bot-settings handler; no admin
  platform-link recovery endpoint exists — both correctly left alone.)
- Pruning: audit_log_retention_years setting (default 2, clamp 1-2) + a
  monthly prune_audit_log Beat task (state-driven, idempotent, NOT
  self-audited).

Tests: +test_audit_log.py (columns, filters/pagination/X-Total-Count,
coverage sites w/ secret redaction, idempotent non-self-auditing pruning).
Full suite 541 pass; migration round-trip verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(frontend): admin audit-log viewer (#120)
Some checks failed
CI / Frontend tests, audit, and build (pull_request) Successful in 2m1s
CI / Docker image build (pull_request) Successful in 42s
CI / Bot tests and audit (pull_request) Successful in 1m57s
CI / Backend migration, tests, and audit (pull_request) Failing after 2m36s
CI / Backend lint (ruff) (pull_request) Successful in 5m40s
e16ccd5178
New "Audit log" admin tab: filterable table (event prefix, actor, campaign,
date range) over GET /admin/audit-log with X-Total-Count pagination
(reusing the requestList list-helper). Columns time/actor/event/target/
campaign/description; per-row expand renders the context JSON. "system"
shown for null actors.

Tests: +13 (fetchAuditLog wrapper 7, AuditLogSection 6). 276 pass, eslint
clean, vite build succeeds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ci: re-trigger (flaky backend job; 541 pass locally incl. exact CI cmd)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 58s
CI / Frontend tests, audit, and build (pull_request) Successful in 2m8s
CI / Bot tests and audit (pull_request) Successful in 2m20s
CI / Docker image build (pull_request) Successful in 7m8s
CI / Backend migration, tests, and audit (pull_request) Successful in 20m39s
4626c8719b
claude-bot deleted branch feat/120-audit-log 2026-07-18 06:57:58 +00:00
Sign in to join this conversation.
No description provided.