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

6.4 KiB
Raw Permalink Blame History

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."