Files
sciagent/docs/audit-log-manager-implementation.md
T
Thinh Lam 688fac73e9
CI/CD / backend (push) Failing after 2m8s
CI/CD / frontend (push) Failing after 1m40s
CI/CD / deploy (push) Has been skipped
sciagent code + Gitea Actions CI/CD
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 09:38:30 +07:00

6.1 KiB
Raw Blame History

Audit Log Manager — Implementation notes (review & debug)

This document describes the Audit Log Manager delivered in this repo: database schema, backend recording, admin API, frontend layout, and how to verify end-to-end (Postgres, API, MinIO).

1. Database

  • Migration: be0/migrations/008_audit_events.sql
    • Creates PostgreSQL enum audit_action and table audit_events (append-only by convention).
    • Indexes: actor+time, entity+time, action+time, GIN on metadata.

Apply (example against local Docker Postgres — adjust connection):

psql "$INITIATIVE_DATABASE_URL" -f be0/migrations/008_audit_events.sql

(INITIATIVE_DATABASE_URL is typically postgresql://… for psql; the app uses SQLAlchemy async URL like postgresql+asyncpg://….)

Docker Compose mounts 008_audit_events.sql into docker-entrypoint-initdb.d for new databases only. Existing initiative_pg_data volumes still need the psql -f …/008_audit_events.sql step once.

2. Backend model & helpers

Piece Path
ORM model be0/src/initiative_db/models.pyAuditEvent
Helpers be0/src/audit.pyrecord_audit, persist_audit_standalone, resolve_actor_fields
  • await record_audit(session, …) — insert via a SAVEPOINT: if audit_events is missing (migration not applied), logs a warning and does not roll back the parent transaction (so login/register still succeed).
  • await persist_audit_standalone(…) — own session + commit; same missing-table handling without raising.

3. Where events are written

Area Actions / entity_type Notes
Auth registercreate / user Same transaction as user + roles
Auth OK login → login / auth
Auth Bad login → login_failed / auth Standalone insert after failed credentials
Auth Valid Bearer logout → logout / auth Standalone; skipped if JWT does not decode
Auth Profile patch → update / user Before/after: fullName, phone
Auth Password change → update / user Snapshots `{ password: "[redacted
Drafts autosave update / application_draft When owner_id is known (authenticated saves)
MinIO evidence create/update/delete / application_evidence After DB + object write/delete in main.py
Staff evidence review update / application_evidence_review

Admin adjudication (application_admin_results.py):

  • create / application_admin_result
  • update / …
  • upsertcreate or update depending on prior row
  • delete — requires actor_user_id; delete_admin_result(..., actor_user_id=…) called from main.py

Legacy table audit_log (draft telemetry) remains unchanged.

4. Admin HTTP API

  • JWT decode for admin routes (decode_bearer_token, decode_access_token_user_id) lives in be0/src/auth_jwt.py so audit routes do not pull in Argon2 at import time.
  • Router: be0/src/admin_audit_routes.py, mounted in main.py as /api/v1/admin.
  • GET /api/v1/admin/audit — list (admin JWT only).
    • Default window: now 7 daysnow if from / to omitted.
    • Query params: from, to, actor_user_id, actor_email (exact, lowercased) entity_type, entity_id, action (comma-separated), request_id, page, page_size (≤ 100), sort (occurred_at:asc|desc).
  • GET /api/v1/admin/audit/{id} — full row incl. before / after JSON.

Non-admin receives 403.

5. Frontend

Layer Path
Shared types + API client fe0/src/audit/
Admin UI fe0/src/admin/audit/AuditLogManagerPage, filters, table, detail Sheet
Applicant-side copy (reuse) fe0/src/applicant/audit/actionLabels.ts — Vietnamese labels for actions
Nav fe0/src/components/admin/DashboardSidebar.tsx (+ duplicate DashboardSidebar.tsx) — « Nhật ký Audit »/dashboard/admin/audit
Routes fe0/src/App.tsxadmin/audit before admin nested under dashboard

Detail panel loads /api/v1/admin/audit/{id} and runs microdiff on the client only (see audit/jsonMicrodiffLines.ts), per architecture doc.

Dependency: microdiff (added to fe0/package.json).

6. Debugging checklist

  1. 500 on /auth/login after adding audit — Postgres log / SQLAlchemy showed relation "audit_events" does not exist: apply migration 008. Until then, record_audit skips the insert and auth still works.
  2. ECONNREFUSED from fe0 to :4402 — API not listening yet (be0 still on NLTK pip, or old entrypoint failed on ollama: command not found). Rebuild be0; entrypoint now skips Ollama when the binary is absent. Wait for Uvicorn running on http://0.0.0.0:4402 before logging in.
  3. Table missing / Admin audit 503 — run migration 008_audit_events.sql.
  4. Empty list but events expected — check from/to; default is last 7 days on the API.
  5. 403 on /api/v1/admin/audit — JWT must include admin in roles (same as other admin APIs).
  6. Failed-login rows missing — must use persist_audit_standalone path; if Postgres URL wrong, standalone insert is skipped (is_postgres_enabled()).
  7. MinIO uploads without audit rows — evidence handler must reach upsert_evidence_artifact after successful s3.upload; failures before that do not audit.
  8. Comment typo broke build onceformatAuditTime.ts JSDoc used invalid **/” sequence; stick to */ closing block comments.

7. Suggested verification flow

  1. Apply migration → restart API.
  2. Register / login → see create/login rows (filter action=login,create).
  3. Save applicant draft tab → application_draft.
  4. Upload evidence with MinIO configured → application_evidence with metadata.minioBucket.
  5. Open /dashboard/admin/audit as admin → pagination + row click opens JSON + microdiff.

8. References

  • Spec: assets/docs/audit-log-implementation.md
  • Product plan: docs/audit-log-manager-plan.md