diff --git a/docs/API_SPEC.md b/docs/API_SPEC.md index 3a2491a..e5d4320 100644 --- a/docs/API_SPEC.md +++ b/docs/API_SPEC.md @@ -1,59 +1,110 @@ # Visione API Spec -Base path: `/api/v1`. +Base path: `/api/v1` -## Technical -- `GET /health`: process health. -- `GET /ready`: MySQL readiness. -- `GET /openapi.json`: generated OpenAPI 3.0 JSON. -- `GET /docs`: Swagger UI page backed by the generated spec. - -## Auth -- `POST /auth/register`: create a citizen account. -- `POST /auth/otp/request`: send an OTP email through Mailpit. -- `POST /auth/otp/verify`: verify OTP and create an API token. -- `POST /auth/login/password`: password login fallback. -- `POST /auth/change-password`: change the authenticated user's password. -- `GET /me`: return the authenticated user. -- `PATCH /me`: edit user informations. - -Protected endpoints use: +## Auth Header ```http Authorization: Bearer ``` -## Reports -- `POST /reports`: create a report. -- `GET /reports`: list reports with optional `status`, `category_id`, `area_id`. -- `GET /report-categories`: list active report categories for authenticated users. -- `GET /operator/teams`: list active intervention teams for operators/admins. -- `GET /operator/reports/assigned`: list reports assigned to the current operator. -- `GET /operator/reports/open`: list open unassigned reports. -- `GET /operator/reports/others`: list reports assigned to other operators. -- `GET /reports/{id}`: report detail. -- `GET /reports/{id}/intervention-summary`: operator-facing report summary with - citizen contact data, media and latest messages. -- `GET /reports/{id}/team-suggestions`: suggest compatible teams by area/category. -- `POST /reports/{id}/intervention-link`: create or rotate public team link. -- `DELETE /reports/{id}/intervention-link`: revoke public team link. -- `PATCH /reports/{id}/status`: update status. -- `POST /reports/{id}/take`: assigned-queue operator takes an open report. -- `POST /reports/{id}/messages`: write a report message. -- `GET /reports/{id}/messages`: list report messages. -- `POST /reports/{id}/messages/{message_id}/flags`: assigned operator flags a - report message for moderation. -- `POST /reports/{id}/assignments`: assign a team. -- `POST /reports/{id}/feedback`: add citizen feedback. -- `POST /reports/{id}/flags`: assigned operator flags a report for moderation. -- `POST /reports/{id}/media`: upload up to five multipart media files. Supported - types are JPEG, PNG, WebP, MP4, WebM and QuickTime. +## Roles +- public: no auth +- authenticated: any valid user +- operator: `reports.manage` or admin +- admin: role/permission admin -## User -- `GET /notifications`: list authenticated user notifications. -- `PATCH /notifications/{id}/read`: mark one notification as read. -- `PATCH /notifications/read-all`: mark all authenticated user notifications as read. -- `GET /me/rewards`: return current points and reward event history. +## HTTP Semantics +- `200`: success +- `401`: missing or invalid auth +- `403`: authenticated but forbidden +- `404`: missing resource or invalid public intervention token +- `409`: workflow or ownership conflict +- `422`: semantic payload/reference error + +## Technical +- `GET /health` +- `GET /ready` +- `GET /openapi.json` +- `GET /docs` + +## Auth + Profile +- `POST /auth/register` + citizen registration +- `POST /auth/otp/request` + sends OTP through Mailpit +- `POST /auth/otp/verify` + consumes OTP, returns bearer token +- `POST /auth/login/password` + password fallback login +- `POST /auth/change-password` + authenticated password change, also clears `must_change_password` +- `GET /me` + current user profile, roles, permissions, points +- `PATCH /me` + editable fields today: `first_name`, `last_name`, `phone` + +## Public Reference / Public Access +- `GET /areas` + active municipal areas +- `GET /interventions/{token}` + public external-team summary link +- `GET /open-data/reports` + anonymized reports in `RESOLVED` or `CLOSED` + +## User Reference / Personal +- `GET /report-categories` + active report categories, authenticated users only +- `GET /notifications` +- `PATCH /notifications/{id}/read` +- `PATCH /notifications/read-all` +- `GET /me/rewards` + +## Report Endpoints +- `POST /reports` + create citizen report, optional inline `media`, area inferred by GeoJSON +- `GET /reports` + citizen sees own reports; operator/admin see all +- `GET /reports/{id}` + detail + status history + media +- `GET /reports/{id}/messages` +- `POST /reports/{id}/messages` +- `POST /reports/{id}/media` + max 5 total media, image/video only +- `POST /reports/{id}/feedback` + only owner, only when status is `RESOLVED` + +## Operator Workflow +- `GET /operator/teams` + active external intervention teams +- `GET /operator/reports/open` +- `GET /operator/reports/assigned` +- `GET /operator/reports/others` +- `POST /reports/{id}/take` + `OPEN -> UNDER_REVIEW` +- `PATCH /reports/{id}/status` + only owner operator or admin, valid transitions only +- `POST /reports/{id}/assignments` + assign external team + if report is `OPEN`, assignment can directly claim report and move to `ASSIGNED` +- `GET /reports/{id}/intervention-summary` + operator/admin full summary with citizen contact data +- `GET /reports/{id}/team-suggestions` + suggestions by category/team-category mapping and area +- `POST /reports/{id}/intervention-link` + create/rotate public intervention token for current assignment +- `DELETE /reports/{id}/intervention-link` + revoke active intervention token + +## Moderation +- `POST /reports/{id}/flags` + operator flags owned report +- `POST /reports/{id}/messages/{message_id}/flags` + operator flags owned report message +- `GET /admin/moderation` + admin moderation queue +- `PATCH /admin/moderation/{id}` + admin resolves/dismisses; may also reject report ## Admin Reference Data - `POST /admin/categories` @@ -62,15 +113,43 @@ Authorization: Bearer - `PATCH /admin/areas/{id}` - `POST /admin/teams` - `PATCH /admin/teams/{id}` -- `GET /admin/stats/overview` -- `GET /admin/moderation`: list moderation flags with optional `status`. -- `PATCH /admin/moderation/{id}`: resolve or dismiss a moderation flag, optionally - rejecting the related report. - `GET /admin/users` - `PATCH /admin/users/{id}/roles` - `PATCH /admin/users/{id}/ban` +- `GET /admin/stats/overview` -## Public Data -- `GET /areas`: list active municipal areas. -- `GET /interventions/{token}`: public intervention summary for external teams. -- `GET /open-data/reports`: anonymized resolved/closed report export. +## Workflow Summary +### Citizen +1. register/login +2. create `OPEN` report +3. watch status/history/media +4. chat with operator +5. receive notifications +6. leave feedback on `RESOLVED` + +### Operator +1. inspect open queue +2. `take` or assign team directly +3. own report through `assigned_operator_id` +4. update status / chat / moderate +5. generate public team link + +### Admin +1. manage reference data and users +2. resolve moderation +3. read global stats + +## Current Status Machine +- `OPEN -> UNDER_REVIEW | REJECTED` +- `UNDER_REVIEW -> ASSIGNED | REJECTED` +- `ASSIGNED -> IN_PROGRESS | REJECTED` +- `IN_PROGRESS -> RESOLVED | REJECTED` +- `RESOLVED -> CLOSED` +- `REJECTED -> CLOSED` + +## Important Current Truths +- External teams are not platform users. +- Platform owner is always `reports.assigned_operator_id`. +- `assignments` is historical external team linkage only. +- Feedback does not auto-close report. +- Public intervention summary exposes citizen phone by design, but not email or full surname. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 68b969f..930e595 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -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`. 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`. -## 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. diff --git a/docs/DATABASE_SCHEMA.md b/docs/DATABASE_SCHEMA.md index 7040c5a..76b6693 100644 --- a/docs/DATABASE_SCHEMA.md +++ b/docs/DATABASE_SCHEMA.md @@ -1,31 +1,103 @@ # Visione Database Schema -The schema is stored in ordered SQL migrations under `migrations/`. +Schema truth is `migrations/*.sql`. This file is compact human map of current DB. -Persistence currently uses `drogon::orm::DbClient` with raw parameterized SQL. -The project does not use Drogon generated models or `Mapper` while the schema -is still evolving. +## Migration Set +- `001_initial_schema.sql` + base auth, users, reports, areas, teams, messages, feedback, rewards +- `004_report_workflow.sql` + `reports.assigned_operator_id`, workflow indexes, richer media columns +- `006_completion_features.sql` + notifications, moderation flags, reward uniqueness +- `007_reference_data_and_intervention_links.sql` + category-to-team-category mapping, public intervention links -Important tables: +## Core Auth Tables +- `users` + identity, profile, password hash, points, `banned`, `must_change_password` +- `roles` +- `permissions` +- `role_permissions` +- `user_roles` +- `otp_challenges` + OTP flow, hashed codes, expiry, consumption +- `api_tokens` + bearer token hashes, expiry, revocation -- `users`: identity, personal data, password hash, points and moderation flags. -- `roles`, `permissions`, `role_permissions`, `user_roles`: authorization. -- `otp_challenges`: local OTP login flow. -- `api_tokens`: bearer tokens for the local API. -- `report_categories`: report classification. -- `areas`: named city areas with GeoJSON polygon boundaries. -- `team_categories`, `teams`: municipal intervention teams. -- `reports`: citizen reports with coordinates, address, status, area and assigned operator. -- `report_status_history`: immutable status transition log. -- `assignments`: report-to-team assignment history. -- `report_photos`: uploaded report media URL, original filename, content type, - media kind and size. The table name is historical; rows can be images or videos. -- `messages`: citizen/operator report conversation. -- `feedback`: citizen rating and comment after resolution. -- `reward_events`: indirect gamification points ledger. -- `notifications`: local in-app notifications for status changes, messages and - reward events. -- `moderation_flags`: report/message review queue for abusive or invalid content. +## Reference Data +- `report_categories` + citizen-facing report categories, `active` +- `areas` + named municipal areas, GeoJSON polygon in `boundary_geojson`, `active` +- `team_categories` + category of external intervention team +- `teams` + external teams, optional `area_id`, `team_category_id`, `active` +- `report_category_team_categories` + explicit compatibility mapping: + report category -> team category -GeoJSON is intentionally used instead of MySQL Spatial in milestone 1 to keep -the local setup simple and portable. +## Report Domain +- `reports` + owner citizen, category, inferred area, coordinates, address, description, + status, `assigned_operator_id` +- `report_status_history` + immutable workflow log +- `assignments` + historical report -> external team assignments, active row has `ended_at IS NULL` +- `report_photos` + historical name; stores both images and videos + current columns include `original_filename`, `content_type`, `media_kind`, + `size_bytes` +- `messages` + citizen/operator conversation per report +- `feedback` + one row per report, unique on `report_id` + +## Engagement / Moderation +- `reward_events` + points ledger, unique `(report_id, reason)` +- `notifications` + local in-app notifications +- `moderation_flags` + operator-created flags, admin-resolved moderation queue +- `intervention_access_links` + public revocable hashed links for external teams, tied to `report_id` and + `assignment_id` + +## Ownership Model +- Platform owner of report: `reports.assigned_operator_id` +- External team linkage: `assignments` +- Public field-team access: `intervention_access_links` + +## Status Model +`reports.status` enum: +- `OPEN` +- `UNDER_REVIEW` +- `ASSIGNED` +- `IN_PROGRESS` +- `RESOLVED` +- `REJECTED` +- `CLOSED` + +## Important Constraints / Semantics +- `users.email` unique +- `users.fiscal_code` unique +- `teams.name` unique +- `areas.name` unique +- `report_categories.name` unique +- `feedback.report_id` unique +- `reward_events(report_id, reason)` unique +- `api_tokens.token_hash` unique +- `intervention_access_links.token_hash` unique + +## Current Query Model +- No generated ORM models +- No `Mapper` +- Raw parameterized SQL through `drogon::orm::DbClient` +- JSON serialization helpers in `repositories/Sql.*` + +## Current Gaps +- No dedicated table for external team personnel +- No route-optimization/routing tables +- No audit table for intervention link access beyond `last_accessed_at` diff --git a/docs/LLM_CONTEXT.md b/docs/LLM_CONTEXT.md index 2bca72b..986af8d 100644 --- a/docs/LLM_CONTEXT.md +++ b/docs/LLM_CONTEXT.md @@ -1,22 +1,120 @@ # Visione Backend LLM Context -Visione is a C++20/Drogon backend for a municipal urban issue reporting system. -Citizens create reports with category, position, address, description and media -attachments. -Operators manage report queues, assign teams, update status, chat with citizens -and use intervention summaries. -Admins manage roles, users, areas, teams, reference data, moderation and stats. +## What This Repo Is +Visione is C++20 + Drogon backend for urban issue reporting. -The project runs locally on the host machine. Docker is used only for external -services such as MySQL and Mailpit. Real production deployment and Dockerizing -the backend are later delivery tasks. +Core actors: +- `citizen`: creates and follows reports +- `operator`: manages report lifecycle on platform +- `admin`: manages users/reference data and approves moderation -Important conventions: +External intervention teams are off-platform. They receive summary through +public revocable link generated by operator/admin. -- Code and database identifiers are English. -- The webapp is Italian-facing, so labels or seeded category names may be Italian. -- Use Drogon references from `/home/gabdevele/Projects/snitchitaly/backend/drogon` - and `https://drogonframework.github.io/drogon-docs/`. -- `AGENTS.md` is the durable agent instruction entrypoint. -- `docs/MILESTONE_01.md`, `docs/MILESTONE_02.md`, `docs/MILESTONE_03.md` and - `docs/MILESTONE_04.md` describe what has been implemented and what comes next. +## Environment +- Local backend runs outside Docker. +- Docker is only for local dependencies like MySQL and Mailpit. +- Primary commands: + - configure: `cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug` + - build: `cmake --build build` + - test: `ctest --test-dir build --output-on-failure` + - openapi: `cmake --build build --target openapi` + +## High-Signal Conventions +- Code, DB identifiers, API fields, comments: English. +- User-facing labels may be Italian. +- Keep controller thin, service owns business logic. +- Use parameterized SQL only. +- Prefer current code over old assumptions. Route truth is in controller headers. + +## Current Auth Model +- Registration creates `citizen`. +- Login paths: + - OTP email via Mailpit + - password fallback +- Auth token: bearer token from `api_tokens` +- Banned users cannot authenticate. +- `must_change_password` blocks most protected operator/admin flows until changed. + +## Current Report Workflow +### Citizen flow +1. Register/login. +2. `POST /reports` with category, coordinates, address, description, optional media array. +3. Backend infers `area_id` by GeoJSON polygon match when possible. +4. Report starts `OPEN`, status history row inserted. +5. Citizen can: + - list own reports + - read detail/history/media + - chat on own report + - upload more media up to 5 total + - read notifications + - read rewards + - leave feedback only when report is `RESOLVED` + +### Operator flow +1. Read queues: + - `/operator/reports/open` + - `/operator/reports/assigned` + - `/operator/reports/others` +2. Optional `POST /reports/{id}/take`: + `OPEN -> UNDER_REVIEW`, owner becomes operator. +3. `POST /reports/{id}/assignments`: + - if report `OPEN`, operator can assign directly without `take` + - owner becomes operator if report had no owner + - status becomes `ASSIGNED` + - current active assignment closes, new one inserted +4. Continue status flow: + - `ASSIGNED -> IN_PROGRESS -> RESOLVED -> CLOSED` + - rejection path to `REJECTED`, then `CLOSED` +5. Operator can chat with citizen on owned report. +6. Operator can create moderation flags for report/message on owned report. +7. Operator can request team suggestions and generate/revoke public intervention link. + +### Admin flow +- Create/update categories, areas, teams +- List users, edit roles, ban/unban +- Read stats +- Review moderation queue and resolve/dismiss flags +- Optionally reject report during moderation resolution + +## Team Model +- `teams` are external intervention squads, not platform users. +- `assignments` is history of which external team was assigned. +- `report_category_team_categories` maps report categories to compatible team categories. +- `GET /reports/{id}/team-suggestions` returns recommended active teams by: + - mapped team category + - same area first + - area-less team as fallback +- `POST /reports/{id}/intervention-link` creates revocable public link. +- `GET /interventions/{token}` returns public summary for field team: + report data, location, media, assigned team, masked citizen identity, phone. + +## Current Notifications / Rewards / Moderation +- Notifications: local in-app only. +- Reward points: awarded once per `(report_id, reason)`. +- Moderation: + - operator creates flag + - admin resolves/dismisses + - admin may reject report during moderation decision + +## Important Behavioral Truths +- Owner on platform is always `reports.assigned_operator_id`, not team. +- Feedback does not auto-close report. +- Public areas list is anonymous. +- Report categories require auth. +- Team list requires operator/admin auth. +- Moderation approval is admin-only. +- Public intervention token invalid/revoked/scoped-out returns `404`. + +## Files To Read First +- `docs/API_SPEC.md` +- `docs/ARCHITECTURE.md` +- `docs/openapi/visione.openapi.yaml` +- `controllers/*.h` +- `services/*.cc` +- `migrations/*.sql` + +## External References +- Local Drogon source: `/home/gabdevele/Projects/snitchitaly/backend/drogon` +- Drogon docs: `https://drogonframework.github.io/drogon-docs/` +- Durable agent instructions: `AGENTS.md` diff --git a/docs/MILESTONE_01.md b/docs/MILESTONE_01.md deleted file mode 100644 index a52a85c..0000000 --- a/docs/MILESTONE_01.md +++ /dev/null @@ -1,22 +0,0 @@ -# Milestone 01: Local Backend Foundation - -## Implemented in this milestone -- Project identity and documentation for Visione. -- Root `AGENTS.md` for future coding agents. -- Local Docker services for MySQL and Mailpit. -- Initial MySQL schema and seed data. -- Drogon backend structure for auth, reports, admin data and stats. -- OTP email delivery through Mailpit. -- Focused regression tests for technical endpoints and core validation helpers. -- Local admin seed: `admin@visione.local` / `ChangeMe123!`, with forced password change flag. -- Multipart report media upload endpoint storing files under `uploads/reports/`. -- PBKDF2-HMAC-SHA256 password hashes for credentials. Legacy SHA-256 hashes are intentionally rejected. - -## To do next -- Consider Argon2id/bcrypt and password reset flows before production hardening. -- Add real email provider configuration for non-local environments. -- Add CIE/SPID integration design and provider adapters. -- Expand frontend-driven API coverage as Vue screens are implemented. -- Add stronger frontend-driven endpoint coverage as Vue screens are implemented. -- Add notification channels, route optimization and open-data exports. -- Dockerize the backend only for final delivery/deployment. diff --git a/docs/MILESTONE_02.md b/docs/MILESTONE_02.md deleted file mode 100644 index 558baf2..0000000 --- a/docs/MILESTONE_02.md +++ /dev/null @@ -1,18 +0,0 @@ -# Milestone 02: Workflow Core and Hardening - -## Implemented in this milestone -- Documentation/config fixes from Milestone 01 review. -- Authorization context with roles, permissions and forced password-change state. -- Authenticated password change endpoint. -- Admin user listing, role assignment and ban/unban endpoints. -- Report ownership workflow with assigned operator and take endpoint. -- Valid report status transitions. -- Citizen report filters scoped to own reports. -- Report media upload validation and metadata storage. - -## To do next -- Add broader API tests once the frontend contracts stabilize. -- Add notification delivery for status changes. -- Add moderation flow for abusive reports/messages. -- Add open-data exports and statistics refinements. -- Design CIE/SPID integration. diff --git a/docs/MILESTONE_03.md b/docs/MILESTONE_03.md deleted file mode 100644 index 7cbc5b6..0000000 --- a/docs/MILESTONE_03.md +++ /dev/null @@ -1,20 +0,0 @@ -# Milestone 03: Media, OpenAPI and API Lab - -## Implemented in this milestone -- Removed legacy `sha256$` password verification and updated local admin seeds to - PBKDF2-HMAC-SHA256. -- Documented why PBKDF2-HMAC-SHA256 is used now: salted, intentionally slower - than plain SHA-256 and available through the existing OpenSSL dependency. -- Added report media uploads at `POST /api/v1/reports/{id}/media`. -- Supported JPEG, PNG, WebP, MP4, WebM and QuickTime uploads. -- Report attachments now use the `/media` upload path. -- Added generated OpenAPI JSON from Drogon route macros plus the manual - `docs/openapi/visione.openapi.yaml` contract. -- Exposed `/api/v1/openapi.json` and `/api/v1/docs`. -- Added a small Streamlit API lab for visual endpoint testing. - -## To do next -- Expand the manual OpenAPI contract as endpoints evolve. -- Add minimal media upload integration tests after endpoint contracts settle. -- Keep API and docs on `media` terminology only. -- Keep the Streamlit lab small and replace it with the real frontend later. diff --git a/docs/MILESTONE_04.md b/docs/MILESTONE_04.md deleted file mode 100644 index 6422a82..0000000 --- a/docs/MILESTONE_04.md +++ /dev/null @@ -1,22 +0,0 @@ -# Milestone 04: Backend Completion - -## Implemented in this milestone -- Added operator queues for assigned reports, open unassigned reports and reports - assigned to other operators. -- Added an operator intervention summary with report data, citizen contact data, - media and latest messages. -- Added local in-app notifications for status changes, team assignment, report - messages and reward events. -- Added citizen reward ledger APIs and automatic points for resolved reports. -- Added report/message moderation flags with admin/operator review and optional - report rejection. -- Expanded admin statistics with category, team, monthly and segmented - resolution-time aggregates. -- Added anonymized public open-data report export. -- Updated the manual OpenAPI contract for the new backend routes. - -## Intentionally not implemented -- Real CIE/SPID integration. -- Real SMS, Telegram, WhatsApp or production SMTP notification delivery. -- Route optimization for intervention teams. -- Frontend-specific pages outside the Streamlit API lab. diff --git a/docs/PROJECT_SPEC.md b/docs/PROJECT_SPEC.md index c763d62..61dd9e8 100644 --- a/docs/PROJECT_SPEC.md +++ b/docs/PROJECT_SPEC.md @@ -1,51 +1,125 @@ # Visione Project Spec +This file describes current implemented product shape, not aspirational backlog. + ## Goal -Visione lets citizens report urban problems and lets municipal staff manage, -assign and resolve interventions while preserving history and basic analytics. +Visione lets citizens report urban issues and lets municipal staff manage report +workflow, communicate with citizens, assign real external intervention teams, +moderate abuse, and inspect operational stats. ## Roles -- `citizen`: registers, requests OTP login, creates reports, follows own reports, - sends messages and leaves feedback after resolution. -- `operator`: views reports, takes open reports, assigns teams, updates managed - reports, messages citizens and flags abusive/fake citizen content on managed - reports. -- `admin`: manages users, roles, reference data, teams, areas and all reports. +- `citizen` + register, login, create reports, read own reports, chat, upload media, + read notifications, read rewards, leave feedback on resolved reports +- `operator` + read all reports, take open reports, own workflow, assign external teams, + change statuses, chat with citizens, create moderation flags, generate public + intervention links +- `admin` + all operator powers plus user/role management, reference data management, + moderation approval, bans, stats -The system admin seed must change password before using protected operational -or administrative APIs. +## Product Model +- Citizens use platform directly. +- Operators use platform directly. +- External intervention teams do not authenticate to platform. +- Teams receive public read-only intervention summary through revocable link. -## Report Statuses -- `OPEN`: created and waiting for review. -- `UNDER_REVIEW`: an operator is checking the report. -- `ASSIGNED`: a team has been assigned. -- `IN_PROGRESS`: intervention is active. -- `RESOLVED`: intervention was completed. -- `REJECTED`: report was invalid or abusive. -- `CLOSED`: administrative closure after feedback or final review. +## Implemented Features +### Auth +- citizen registration +- OTP login through Mailpit +- password login fallback +- bearer token auth +- forced first password change for seed admin +- profile read + limited self-edit (`first_name`, `last_name`, `phone`) -Valid milestone 2 transitions: +### Reports +- create report with category, coordinates, address, description +- optional inline media on creation +- later media upload up to total 5 files +- area inferred by GeoJSON polygon match +- citizen report list/detail/history/media +- operator queues: + - open unassigned + - assigned to current operator + - assigned to other operators -- `OPEN -> UNDER_REVIEW` when an operator takes the report. -- `UNDER_REVIEW -> ASSIGNED` when a team is assigned. -- `ASSIGNED -> IN_PROGRESS`. -- `IN_PROGRESS -> RESOLVED`. -- `RESOLVED -> CLOSED`. -- `OPEN/UNDER_REVIEW/ASSIGNED/IN_PROGRESS -> REJECTED` only for invalid reports. -- `REJECTED -> CLOSED`. +### Workflow +- statuses: + `OPEN`, `UNDER_REVIEW`, `ASSIGNED`, `IN_PROGRESS`, `RESOLVED`, `REJECTED`, `CLOSED` +- valid transitions enforced in backend +- `take` claims open report into `UNDER_REVIEW` +- team assignment can directly claim open report and move to `ASSIGNED` +- owner on platform remains operator, not team -## MVP Scope -- Local auth with email OTP delivered through Mailpit. -- MySQL persistence with migrations and seed data. -- Report creation/list/detail/status/message/assignment/feedback/moderation flows. -- Area polygons stored as GeoJSON and matched by backend logic. -- Operator queues for assigned, open and other-operator reports. -- Local in-app notifications and reward points ledger. -- Public anonymized open-data report export. -- Basic and segmented stats for administration. +### Communication +- citizen/operator report chat +- local in-app notifications for status, assignment, messages, rewards -## Out of Scope for the Local Backend MVP -- Real CIE integration. -- Real SMTP provider, SMS, Telegram or WhatsApp notifications. -- Route optimization. -- Full Docker packaging of the backend. +### External Team Handling +- external teams are reference data, not platform accounts +- active teams filtered for operator list +- suggestion endpoint recommends compatible teams by category mapping and area +- operator/admin can create or revoke public intervention summary link +- public summary exposes operational info needed on field: + location, description, media, assigned team, masked citizen identity, phone + +### Moderation +- operator can flag report or message on owned report +- admin resolves or dismisses flag +- admin can reject report during moderation decision + +### Rewards / Feedback +- reward events ledger +- points awarded on resolved/closed report, deduplicated by `(report_id, reason)` +- citizen feedback allowed only on `RESOLVED` +- feedback does not auto-close report + +### Admin +- create/update categories +- create/update areas +- create/update teams +- list users, update roles, ban/unban +- global stats including area/category/team resolution aggregates + +### Public Data +- active area list +- anonymized resolved/closed open-data export +- public intervention token endpoint + +## Current Workflow Summary +### Citizen +1. register/login +2. create report +3. report starts `OPEN` +4. follow history/status +5. chat with operator if needed +6. receive notifications +7. leave feedback when resolved + +### Operator +1. inspect open queue +2. `take` or assign external team directly +3. become report owner on platform +4. update status / chat / flag abuse +5. generate public intervention link for team + +### Admin +1. manage users and reference data +2. approve moderation decisions +3. inspect stats + +## Out Of Scope Today +- real CIE/SPID integration +- production email/SMS/Telegram/WhatsApp notifications +- routing optimization for teams +- team platform accounts +- citizen-facing frontend in this repo +- hard-delete lifecycle for reference data + +## Product Truth Rules +- Code truth beats stale docs. +- Route truth: controller headers. +- Business truth: services. +- DB truth: migrations.