Implementa il ruolo allenatore nell'app
- Modello: tipo sugli slot, inRosa() al posto del filtro su attivo in tutte le letture della rosa di gioco (presenze, convocati, voti, palloni, classifiche, badge, sollecito); gli allenatori restano solo nei compleanni e in Squadra. - Permessi: user_roles letto una volta per tutti i ruoli; usePuoGestireEventi() apre /eventi e il sollecito presenze anche all'allenatore, lato server con richiediGestoreEventi(). Palloni, sondaggio e messaggi restano all'admin. - Identità: useGiocatoreInCampo() è null per l'allenatore, così non risponde alle presenze e non vota; useSonoAllenatore() decide cosa si vede. - Profilo: per l'allenatore solo Docs (nome, cognome, nascita, telefono, email) e Opzioni; card avatar e tab Opzioni estratte per riusarle. - Home, Squadra e partita: niente statistiche personali, badge, cacche e obiettivi per l'allenatore; in Rosa compare con la dicitura «Allenatore». - Admin: tipo in «Aggiungi», allenatori a parte in Profili, fuori da conteggi ed export CSI. Test unit su rosa di gioco e completamento, route test sull'allenatore. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
15a010855e
commit
2ec72fd08f
35 files changed
+1215
-577
No files matched your search
@@ -7,9 +7,9 @@
|
||||
* è un timestamp in base 36 e compare negli URL che la squadra si scambia — quindi non
|
||||
* può fare da credenziale.
|
||||
*
|
||||
* Tutte e tre le route che mandano notifiche partono da un pulsante riservato agli
|
||||
* amministratori, quindi il controllo è uno solo: `richiediAdmin` verifica il token della
|
||||
* sessione Supabase e poi il ruolo in `user_roles`. Torna `null` quando la richiesta può
|
||||
* Le route che mandano notifiche partono da un pulsante riservato agli amministratori, e il
|
||||
* sollecito presenze anche agli allenatori (DD-034): `richiediAdmin` e `richiediGestoreEventi`
|
||||
* verificano il token della sessione Supabase e poi il ruolo in `user_roles`. Torna `null` quando la richiesta può
|
||||
* proseguire, altrimenti la `Response` di rifiuto già pronta.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,14 @@ function tokenDaRichiesta(request: Request): string | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Lascia passare solo un amministratore autenticato. `401` se manca o non vale il token,
|
||||
* `403` se il token è buono ma l'utente non è admin.
|
||||
* Lascia passare solo un utente autenticato con almeno uno dei `ruoli`. `401` se manca o non
|
||||
* vale il token, `403` se il token è buono ma il ruolo no.
|
||||
*/
|
||||
export async function richiediAdmin(request: Request): Promise<Response | null> {
|
||||
async function richiediRuolo(
|
||||
request: Request,
|
||||
ruoli: Array<"admin" | "allenatore">,
|
||||
rifiuto: string,
|
||||
): Promise<Response | null> {
|
||||
const token = tokenDaRichiesta(request);
|
||||
if (!token) return new Response("Autenticazione richiesta", { status: 401 });
|
||||
|
||||
@@ -37,13 +41,22 @@ export async function richiediAdmin(request: Request): Promise<Response | null>
|
||||
if (error || !utente?.user) return new Response("Sessione non valida", { status: 401 });
|
||||
|
||||
// Stessa fonte di `src/lib/ruoli.ts`: i permessi stanno solo in `user_roles` (DD-011).
|
||||
const { data: ruolo } = await supabaseAdmin
|
||||
const { data: righe } = await supabaseAdmin
|
||||
.from("user_roles")
|
||||
.select("role")
|
||||
.eq("user_id", utente.user.id)
|
||||
.eq("role", "admin")
|
||||
.maybeSingle();
|
||||
.in("role", ruoli);
|
||||
|
||||
if (!ruolo) return new Response("Riservato agli amministratori", { status: 403 });
|
||||
if (!righe?.length) return new Response(rifiuto, { status: 403 });
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Solo un amministratore: promemoria palloni, messaggi, sondaggio, notifiche attive. */
|
||||
export function richiediAdmin(request: Request): Promise<Response | null> {
|
||||
return richiediRuolo(request, ["admin"], "Riservato agli amministratori");
|
||||
}
|
||||
|
||||
/** Admin o allenatore: chi gestisce gli eventi può anche sollecitare le presenze (DD-034). */
|
||||
export function richiediGestoreEventi(request: Request): Promise<Response | null> {
|
||||
return richiediRuolo(request, ["admin", "allenatore"], "Riservato a admin e allenatori");
|
||||
}
|
||||
Reference in new issue
Block a user