feat: update API documentation and project specifications

This commit is contained in:
gabdevele
2026-06-15 12:01:34 +02:00 Unverified
parent 170fadbfe0
commit 088a2ed866
9 changed files with 547 additions and 257 deletions
+134 -55
View File
@@ -1,59 +1,110 @@
# Visione API Spec # Visione API Spec
Base path: `/api/v1`. Base path: `/api/v1`
## Technical ## Auth Header
- `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:
```http ```http
Authorization: Bearer <token> Authorization: Bearer <token>
``` ```
## Reports ## Roles
- `POST /reports`: create a report. - public: no auth
- `GET /reports`: list reports with optional `status`, `category_id`, `area_id`. - authenticated: any valid user
- `GET /report-categories`: list active report categories for authenticated users. - operator: `reports.manage` or admin
- `GET /operator/teams`: list active intervention teams for operators/admins. - admin: role/permission admin
- `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.
## User ## HTTP Semantics
- `GET /notifications`: list authenticated user notifications. - `200`: success
- `PATCH /notifications/{id}/read`: mark one notification as read. - `401`: missing or invalid auth
- `PATCH /notifications/read-all`: mark all authenticated user notifications as read. - `403`: authenticated but forbidden
- `GET /me/rewards`: return current points and reward event history. - `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 ## Admin Reference Data
- `POST /admin/categories` - `POST /admin/categories`
@@ -62,15 +113,43 @@ Authorization: Bearer <token>
- `PATCH /admin/areas/{id}` - `PATCH /admin/areas/{id}`
- `POST /admin/teams` - `POST /admin/teams`
- `PATCH /admin/teams/{id}` - `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` - `GET /admin/users`
- `PATCH /admin/users/{id}/roles` - `PATCH /admin/users/{id}/roles`
- `PATCH /admin/users/{id}/ban` - `PATCH /admin/users/{id}/ban`
- `GET /admin/stats/overview`
## Public Data ## Workflow Summary
- `GET /areas`: list active municipal areas. ### Citizen
- `GET /interventions/{token}`: public intervention summary for external teams. 1. register/login
- `GET /open-data/reports`: anonymized resolved/closed report export. 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.
+87 -38
View File
@@ -1,48 +1,97 @@
# Visione Backend Architecture # Visione Backend Architecture
## Layout ## Purpose
- `controllers/`: Drogon `HttpController` classes. Visione backend is C++20 + Drogon API for municipal urban issue reporting.
- `services/`: application/business logic. Citizens create reports. Operators own report workflow on platform. External
- `repositories/`: database helpers and migrations. intervention teams do not log into platform; operators can assign them and
- `common/`: reusable HTTP, auth, crypto, email and validation utilities. generate public read-only intervention links.
- `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.
## Drogon Usage ## Source Of Truth
Controllers use Drogon route macros and return JSON with - Route truth: `controllers/*.h`
`HttpResponse::newHttpJsonResponse`. Database access uses - Business truth: `services/*.cc`
`drogon::orm::DbClient` with raw parameterized SQL placeholders. - 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 ## Code Layout
in repository/database helpers because the schema is still evolving. - `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 ## Runtime Model
OTP emails are sent to Mailpit over SMTP. Mailpit keeps messages local and - Auth uses bearer tokens from `api_tokens`.
inspectable through its web UI. - 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 ## Role Model
User passwords use PBKDF2-HMAC-SHA256 with per-password random salts and 210000 - `citizen`: own reports only, own messages, feedback, notifications.
iterations. This is intentionally slower than a plain SHA-256 digest and is - `operator`: can read all reports, manage workflow, assign teams, create flags.
available through OpenSSL, which is already a Drogon dependency in this project. - `admin`: full admin + report management + moderation approval.
Legacy `sha256$` password hashes are rejected instead of silently accepted.
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 ## OpenAPI Generation
`scripts/generate_openapi.py` scans Drogon `ADD_METHOD_TO` route macros in `scripts/generate_openapi.py` scans `ADD_METHOD_TO(...)` in controller headers.
`controllers/*.h` and extracts only paths, HTTP methods, operation ids, tags and Generated route skeletons merge with `docs/openapi/visione.openapi.yaml`.
path parameters. It merges those route skeletons with Build fails if manual spec contains operation without matching route, unless
`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
`x-manual-only: true`. `x-manual-only: true`.
## Visione Lab Output path:
`scripts/lab/` is a Streamlit dev frontend for visual endpoint testing. It is - `build/openapi/visione.openapi.json`
intentionally separate from the real product frontend and should stay small:
forms, uploads and response rendering only. Run it with uv from `scripts/lab/`. ## 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.
+97 -25
View File
@@ -1,31 +1,103 @@
# Visione Database Schema # 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. ## Migration Set
The project does not use Drogon generated models or `Mapper<T>` while the schema - `001_initial_schema.sql`
is still evolving. 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. ## Reference Data
- `roles`, `permissions`, `role_permissions`, `user_roles`: authorization. - `report_categories`
- `otp_challenges`: local OTP login flow. citizen-facing report categories, `active`
- `api_tokens`: bearer tokens for the local API. - `areas`
- `report_categories`: report classification. named municipal areas, GeoJSON polygon in `boundary_geojson`, `active`
- `areas`: named city areas with GeoJSON polygon boundaries. - `team_categories`
- `team_categories`, `teams`: municipal intervention teams. category of external intervention team
- `reports`: citizen reports with coordinates, address, status, area and assigned operator. - `teams`
- `report_status_history`: immutable status transition log. external teams, optional `area_id`, `team_category_id`, `active`
- `assignments`: report-to-team assignment history. - `report_category_team_categories`
- `report_photos`: uploaded report media URL, original filename, content type, explicit compatibility mapping:
media kind and size. The table name is historical; rows can be images or videos. report category -> team category
- `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.
GeoJSON is intentionally used instead of MySQL Spatial in milestone 1 to keep ## Report Domain
the local setup simple and portable. - `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<T>`
- 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`
+115 -17
View File
@@ -1,22 +1,120 @@
# Visione Backend LLM Context # Visione Backend LLM Context
Visione is a C++20/Drogon backend for a municipal urban issue reporting system. ## What This Repo Is
Citizens create reports with category, position, address, description and media Visione is C++20 + Drogon backend for urban issue reporting.
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.
The project runs locally on the host machine. Docker is used only for external Core actors:
services such as MySQL and Mailpit. Real production deployment and Dockerizing - `citizen`: creates and follows reports
the backend are later delivery tasks. - `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. ## Environment
- The webapp is Italian-facing, so labels or seeded category names may be Italian. - Local backend runs outside Docker.
- Use Drogon references from `/home/gabdevele/Projects/snitchitaly/backend/drogon` - Docker is only for local dependencies like MySQL and Mailpit.
and `https://drogonframework.github.io/drogon-docs/`. - Primary commands:
- `AGENTS.md` is the durable agent instruction entrypoint. - configure: `cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug`
- `docs/MILESTONE_01.md`, `docs/MILESTONE_02.md`, `docs/MILESTONE_03.md` and - build: `cmake --build build`
`docs/MILESTONE_04.md` describe what has been implemented and what comes next. - 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`
-22
View File
@@ -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: `[email protected]` / `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.
-18
View File
@@ -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.
-20
View File
@@ -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.
-22
View File
@@ -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.
+114 -40
View File
@@ -1,51 +1,125 @@
# Visione Project Spec # Visione Project Spec
This file describes current implemented product shape, not aspirational backlog.
## Goal ## Goal
Visione lets citizens report urban problems and lets municipal staff manage, Visione lets citizens report urban issues and lets municipal staff manage report
assign and resolve interventions while preserving history and basic analytics. workflow, communicate with citizens, assign real external intervention teams,
moderate abuse, and inspect operational stats.
## Roles ## Roles
- `citizen`: registers, requests OTP login, creates reports, follows own reports, - `citizen`
sends messages and leaves feedback after resolution. register, login, create reports, read own reports, chat, upload media,
- `operator`: views reports, takes open reports, assigns teams, updates managed read notifications, read rewards, leave feedback on resolved reports
reports, messages citizens and flags abusive/fake citizen content on managed - `operator`
reports. read all reports, take open reports, own workflow, assign external teams,
- `admin`: manages users, roles, reference data, teams, areas and all reports. 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 ## Product Model
or administrative APIs. - 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 ## Implemented Features
- `OPEN`: created and waiting for review. ### Auth
- `UNDER_REVIEW`: an operator is checking the report. - citizen registration
- `ASSIGNED`: a team has been assigned. - OTP login through Mailpit
- `IN_PROGRESS`: intervention is active. - password login fallback
- `RESOLVED`: intervention was completed. - bearer token auth
- `REJECTED`: report was invalid or abusive. - forced first password change for seed admin
- `CLOSED`: administrative closure after feedback or final review. - 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. ### Workflow
- `UNDER_REVIEW -> ASSIGNED` when a team is assigned. - statuses:
- `ASSIGNED -> IN_PROGRESS`. `OPEN`, `UNDER_REVIEW`, `ASSIGNED`, `IN_PROGRESS`, `RESOLVED`, `REJECTED`, `CLOSED`
- `IN_PROGRESS -> RESOLVED`. - valid transitions enforced in backend
- `RESOLVED -> CLOSED`. - `take` claims open report into `UNDER_REVIEW`
- `OPEN/UNDER_REVIEW/ASSIGNED/IN_PROGRESS -> REJECTED` only for invalid reports. - team assignment can directly claim open report and move to `ASSIGNED`
- `REJECTED -> CLOSED`. - owner on platform remains operator, not team
## MVP Scope ### Communication
- Local auth with email OTP delivered through Mailpit. - citizen/operator report chat
- MySQL persistence with migrations and seed data. - local in-app notifications for status, assignment, messages, rewards
- 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.
## Out of Scope for the Local Backend MVP ### External Team Handling
- Real CIE integration. - external teams are reference data, not platform accounts
- Real SMTP provider, SMS, Telegram or WhatsApp notifications. - active teams filtered for operator list
- Route optimization. - suggestion endpoint recommends compatible teams by category mapping and area
- Full Docker packaging of the backend. - 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.