Dashboard amministratore e profilo per il tesseramento
Implementa la voce "Dashboard amministratore" della roadmap v1.1, come descritta in docs/modules/profilo-giocatore.md, insieme alla parte di profilo che la alimenta. - /admin: stato dei profili della squadra, download di documento, certificato e foto tessera, export CSV con i 12 campi del tesseramento CSI. Un certificato scaduto non conta come valido. - Profilo giocatore: dati personali, documento (fronte e retro), certificato e foto tessera, con widget di completamento in Home che sparisce al 100%. - I permessi di amministrazione arrivano da user_roles (DD-011) e non più dalla lista di nomi in crapp-data.ts, che resta come ponte finché VITE_AUTH_OBBLIGATORIA non viene acceso in produzione. - Login Google via Supabase Auth: al primo accesso l'account si collega a uno slot libero di giocatori_squadra, e il vincolo lo fa rispettare il trigger di M1 (DD-016 regola 2). Migration additive: M2 crea profili_giocatore, M3 il bucket privato profili-giocatore. Nessuna tabella v1.0 viene toccata, quindi si possono applicare senza cambiare il comportamento attuale dell'app. supabase/config.toml e seed.sql configurano lo stack locale: serve perché il progetto Supabase è uno solo, condiviso tra sviluppo e produzione. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
-- M3 — Bucket privato per documenti, certificati e foto tessera (DD-016 regole 3 e 4)
|
||||
-- Migration additiva. Il bucket nasce privato e resta privato: documenti d'identità e
|
||||
-- dati sanitari non devono mai essere raggiungibili da un URL pubblico. L'accesso avviene
|
||||
-- solo con client autenticato o con signed URL a scadenza breve generata per gli admin.
|
||||
|
||||
INSERT INTO storage.buckets (id, name, public)
|
||||
VALUES ('profili-giocatore', 'profili-giocatore', false)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Convenzione dei path: `<giocatore_id>/<sezione>.<estensione>` (es. `g4/certificato.pdf`).
|
||||
-- La prima cartella è l'ID del giocatore: è così che si riconosce il proprietario del file.
|
||||
CREATE POLICY "Il giocatore gestisce i propri file" ON storage.objects
|
||||
FOR ALL TO authenticated
|
||||
USING (
|
||||
bucket_id = 'profili-giocatore'
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.giocatori_squadra g
|
||||
WHERE g.id = (storage.foldername(name))[1] AND g.auth_user_id = auth.uid()
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
bucket_id = 'profili-giocatore'
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.giocatori_squadra g
|
||||
WHERE g.id = (storage.foldername(name))[1] AND g.auth_user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Gli admin scaricano i file di tutti, ma non li modificano: i documenti restano
|
||||
-- in mano al giocatore che li ha caricati.
|
||||
CREATE POLICY "Gli admin scaricano tutti i file dei profili" ON storage.objects
|
||||
FOR SELECT TO authenticated
|
||||
USING (
|
||||
bucket_id = 'profili-giocatore'
|
||||
AND public.has_role(auth.uid(), 'admin'::public.app_role)
|
||||
);
|
||||
Reference in New Issue
Block a user