start project
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# 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."
|
||||
Reference in New Issue
Block a user