feat: update API documentation and project specifications
This commit is contained in:
+87
-38
@@ -1,48 +1,97 @@
|
||||
# Visione Backend Architecture
|
||||
|
||||
## Layout
|
||||
- `controllers/`: Drogon `HttpController` classes.
|
||||
- `services/`: application/business logic.
|
||||
- `repositories/`: database helpers and migrations.
|
||||
- `common/`: reusable HTTP, auth, crypto, email and validation utilities.
|
||||
- `migrations/`: versioned SQL files.
|
||||
- `scripts/generate_openapi.py`: minimal OpenAPI route extractor.
|
||||
- `docs/openapi/`: manual OpenAPI contract documents.
|
||||
- `scripts/lab/`: local Streamlit frontend for visual endpoint testing.
|
||||
- `docs/`: product, API, schema and milestone documentation.
|
||||
## Purpose
|
||||
Visione backend is C++20 + Drogon API for municipal urban issue reporting.
|
||||
Citizens create reports. Operators own report workflow on platform. External
|
||||
intervention teams do not log into platform; operators can assign them and
|
||||
generate public read-only intervention links.
|
||||
|
||||
## Drogon Usage
|
||||
Controllers use Drogon route macros and return JSON with
|
||||
`HttpResponse::newHttpJsonResponse`. Database access uses
|
||||
`drogon::orm::DbClient` with raw parameterized SQL placeholders.
|
||||
## Source Of Truth
|
||||
- Route truth: `controllers/*.h`
|
||||
- Business truth: `services/*.cc`
|
||||
- Schema truth: `migrations/*.sql`
|
||||
- Contract truth: `docs/openapi/visione.openapi.yaml` merged with route scan
|
||||
- High-signal human summary: `docs/API_SPEC.md` and `docs/LLM_CONTEXT.md`
|
||||
|
||||
The codebase does not use Drogon generated models or `Mapper<T>`. Raw SQL stays
|
||||
in repository/database helpers because the schema is still evolving.
|
||||
## Code Layout
|
||||
- `controllers/`: HTTP input/output only, auth gate, status mapping.
|
||||
- `services/`: business rules, workflow, permission checks, orchestration.
|
||||
- `repositories/`: SQL serialization helpers and migration runner.
|
||||
- `common/`: auth, crypto, validation, HTTP helpers, email, CORS.
|
||||
- `migrations/`: ordered SQL schema evolution.
|
||||
- `docs/openapi/`: manual OpenAPI layer.
|
||||
- `scripts/generate_openapi.py`: extracts Drogon routes and merges manual spec.
|
||||
|
||||
## Local Email
|
||||
OTP emails are sent to Mailpit over SMTP. Mailpit keeps messages local and
|
||||
inspectable through its web UI.
|
||||
## Runtime Model
|
||||
- Auth uses bearer tokens from `api_tokens`.
|
||||
- OTP login uses `otp_challenges` and Mailpit in local dev.
|
||||
- Password hash uses PBKDF2-HMAC-SHA256. Legacy `sha256$` hashes rejected.
|
||||
- DB access uses raw parameterized SQL through `drogon::orm::DbClient`.
|
||||
- No Drogon generated ORM models. No `Mapper<T>`.
|
||||
|
||||
## Password Hashing
|
||||
User passwords use PBKDF2-HMAC-SHA256 with per-password random salts and 210000
|
||||
iterations. This is intentionally slower than a plain SHA-256 digest and is
|
||||
available through OpenSSL, which is already a Drogon dependency in this project.
|
||||
Legacy `sha256$` password hashes are rejected instead of silently accepted.
|
||||
## Role Model
|
||||
- `citizen`: own reports only, own messages, feedback, notifications.
|
||||
- `operator`: can read all reports, manage workflow, assign teams, create flags.
|
||||
- `admin`: full admin + report management + moderation approval.
|
||||
|
||||
Effective permission helpers:
|
||||
- `auth::canManageReports(user)`: admin or `reports.manage`
|
||||
- `auth::canOperateReports(user)`: non-admin report manager
|
||||
- `auth::mustChangePassword(user)`: blocks protected operational/admin flows
|
||||
|
||||
## Workflow Ownership Rules
|
||||
- `reports.assigned_operator_id` is single platform owner of report.
|
||||
- `assignments` is external team assignment history only.
|
||||
- Team assignment can claim `OPEN` report directly:
|
||||
operator becomes `assigned_operator_id`, report moves to `ASSIGNED`.
|
||||
- `take` exists for `OPEN -> UNDER_REVIEW` without team assignment.
|
||||
- External team link is public, revocable, tied to current assignment, hashed in
|
||||
`intervention_access_links`.
|
||||
|
||||
## Status Machine
|
||||
- Allowed statuses: `OPEN`, `UNDER_REVIEW`, `ASSIGNED`, `IN_PROGRESS`,
|
||||
`RESOLVED`, `REJECTED`, `CLOSED`
|
||||
- Allowed transitions:
|
||||
- `OPEN -> UNDER_REVIEW | REJECTED`
|
||||
- `UNDER_REVIEW -> ASSIGNED | REJECTED`
|
||||
- `ASSIGNED -> IN_PROGRESS | REJECTED`
|
||||
- `IN_PROGRESS -> RESOLVED | REJECTED`
|
||||
- `RESOLVED -> CLOSED`
|
||||
- `REJECTED -> CLOSED`
|
||||
|
||||
## Main Persistence Objects
|
||||
- `users`, `roles`, `permissions`, `user_roles`, `role_permissions`
|
||||
- `api_tokens`, `otp_challenges`
|
||||
- `report_categories`, `areas`, `team_categories`, `teams`
|
||||
- `reports`, `report_status_history`, `assignments`, `report_photos`
|
||||
- `messages`, `feedback`, `reward_events`, `notifications`
|
||||
- `moderation_flags`
|
||||
- `report_category_team_categories`
|
||||
- `intervention_access_links`
|
||||
|
||||
## HTTP/Error Conventions
|
||||
- `401`: missing/invalid auth
|
||||
- `403`: authenticated but forbidden
|
||||
- `404`: missing resource or invalid public intervention token
|
||||
- `409`: workflow/ownership conflict
|
||||
- `422`: semantic payload/reference error
|
||||
|
||||
Controllers currently map service error strings heuristically. When adding new
|
||||
service errors, update controller mappers too.
|
||||
|
||||
## OpenAPI Generation
|
||||
`scripts/generate_openapi.py` scans Drogon `ADD_METHOD_TO` route macros in
|
||||
`controllers/*.h` and extracts only paths, HTTP methods, operation ids, tags and
|
||||
path parameters. It merges those route skeletons with
|
||||
`docs/openapi/visione.openapi.yaml`, which owns summaries, request bodies,
|
||||
responses, schemas and examples. The manual file uses JSON-compatible YAML so
|
||||
the CMake `openapi` target can run without extra Python packages and writes
|
||||
`build/openapi/visione.openapi.json`.
|
||||
|
||||
If the manual YAML contains an operation that no longer exists in the Drogon
|
||||
route list, generation fails unless that operation is explicitly marked
|
||||
`scripts/generate_openapi.py` scans `ADD_METHOD_TO(...)` in controller headers.
|
||||
Generated route skeletons merge with `docs/openapi/visione.openapi.yaml`.
|
||||
Build fails if manual spec contains operation without matching route, unless
|
||||
`x-manual-only: true`.
|
||||
|
||||
## Visione Lab
|
||||
`scripts/lab/` is a Streamlit dev frontend for visual endpoint testing. It is
|
||||
intentionally separate from the real product frontend and should stay small:
|
||||
forms, uploads and response rendering only. Run it with uv from `scripts/lab/`.
|
||||
Output path:
|
||||
- `build/openapi/visione.openapi.json`
|
||||
|
||||
## Current Gaps / Sharp Edges
|
||||
- No dedicated repository layer split yet; SQL still lives inside services.
|
||||
- `AuthService::editUser()` updates fields with dynamic SQL column name from
|
||||
hardcoded allowlist, not user input.
|
||||
- Public intervention summary currently excludes email and full surname, but
|
||||
still exposes citizen phone by design.
|
||||
- No frontend doc set here beyond API/OpenAPI docs.
|
||||
|
||||
Reference in New Issue
Block a user