14 KiB
API Face Review: riepilogo completo degli endpoint
Scopo generale
Le API Face Review gestiscono il flusso completo di revisione dei risultati del riconoscimento facciale:
- lettura delle run HDBSCAN;
- visualizzazione dei cluster automatici;
- visualizzazione dei volti classificati come noise;
- generazione protetta delle miniature dei volti;
- creazione e rinomina delle persone;
- assegnazione di uno o più cluster alla stessa persona;
- esclusione di singoli volti da una persona;
- assegnazione singola o multipla dei volti non raggruppati;
- conservazione separata del risultato automatico e della decisione manuale.
Principio fondamentale:
cluster automatico != persona confermata
Più cluster possono essere collegati allo stesso person_id senza fondere o cancellare i cluster originali.
Prefisso e autenticazione
Tutti gli endpoint descritti usano il prefisso:
/face-review
Le route sono protette dal middleware JWT globale. Il client deve inviare:
Authorization: Bearer <token>
Il middleware JWT verifica il token e salva il payload in:
req.user
Il nome dell'utente autenticato è disponibile in:
req.user.name
Risoluzione dell'utente visualizzato
La funzione resolveFaceReviewUser(req) applica queste regole:
- un utente normale senza parametro
uservede esclusivamente i propri dati; - Admin può specificare
?user=Fabioper visualizzare la libreria di un altro utente; - un utente normale non può selezionare un altro utente;
- cluster, volti, fotografie e persone vengono sempre verificati lato backend.
Esempi:
JWT name = Fabio
GET /face-review/context
=> target_user = Fabio
JWT name = Admin
GET /face-review/context?user=Fabio
=> target_user = Fabio
JWT name = Fabio
GET /face-review/context?user=Jessica
=> HTTP 403
Endpoint di sola lettura
GET /face-review/context
Restituisce il contesto di autenticazione e la run completata più recente dell'utente visualizzato.
Richiesta utente normale:
GET /face-review/context
Authorization: Bearer <token>
Richiesta Admin:
GET /face-review/context?user=Fabio
Authorization: Bearer <admin-token>
Risposta:
{
"authenticated_user": "Fabio",
"target_user": "Fabio",
"is_admin": false,
"can_select_user": false,
"latest_run_id": "c44546a7-55a8-423c-b3b3-7dd5da671a1d"
}
Possibili errori:
401se il token non è valido o manca l'identità;403se un utente normale prova a selezionare un altro utente;404se l'utente richiesto non possiede dati riconoscibili.
GET /face-review/runs
Restituisce la cronologia delle run di clustering per l'utente visualizzato.
GET /face-review/runs
GET /face-review/runs?user=Fabio
Ogni run contiene, tra gli altri:
id;user;status;scope;embedding_count;min_cluster_size;min_samples;metric;selection_method;engineeengine_version;cluster_count;clustered_faces;noise_faces;noise_percent;- date di avvio e completamento.
Risposta:
{
"user": "Fabio",
"runs": [
{
"id": "...",
"status": "done",
"embedding_count": 30,
"cluster_count": 5,
"clustered_faces": 14,
"noise_faces": 16
}
]
}
GET /face-review/clusters
Restituisce i cluster e i relativi volti. Senza run_id usa la run completata più recente.
GET /face-review/clusters
GET /face-review/clusters?run_id=<run-id>
GET /face-review/clusters?user=Fabio
Per ogni cluster restituisce:
- identificativo e label HDBSCAN;
- dimensione;
- parametri della run;
- eventuale
person_ide nome della persona; - elenco dei volti appartenenti al cluster.
Ogni volto include:
id;photo_idephoto_name;face_index;- detector score;
- probabilità HDBSCAN;
- stato di revisione;
- rotazione;
- URL protetto della miniatura.
Risposta sintetica:
{
"user": "Fabio",
"run": {
"id": "...",
"status": "done"
},
"clusters": [
{
"id": "...",
"label": 2,
"size": 4,
"person_id": "...",
"person_name": "Jessica",
"faces": []
}
]
}
GET /face-review/clusters/:clusterId
Restituisce un singolo cluster, purché appartenga all'utente autorizzato.
GET /face-review/clusters/<cluster-id>
Restituisce i dati del cluster e tutti i suoi volti. Risponde 404 se il cluster non esiste o appartiene a un altro utente.
GET /face-review/unclustered
Restituisce i volti noise della run più recente che non sono ancora assegnati manualmente a una persona.
La query applica concettualmente:
m.noise_flag = 1
AND f.person_id IS NULL
Richiesta:
GET /face-review/unclustered
Quando un noise viene assegnato a una persona:
cluster_idrestaNULL;labelresta-1;noise_flagresta1;person_idviene valorizzato;review_statusdiventaaccepted;- il volto non compare più in questo endpoint.
GET /face-review/persons
Restituisce le persone dell'utente visualizzato.
GET /face-review/persons
Risposta:
{
"user": "Fabio",
"persons": [
{
"id": "...",
"user": "Fabio",
"name": "Jessica",
"created_at": "...",
"updated_at": "..."
}
]
}
GET /face-review/persons/:personId
Restituisce il dettaglio di una persona e tutti i volti assegnati, inclusi quelli provenienti da cluster e quelli assegnati manualmente dal noise.
GET /face-review/persons/<person-id>
La risposta include:
- dati della persona;
- numero totale di volti;
- numero di cluster collegati;
- elenco dei volti;
- cluster di provenienza, se presente;
- miniatura protetta.
Un volto noise assegnato manualmente viene restituito con:
cluster_id = null
cluster_label = null
GET /face-review/faces/:faceId/thumbnail
Genera una miniatura WebP protetta del volto.
GET /face-review/faces/<encoded-face-id>/thumbnail
GET /face-review/faces/<encoded-face-id>/thumbnail?size=256
Dimensione predefinita:
192 x 192 pixel
Intervallo accettato per size:
64 <= size <= 512
Pipeline dell'immagine:
- recupera il volto e la fotografia filtrandoli per utente;
- risolve il percorso tramite
resolveFilePathForEntryId(); - applica esclusivamente
photos.rotation; - non applica automaticamente l'orientamento EXIF;
- aggiunge un margine al bounding box;
- crea un crop quadrato;
- ridimensiona in WebP.
Header restituiti:
Content-Type: image/webp
Cache-Control: private, max-age=3600
X-Content-Type-Options: nosniff
Il frontend deve caricare la miniatura tramite fetch() con Bearer token e trasformarla in Object URL, perché un normale elemento immagine non aggiunge automaticamente l'header Authorization.
Endpoint di scrittura per le persone
POST /face-review/persons
Crea una nuova persona per l'utente visualizzato.
POST /face-review/persons
Content-Type: application/json
Authorization: Bearer <token>
Body:
{
"name": "Jessica"
}
Comportamento:
- normalizza spazi e nome;
- rifiuta nomi vuoti;
- limita il nome a 120 caratteri;
- cerca una persona omonima senza distinguere maiuscole e minuscole;
- se esiste già, restituisce la persona esistente;
- altrimenti crea un UUID e inserisce il record.
Codici:
201se la persona è stata creata;200se il nome esisteva già;400se il nome è assente o non valido.
PATCH /face-review/persons/:personId
Rinomina una persona.
PATCH /face-review/persons/<person-id>
Content-Type: application/json
Authorization: Bearer <token>
Body:
{
"name": "Nuovo nome"
}
Comportamento:
- verifica che la persona appartenga all'utente;
- normalizza e valida il nome;
- blocca duplicati dello stesso utente;
- aggiorna
updated_at; - non modifica volti o cluster.
Possibili errori:
400nome non valido;404persona non trovata;409nome già utilizzato da un'altra persona.
DELETE /face-review/persons/:personId/faces/:faceId
Rimuove un volto da una persona senza cancellare il volto e senza cancellare il cluster automatico.
DELETE /face-review/persons/<person-id>/faces/<encoded-face-id>
Authorization: Bearer <token>
Operazioni transazionali:
- verifica persona, volto e utente;
- imposta
faces.person_id = NULL; - mantiene
faces.cluster_idinvariato; - marca la membership come
excluded; - salva
reviewed_byereviewed_at; - inserisce o aggiorna
person_face_exclusions.
Un volto rimosso da una persona può essere riassociato successivamente dal cluster o dalla UI appropriata.
Endpoint di assegnazione cluster-persona
POST /face-review/clusters/:clusterId/assign-person
Assegna un cluster a una persona esistente e consente di includere o escludere singoli volti.
POST /face-review/clusters/<cluster-id>/assign-person
Content-Type: application/json
Authorization: Bearer <token>
Body:
{
"person_id": "<person-id>",
"included_face_ids": [
"face:...:0",
"face:...:0"
]
}
Comportamento transazionale:
- verifica che persona e cluster appartengano allo stesso utente;
- verifica che tutti gli ID inclusi appartengano al cluster;
- per i volti inclusi:
- imposta
faces.person_id; - marca la membership come
accepted; - rimuove eventuali esclusioni precedenti;
- imposta
- per i volti non inclusi:
- non li assegna alla persona;
- marca la membership come
excluded; - registra
person_face_exclusions;
- imposta
face_clusters.person_id.
Questo endpoint permette di collegare più cluster alla stessa persona. Per esempio:
Cluster 2 -> Jessica
Cluster 3 -> Jessica
Cluster 4 -> Jessica
I cluster restano distinti e conservano la cronologia HDBSCAN.
Endpoint di assegnazione dei noise
POST /face-review/faces/:faceId/assign-person
Assegna manualmente un singolo volto a una persona.
POST /face-review/faces/<encoded-face-id>/assign-person
Content-Type: application/json
Authorization: Bearer <token>
Body:
{
"person_id": "<person-id>"
}
Comportamento:
- verifica persona, volto e utente;
- aggiorna
faces.person_id; - non modifica
faces.cluster_id; - accetta la membership più recente del volto;
- elimina l'eventuale esclusione precedente dalla stessa persona.
Per un noise:
cluster_id resta NULL
label resta -1
noise_flag resta 1
review_status diventa accepted
POST /face-review/unclustered/assign-person
Assegna più volti noise selezionati alla stessa persona in una singola transazione.
Persona esistente:
{
"person_id": "<person-id>",
"face_ids": [
"face:...:0",
"face:...:1"
]
}
Nuova persona:
{
"name": "Nuova persona",
"face_ids": [
"face:...:0",
"face:...:1"
]
}
Controlli principali:
- almeno un volto deve essere selezionato;
- deve essere presente
person_idoppurename; - usa la run completata più recente;
- tutti i volti devono essere noise della run corrente;
- tutti devono appartenere all'utente;
- tutti devono avere
person_id IS NULL; - se un solo volto non è valido, l'intera transazione viene annullata.
Per ogni volto valido:
faces.person_idviene impostato;cluster_idresta invariato, normalmenteNULL;- la membership passa ad
accepted; - vengono salvati revisore e data;
- eventuali esclusioni pregresse dalla persona vengono eliminate.
Risposta:
{
"ok": true,
"user": "Fabio",
"person": {
"id": "...",
"name": "Jessica"
},
"person_created": false,
"run_id": "...",
"assigned_face_ids": [],
"assigned_count": 3
}
Tabelle coinvolte
face_cluster_runs
Conserva ogni esecuzione del clustering e i relativi parametri, metriche e stato.
face_clusters
Conserva i cluster automatici. Il campo person_id collega il cluster alla persona confermata senza eliminare il cluster.
face_cluster_memberships
Conserva l'appartenenza di ogni volto a una run e a un eventuale cluster.
Campi rilevanti:
run_id;cluster_id;face_id;label;probability;noise_flag;review_status;reviewed_by;reviewed_at.
Stati di revisione:
suggested
accepted
excluded
faces
Conserva bounding box, embedding e stato corrente del volto.
Distinzione importante:
cluster_idrappresenta il raggruppamento automatico corrente;person_idrappresenta la persona confermata manualmente.
persons
Conserva le persone nominate, sempre separate per utente.
person_face_exclusions
Registra che uno specifico volto non appartiene a una specifica persona. Impedisce di perdere la decisione manuale durante successive revisioni.
Regole di coerenza
Volto clusterizzato e accettato
cluster_id = valorizzato
person_id = valorizzato
noise_flag = 0
review_status = accepted
Volto noise non revisionato
cluster_id = NULL
person_id = NULL
label = -1
noise_flag = 1
review_status = suggested
Volto noise assegnato manualmente
cluster_id = NULL
person_id = valorizzato
label = -1
noise_flag = 1
review_status = accepted
Volto escluso da una persona
person_id = NULL
cluster_id = invariato
review_status = excluded
person_face_exclusions = riga presente
Transazioni e sicurezza
Tutte le operazioni di assegnazione o rimozione importanti vengono eseguite in transazione. Se una validazione o una scrittura fallisce, viene eseguito il rollback completo.
Le API non accettano percorsi filesystem dal browser. Le miniature partono dal solo faceId, recuperano la fotografia dal database e risolvono internamente il file.
Ogni operazione verifica la coerenza tra:
utente della run
utente della fotografia
utente della persona
utente autenticato o selezionato da Admin
Conversione dei segnaposto in Markdown standard
Questo file usa segnaposto intenzionali:
§1al posto di intestazione livello 1;§2al posto di intestazione livello 2;§3al posto di intestazione livello 3;-
- ` al posto dell'apice rovescio inline.
Esempio di conversione con sed, usando caratteri ASCII generati dalla shell:
hash=$(printf '\043')
tick=$(printf '\140')
sed \
-e "s/^§1 /${hash} /" \
-e "s/^§2 /${hash}${hash} /" \
-e "s/^§3 /${hash}${hash}${hash} /" \
-e "s/```/${tick}${tick}${tick}/g" \
-e "s/`/${tick}/g" \
face_review_endpoints_summary.md \
> face_review_endpoints_standard.md
Nota: è importante sostituire prima ``` e poi il singolo `, come mostrato nel comando.