80 lines
4.1 KiB
Markdown
80 lines
4.1 KiB
Markdown
# Phase 4: REST API — query / filter / export / settings / clear
|
|
|
|
**Status:** ⬜ Not Started
|
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
|
**Domain:** backend
|
|
|
|
## Objective
|
|
|
|
Expose the audit log over the REST API: a filtered, keyset-paginated list endpoint; a
|
|
streaming CSV/JSON export honoring the same filters; retention settings get/update; and a
|
|
destructive clear. Apply the project's auth posture (stricter auth on export + clear).
|
|
|
|
## Tasks
|
|
|
|
- [ ] `server/src/ledgrab/api/schemas/activity_log.py` (Pydantic):
|
|
- `ActivityLogEntryResponse` (matches the frozen Phase 2 entry dict shape).
|
|
- `ActivityLogPageResponse` { `entries: list[...]`, `next_before_seq: int | None`,
|
|
`total: int` (optional/over filters), `has_more: bool` }.
|
|
- `ActivityLogSettingsResponse` / `UpdateActivityLogSettingsRequest`
|
|
(`enabled`, `max_days`, `max_entries`) with validation bounds.
|
|
- [ ] `server/src/ledgrab/api/routes/activity_log.py` — `APIRouter(prefix="/api/v1/activity-log")`:
|
|
- `GET ""` — list. Query params: `categories`, `severities`, `actor`, `entity_type`,
|
|
`entity_id`, `since`/`until` (ISO), `q` (free-text), `before_seq` (cursor), `limit`
|
|
(default 50, capped e.g. 200). `AuthRequired`. Maps params → `ActivityLogFilters`,
|
|
calls `repo.query(...)`, returns page envelope.
|
|
- `GET "/export"` — streaming export. Same filters; `format=csv|json`. Uses
|
|
`StreamingResponse` over `repo.iter_export(...)`. **`require_authenticated()`** (may
|
|
contain IPs/labels). Sets `Content-Disposition` with a timestamped filename.
|
|
- `GET "/settings"` / `PUT "/settings"` — retention settings via the retention engine.
|
|
`AuthRequired`; updates apply immediately.
|
|
- `DELETE ""` — clear all entries. **`require_authenticated()`**. The clear is itself
|
|
audited (recorder records a `system`/`activity_log_cleared` entry AFTER the wipe, so the
|
|
log shows who cleared it and when).
|
|
- Register the router in `server/src/ledgrab/api/__init__.py` (aggregator).
|
|
- [ ] API tests `server/tests/api/routes/test_activity_log_api.py`:
|
|
- list returns entries; each filter narrows results; `before_seq` cursor paginates without
|
|
overlap/gaps; `limit` cap enforced;
|
|
- export CSV and JSON both stream and honor filters; export requires authentication
|
|
(401 for loopback-anonymous when keys configured);
|
|
- settings get/update round-trip + validation rejects out-of-range;
|
|
- clear empties the log, requires auth, and leaves exactly one post-clear audit entry.
|
|
|
|
## Files to Modify/Create
|
|
|
|
- `server/src/ledgrab/api/schemas/activity_log.py` — new
|
|
- `server/src/ledgrab/api/routes/activity_log.py` — new
|
|
- `server/src/ledgrab/api/__init__.py` — modify: register router
|
|
- `server/tests/api/routes/test_activity_log_api.py` — new
|
|
|
|
## Acceptance Criteria
|
|
|
|
- List is filterable on every dimension and keyset-paginated (stable, no dupes/gaps).
|
|
- Export streams CSV + JSON, honors filters, and requires authentication.
|
|
- Settings get/update works and validates bounds; changes take effect immediately.
|
|
- Clear requires authentication and is itself audited.
|
|
- New + existing tests green; `ruff` clean.
|
|
|
|
## Notes
|
|
|
|
- Auth helpers: `AuthRequired` dependency for normal endpoints; `require_authenticated()` for
|
|
export + clear (pattern: backup download / secret reveal). See `api/auth.py` + `server/CLAUDE.md`.
|
|
- Follow the existing route/schema conventions (one schema file per entity, router registered
|
|
in `api/__init__.py`). Reference `api/routes/backup.py` for settings-style GET/PUT + a
|
|
`StreamingResponse`/download pattern.
|
|
- Reuse the entry→dict serializer from Phase 2 to keep the response shape single-sourced.
|
|
- Backup/restore: no `STORE_MAP` change needed — backup is whole-DB; the table is auto-covered.
|
|
|
|
## Review Checklist
|
|
|
|
- [ ] All tasks completed
|
|
- [ ] Code follows project conventions (router registration, schema-per-entity, auth posture)
|
|
- [ ] No unintended side effects
|
|
- [ ] Build passes (ruff + pytest)
|
|
- [ ] Tests pass (new + existing)
|
|
|
|
## Handoff to Next Phase
|
|
|
|
<!-- Filled in by the implementer: exact endpoint paths, query-param names, page-envelope
|
|
field names, and settings field bounds — Phase 5/6 frontend consumes these verbatim. -->
|