Il testo della notifica viaggia ora cifrato dentro la push (aes128gcm, RFC
8188/8291) invece di essere recuperato dal service worker con una fetch al
risveglio. Era quella fetch a non chiudersi in tempo: ad app chiusa il browser
tiene vivo il worker pochi secondi, showNotification non veniva mai chiamata e
non compariva niente, mentre ad app aperta con la rete calda sembrava tutto a
posto. La POST porta anche Urgency: high, che chiede la consegna immediata
invece di far accumulare i messaggi fino al risveglio del dispositivo.
Cadono i pezzi che esistevano solo per rimediare al payload vuoto: la route
push-messaggio, la coda promemoria_push con la sua scadenza a 12 ore,
messaggioPalloniOggi() e il timeout nel worker. Tutti e tre i mittenti avevano
gia il testo pronto prima di inviare.
Il worker si aggiorna da solo all'avvio e a ogni ritorno in primo piano
(mantieniWorkerPushAggiornato): nella webapp installata quello vecchio puo
sopravvivere a lungo, e senza questo un dispositivo resterebbe fermo alla
versione che va a cercare il testo in rete.
Profilo -> Opzioni ha "Mandami una notifica di prova", visibile solo a notifiche
attive: manda una push a questo dispositivo e riporta stato HTTP, corpo della
risposta e se l'endpoint risulta davvero in push_subscriptions. Senza, "non
arriva" era cieco: ogni prova richiedeva un admin, un evento nello stato giusto
e una seconda persona, e la risposta del servizio push veniva buttata via.
inviaPush torna { stato, corpo } e logga il corpo sui rifiuti.
Dalle prove sul campo: a parita di server, iPhone installato da Home riceve ad
app chiusa. Su Android installato come webapp resta da verificare: il WebAPK e
un'app Android a se, con permesso notifiche (Android 13+) e voce batteria
distinti da quelli di Chrome. Annotato nei limiti noti.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
88 lines
17 KiB
Markdown
88 lines
17 KiB
Markdown
# Database CrAPP
|
|
|
|
Struttura del database Supabase (PostgreSQL) e ruolo di ogni tabella. Lo schema autoritativo
|
|
sono le migration in `supabase/migrations/`: **una tabella nuova va documentata qui nella
|
|
stessa modifica che la crea**. Le funzionalità future stanno in [ROADMAP.md](ROADMAP.md),
|
|
non in questo file.
|
|
|
|
## Permessi di scrittura
|
|
|
|
Chi può scrivere cosa, dopo la migration `m11_scritture_per_ruolo` (DD-023). La **lettura**
|
|
resta aperta a tutti gli autenticati su ogni tabella di questo elenco; `anon` non arriva a
|
|
nessuna di esse da M4 (DD-011).
|
|
|
|
| Tabella | Chi può scrivere |
|
|
| ---------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
| `eventi_app` | solo admin (nell'app li gestisce la rotta `/eventi`, già riservata) |
|
|
| `risposte_presenze`, `cacche_partita` | il giocatore sulla propria riga (`giocatore_id`), più gli admin |
|
|
| `pagelle_voti`, `mvp_voti`, `badge_social_voti` | il votante sui propri voti (`votante_id`), più gli admin |
|
|
| `turni_palloni`, `scout_sessioni`, `scout_live`, `scout_partite` | qualsiasi autenticato: nell'interfaccia non hanno gate |
|
|
| `profili_giocatore` | il giocatore sul proprio profilo, admin su tutti (DD-016, DD-017) |
|
|
| `giocatori_squadra` | admin; il giocatore può solo reclamare uno slot libero (DD-016) |
|
|
| `user_roles` | solo admin |
|
|
|
|
L'identità del giocatore è lo slot di `giocatori_squadra` con `auth_user_id = auth.uid()`.
|
|
La tabella è verificata da `test/integration/permessi.test.ts` contro il database locale.
|
|
|
|
## Anagrafica e utenti
|
|
|
|
| Tabella | Scopo | Note |
|
|
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `giocatori_squadra` | Anagrafica operativa della squadra, con ID testuali (`g1`…`gN`), dati gestiti dagli admin (nome, cognome, numero, ruolo), collegamento all'account (`auth_user_id`) ed email registrata (`email`). | Introdotta dalla migration `m1_giocatori_squadra`, source of truth della rosa (DD-015): `useRosa()` e gli altri punti che elencano i giocatori la leggono tramite `useGiocatoriSquadra()` (client) o `leggiGiocatoriSquadra()` (server), filtrando `attivo`. `src/lib/crapp-data.ts` resta solo come seed storico e fallback (`rosaFallback()`) quando il database non risponde, e come sorgente della data di nascita (non ancora una colonna di questa tabella). Vedi DD-015 e DD-016. La colonna `email` (migration `m5_email_giocatori_squadra`, impostabile anche da `/admin`) è la chiave del collegamento automatico account↔giocatore al primo accesso (DD-018): NULL finché non nota, oggi impostata per tutta la rosa attiva. Le colonne `numero_tessera`/`data_tessera` (migration `m8_tesseramento_csi`) tracciano chi è già tesserato al CSI; come `numero`/`ruolo` le scrive solo un admin, il trigger di M1/M5 le include tra i campi bloccati per chi reclama il proprio slot. |
|
|
| `giocatori` | Anagrafica giocatori con UUID. | Presente ma **non usata** dal codice attuale: la convergenza è rinviata (DD-012, DD-014). |
|
|
| `profili_giocatore` | Dati personali, metadati del documento d'identità, certificato medico e path dei file, in relazione 1:1 con `giocatori_squadra`. | Creata dalla migration `m2_profili_giocatore` (DD-016). Letta da `src/lib/profili.ts`; le policy mostrano al giocatore solo il proprio profilo e all'admin tutti. I file non stanno qui: la tabella conserva i path nel bucket. |
|
|
| `user_roles` | Ruoli applicativi (es. amministratore, giocatore). | Fonte dei permessi di amministrazione, letta da `src/lib/ruoli.ts` (DD-011). Il primo admin va inserito a mano; vedi [PROJECT_STATE.md](../PROJECT_STATE.md). |
|
|
|
|
`giocatori_squadra` / `giocatori` sono usate da: Squadra, Profili, Presenze, Scout, Badge, Pagelle.
|
|
|
|
## Storage
|
|
|
|
| Bucket | Scopo | Note |
|
|
| ------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `profili-giocatore` | Documento d'identità, certificato medico e foto tessera, in cartelle per giocatore (`<giocatore_id>/<sezione>.<est>`). | **Privato** e destinato a restare tale: contiene documenti e dati sanitari, che non devono mai avere URL pubblici (DD-016 regola 4). Il giocatore gestisce solo la propria cartella, l'admin può scaricare tutto tramite signed URL a scadenza breve. Creato dalla migration `m2_profili_giocatore`. |
|
|
| `avatar-giocatori` | Foto profilo mostrate nel cerchio avatar (Squadra, Profilo), un file per giocatore (`<giocatore_id>/avatar.jpg`). | **Pubblico**: foto informali, non documenti sensibili. Qualsiasi autenticato può caricare/sostituire/eliminare un file (nessun controllo per-proprietario, la maggior parte dei giocatori non ha ancora `auth_user_id` collegato, DD-018). Letto da `src/lib/avatar-store.ts`. Creato dalla migration `m6_avatar_giocatori`. |
|
|
|
|
## Eventi e presenze
|
|
|
|
| Tabella | Scopo | Note |
|
|
| ------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `eventi_app` | Eventi gestionali utilizzati dall'app. | Modello in uso dal codice attuale. |
|
|
| `risposte_presenze` | Risposte dei giocatori agli eventi. | Modello in uso dal codice attuale. `risposto_il` è l'istante della **prima** risposta (migration `m9_risposte_presenze_risposto_il`): confrontato con `eventi_app.creato_il` dà la serie "Conferme 24h". Un trigger lo rende immutabile, così un ripensamento non fa risultare rapida una risposta lenta — `aggiornato_il` resta l'ultima modifica. |
|
|
| `eventi` | Calendario generale: allenamenti, partite, eventi della squadra. | Modello "nuovo" con autenticazione e vincoli, non ancora adottato (DD-014). |
|
|
| `presenze` | Presenze agli eventi. | Come sopra (DD-014). |
|
|
|
|
## Scout
|
|
|
|
| Tabella | Scopo | Note |
|
|
| ---------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `scout_sessioni` | Chi ha il controllo dello Scout Live per una partita (blocco condiviso), una riga per evento. | Letta/scritta da `src/lib/scout-live.ts`. Prima viveva solo in `localStorage`: "Scout occupato da X" non funzionava mai tra dispositivi diversi (fix M7). |
|
|
| `scout_live` | Stato in corso (azioni non ancora concluse) di una sessione di Scout Live. | Serve esclusivamente per statistiche di squadra, mai per classifiche individuali (DD-008). Letta/scritta da `src/lib/scout-stato.ts`. |
|
|
| `scout_partite` | Archivio delle partite scoutate concluse (risultato, parziali, azioni). | Letta/scritta da `src/lib/scout-store.ts`. Prima il risultato finale finiva solo in `localStorage`: invisibile a chiunque non fosse il dispositivo di chi aveva chiuso la partita (fix M7). |
|
|
|
|
## Votazioni
|
|
|
|
| Tabella | Scopo | Note |
|
|
| ------------------- | ------------------------------------ | ------------------------ |
|
|
| `mvp_voti` | Voti MVP assegnati a fine partita. | |
|
|
| `pagelle_voti` | Voti anonimi assegnati ai giocatori. | Usati per il voto medio. |
|
|
| `badge_social_voti` | Voti social per i badge. | |
|
|
|
|
## Turni e notifiche
|
|
|
|
| Tabella | Scopo | Note |
|
|
| -------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
| `turni_palloni` | Gestione dei turni palloni. | Solo turni **confermati**. Gli allenamenti non ricevono proposta automatica (vedi [palloni.md](modules/palloni.md)); M10 azzera i turni salvati su allenamenti da oggi in poi. |
|
|
| `push_subscriptions` | Dispositivi registrati per le notifiche Push. | |
|
|
| `promemoria_push` | Non più usata. | Serviva da coda del testo quando la push partiva vuota; dal payload cifrato non la scrive né la legge nessuno. Tabella ancora presente, da eliminare con una migrazione. |
|
|
|
|
## Funzioni speciali
|
|
|
|
| Tabella | Scopo | Note |
|
|
| ---------------- | --------------------- | -------------------------------------- |
|
|
| `cacche_partita` | Sondaggio prepartita. | Usato per statistiche e badge segreti. |
|
|
|
|
## Badge
|
|
|
|
Non esiste una tabella dedicata: i badge vengono **calcolati a runtime** dall'applicazione a
|
|
partire dai dati esistenti (DD-007).
|