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

102 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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):
```bash
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.py``AuditEvent` |
| Helpers | `be0/src/audit.py``record_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 | `register``create` / `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|changed]" }` only |
| 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` / …
- `upsert``create` 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 days****now** 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.tsx`**`admin/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 once**`formatAuditTime.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`