Files
Kavosh/docs/api-contract-final.md
T
2026-08-07 21:17:17 +03:30

106 lines
6.4 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.
# Chapter 11 — Final API Contract (post Ch08–12 additions)
Consolidated per Chapter 11's own instruction to update/extend the route
table as new routes are introduced elsewhere, flagging additions explicitly.
## Additions beyond the original Chapter 11 table
| Method | Path | Blueprint | Purpose | Response | Added in |
|---|---|---|---|---|---|
| GET | `/api/overview/browser-breakdown` | overview | Human-only browser/OS breakdown | JSON | Ch08 (Method A) |
| GET | `/` | (root) | Redirect to `/overview` if authenticated, else `/login` | redirect | Ch12 follow-up |
| GET | `/api/uploads` | uploads | List uploaded files, paginated | JSON (Grid.js) | Ch12 follow-up |
| DELETE | `/api/uploads` | uploads | Bulk-delete uploaded files + derived data | JSON | Ch12 follow-up |
`/login`, `/logout`, and `/healthz` were already listed in the original
table (Ch02/Ch04) and are now actually implemented (Ch12) — no table
change needed, just noting they're no longer placeholders. One narrowing:
`/logout` is implemented as POST-only, not GET/POST, so a state-changing
action isn't reachable via a plain GET (CSRF-safety; see
`app/blueprints/auth/routes.py`).
## Architecture note: rollup recompute is now delete-safe (Ch12 follow-up)
Deleting an uploaded file (`app/services/file_deletion.py`) removes its
`log_entries`/`bot_hits`/`suspicious_events` rows explicitly (this
project's SQLite connections don't have `PRAGMA foreign_keys=ON`, so the
`ondelete="CASCADE"` in the migrations is documentation, not enforced
behavior) and recomputes rollups for whatever dates the file touched.
That recompute exposed a real bug in `app/services/aggregator.py`: three
of its five writers only ever upserted-when-data-present and silently
left stale rows behind when a day's data disappeared entirely — never
reachable before deletion existed (rollups only ever grew). All five
writers now use delete-then-insert consistently. See the module
docstrings in `aggregator.py` and `file_deletion.py` for the full
reasoning, including the deliberately-out-of-scope limitation around
`ip_registry`/`blocklist_suggestions` not being recomputed on delete.
## Architecture note: cron is now optional (Ch12 follow-up)
`POST /uploads` now triggers processing automatically in a background
thread (`app/services/background.py`), and `GET /overview` opportunistically
resumes any file left incomplete. `flask process-logs` and `flask cleanup`
(`app/cli.py`) still exist and work identically to before for anyone who
wants a cron-based fallback, but nothing in the app requires it anymore.
See the module docstring in `app/services/background.py` for the explicit
tradeoff this introduces relative to the original Chapter 02/03 "no
persistent background workers, cron-triggered CLI only" stance.
## Full current route table
| Method | Path | Blueprint | Purpose | Response | Auth |
|---|---|---|---|---|---|
| GET | `/overview` | overview | Overview tab | full page / HTMX partial | required |
| GET | `/api/overview/kpis` | overview | KPI card values | JSON | required |
| GET | `/api/overview/traffic-chart` | overview | Traffic-over-time series | JSON | required |
| GET | `/api/overview/status-codes` | overview | Status-code breakdown | JSON | required |
| GET | `/api/overview/top-urls` | overview | Top URLs table | JSON (Grid.js) | required |
| GET | `/api/overview/top-referrers` | overview | Top referrers table | JSON (Grid.js) | required |
| GET | `/api/overview/browser-breakdown` | overview | Human browser/OS breakdown | JSON | required |
| GET | `/seo` | seo | SEO tab | full page / HTMX partial | required |
| GET | `/api/seo/bot-summary` | seo | Bot summary cards | JSON | required |
| GET | `/api/seo/crawl-chart-data` | seo | Crawl frequency series | JSON | required |
| GET | `/api/seo/bot-status-codes` | seo | Status codes served to bots | JSON | required |
| GET | `/api/seo/crawled-vs-visited` | seo | Bot vs. human URL comparison | JSON (Grid.js) | required |
| GET | `/security` | security | Security tab | full page / HTMX partial | required |
| GET | `/api/security/events` | security | Suspicious events table | JSON (Grid.js) | required |
| GET | `/api/security/sensitive-paths` | security | Sensitive-path summary | JSON | required |
| GET | `/api/security/ip/<ip>` | security | IP history drill-down | HTMX fragment | required |
| GET | `/api/security/export-blocklist` | security | Blocklist export | text/plain download | required |
| POST | `/uploads` | uploads | Upload a log file | HTMX fragment (queued state) | required |
| GET | `/api/uploads/<id>/status` | uploads | Poll parse status | JSON or HTMX fragment | required |
| GET | `/api/uploads` | uploads | List uploaded files | JSON (Grid.js) | required |
| DELETE | `/api/uploads` | uploads | Bulk-delete uploaded files | JSON | required |
| GET | `/login` | auth | Show login form | full page | public |
| POST | `/login` | auth | Authenticate | redirect | public |
| POST | `/logout` | auth | End session | redirect | required |
| GET | `/healthz` | (root) | Liveness check | JSON | public |
| GET | `/` | (root) | Auth-based redirect | redirect | public |
## Envelope compliance audit (Chapter 12)
Every `/api/...` JSON endpoint above was checked against Chapter 11's
`{"data": ..., "meta": {...}}` / `{"error": {"code", "message"}}`
convention. All conform. `app/utils/envelope.py` was added this chapter
as a shared helper for *new* endpoints going forward (used by the
`unauthorized_handler` in `app/__init__.py`) — existing endpoints already
matched the shape by hand and weren't rewritten, to avoid churn on
working code.
New this chapter: every `/api/...` path now returns a JSON `401` with
`{"error": {"code": "unauthorized", ...}}` when unauthenticated, instead
of Flask-Login's default redirect — consistent with the envelope even
for auth failures. `/api/security/ip/<ip>` and `/api/security/export-
blocklist` are the two exceptions where a *successful* response isn't
JSON (HTMX fragment / text download, per the table above and Chapter 10)
— their auth-failure response is still the JSON envelope for consistency.
## Auth model (Chapter 12)
Every blueprint except `auth` and the root `/healthz` route requires a
logged-in session (`@bp.before_request` + `flask_login.login_required` in
each blueprint's `__init__.py`). This closes a gap that existed from
Chapter 04 through Chapter 10: all dashboard and `/api/...` routes were
reachable without authentication until Chapter 12 wired in Flask-Login,
even though Chapter 01 specifies "one authenticated single-page shell."