Rende pubblica a tutta la squadra la data di nascita inserita dal Profilo
Prima veniva mostrata solo per i 17 giocatori del seed storico: chi la inseriva da sé restava con "Invalid Date" in Squadra, perché il dato finiva solo in profili_giocatore (privato). Un trigger ora lo sincronizza in giocatori_squadra.nascita, colonna pubblica a tutta la rosa. Include il backfill dei profili già compilati prima di M18 (M19), mancante nel push iniziale. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4d036f532d
commit
3f2ca30775
14 files changed
+471
-35
No files matched your search
@@ -0,0 +1,124 @@
|
||||
-- M18 — Data di nascita pubblica a tutta la squadra (DD-031)
|
||||
--
|
||||
-- In "Squadra" la data di nascita mostrava "Invalid Date" per chi l'aveva inserita da solo
|
||||
-- dal proprio Profilo: quel valore finisce in `profili_giocatore.data_nascita`, tabella con
|
||||
-- RLS "solo il proprio profilo o admin" (M2), mai letta da `useRosa()`/`useAnagraficaRosa()`
|
||||
-- (src/lib/rosa.ts). Quelle funzioni leggevano solo la mappa statica `nascitaPerId` del seed
|
||||
-- storico dei 17 giocatori originali (src/lib/crapp-data.ts) — pubblica perché bundlata nel
|
||||
-- client, ma senza voce per chi si è aggiunto dopo.
|
||||
--
|
||||
-- Questa migration aggiunge la colonna pubblica prevista fin da DD-015 ("giocatori_squadra
|
||||
-- non ha ancora questa colonna, DD-015 follow-up") e la tiene sincronizzata con
|
||||
-- `profili_giocatore.data_nascita`: un solo input nel form Profilo, due destinazioni — quella
|
||||
-- amministrativa esistente (RLS invariata) e questo specchio pubblico, leggibile da tutta la
|
||||
-- rosa attiva con la stessa policy di sempre.
|
||||
|
||||
ALTER TABLE public.giocatori_squadra ADD COLUMN nascita date;
|
||||
|
||||
-- Backfill una tantum dei 17 giocatori storici (stessi valori di `rosaCSI` in
|
||||
-- src/lib/crapp-data.ts, stesso ordine g1..g17 del seed di M1). Il trigger
|
||||
-- `enforce_giocatori_squadra_update` (attivo dalla migration M1) rifiuterebbe questi UPDATE:
|
||||
-- gira in un contesto senza `auth.uid()`, quindi non passa né il ramo admin né quello di
|
||||
-- reclamazione. Va disabilitato solo per il backfill e riattivato subito dopo.
|
||||
ALTER TABLE public.giocatori_squadra DISABLE TRIGGER enforce_giocatori_squadra_update;
|
||||
|
||||
UPDATE public.giocatori_squadra SET nascita = '1997-08-30' WHERE id = 'g1';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1996-12-07' WHERE id = 'g2';
|
||||
UPDATE public.giocatori_squadra SET nascita = '2000-04-28' WHERE id = 'g3';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1995-05-01' WHERE id = 'g4';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1995-08-13' WHERE id = 'g5';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1996-02-01' WHERE id = 'g6';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1997-07-04' WHERE id = 'g7';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1994-05-28' WHERE id = 'g8';
|
||||
UPDATE public.giocatori_squadra SET nascita = '2003-04-04' WHERE id = 'g9';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1998-11-30' WHERE id = 'g10';
|
||||
UPDATE public.giocatori_squadra SET nascita = '2000-02-02' WHERE id = 'g11';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1999-10-03' WHERE id = 'g12';
|
||||
UPDATE public.giocatori_squadra SET nascita = '2000-09-16' WHERE id = 'g13';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1996-12-13' WHERE id = 'g14';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1993-03-24' WHERE id = 'g15';
|
||||
UPDATE public.giocatori_squadra SET nascita = '2001-04-18' WHERE id = 'g16';
|
||||
UPDATE public.giocatori_squadra SET nascita = '1994-05-20' WHERE id = 'g17';
|
||||
|
||||
ALTER TABLE public.giocatori_squadra ENABLE TRIGGER enforce_giocatori_squadra_update;
|
||||
|
||||
-- Il trigger di sync (sotto) è SECURITY DEFINER e bypassa la RLS in scrittura su
|
||||
-- giocatori_squadra (di proprietà dell'owner della tabella), ma `enforce_giocatori_squadra_update`
|
||||
-- resta un trigger BEFORE UPDATE che scatta comunque: senza questo ramo, l'update annidato
|
||||
-- fallirebbe con l'eccezione già in vigore per chi non è admin.
|
||||
--
|
||||
-- Non apre una scrittura diretta dal client: la policy di update per un non-admin resta
|
||||
-- "Users can claim unassigned roster slot" (USING auth_user_id IS NULL) — una riga già
|
||||
-- collegata non la supera nemmeno, quindi l'unica strada che raggiunge questo ramo è la
|
||||
-- funzione di sync qui sotto, che gira con privilegi propri e ignora la RLS.
|
||||
CREATE OR REPLACE FUNCTION public.enforce_giocatori_squadra_update()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
BEGIN
|
||||
IF public.has_role(auth.uid(), 'admin'::public.app_role) THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
IF OLD.auth_user_id IS NULL
|
||||
AND NEW.auth_user_id = auth.uid()
|
||||
AND OLD.email IS NOT NULL
|
||||
AND lower(OLD.email) = lower(coalesce(auth.jwt() ->> 'email', ''))
|
||||
AND NEW.id IS NOT DISTINCT FROM OLD.id
|
||||
AND NEW.nome IS NOT DISTINCT FROM OLD.nome
|
||||
AND NEW.cognome IS NOT DISTINCT FROM OLD.cognome
|
||||
AND NEW.numero IS NOT DISTINCT FROM OLD.numero
|
||||
AND NEW.ruolo IS NOT DISTINCT FROM OLD.ruolo
|
||||
AND NEW.attivo IS NOT DISTINCT FROM OLD.attivo
|
||||
AND NEW.email IS NOT DISTINCT FROM OLD.email
|
||||
AND NEW.numero_tessera IS NOT DISTINCT FROM OLD.numero_tessera
|
||||
AND NEW.data_tessera IS NOT DISTINCT FROM OLD.data_tessera
|
||||
AND NEW.creato_il IS NOT DISTINCT FROM OLD.creato_il THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Slot già collegato al chiamante: può cambiare solo `nascita` (M18). È la via percorsa
|
||||
-- dal trigger di sync qui sotto, mai da un client (vedi commento sopra).
|
||||
IF OLD.auth_user_id IS NOT NULL
|
||||
AND OLD.auth_user_id = auth.uid()
|
||||
AND NEW.auth_user_id IS NOT DISTINCT FROM OLD.auth_user_id
|
||||
AND NEW.id IS NOT DISTINCT FROM OLD.id
|
||||
AND NEW.nome IS NOT DISTINCT FROM OLD.nome
|
||||
AND NEW.cognome IS NOT DISTINCT FROM OLD.cognome
|
||||
AND NEW.numero IS NOT DISTINCT FROM OLD.numero
|
||||
AND NEW.ruolo IS NOT DISTINCT FROM OLD.ruolo
|
||||
AND NEW.attivo IS NOT DISTINCT FROM OLD.attivo
|
||||
AND NEW.email IS NOT DISTINCT FROM OLD.email
|
||||
AND NEW.numero_tessera IS NOT DISTINCT FROM OLD.numero_tessera
|
||||
AND NEW.data_tessera IS NOT DISTINCT FROM OLD.data_tessera
|
||||
AND NEW.creato_il IS NOT DISTINCT FROM OLD.creato_il THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
RAISE EXCEPTION 'Aggiornamento non autorizzato su giocatori_squadra';
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.sincronizza_nascita_pubblica()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
UPDATE public.giocatori_squadra SET nascita = NULL WHERE id = OLD.giocatore_id;
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
|
||||
UPDATE public.giocatori_squadra SET nascita = NEW.data_nascita WHERE id = NEW.giocatore_id;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER sincronizza_nascita_pubblica
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.profili_giocatore
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.sincronizza_nascita_pubblica();
|
||||
@@ -0,0 +1,22 @@
|
||||
-- M19 — Backfill della nascita pubblica per i profili già esistenti (fix di M18)
|
||||
--
|
||||
-- M18 ha aggiunto `giocatori_squadra.nascita` e un trigger che la sincronizza da
|
||||
-- `profili_giocatore.data_nascita`, ma quel trigger scatta solo sulle scritture *future*:
|
||||
-- chi aveva già compilato il proprio profilo (data_nascita) prima che M18 fosse applicata è
|
||||
-- rimasto con `nascita` vuota, mentre M18 aveva backfillato a mano solo i 17 giocatori del
|
||||
-- seed storico. Effetto visibile: in Squadra la data di nascita compariva solo per quei 17,
|
||||
-- non per chi l'aveva inserita dal Profilo prima di oggi.
|
||||
--
|
||||
-- Stesso motivo di M18 per disabilitare temporaneamente `enforce_giocatori_squadra_update`:
|
||||
-- gira in un contesto senza `auth.uid()`, quindi non passerebbe nessun ramo del trigger.
|
||||
|
||||
ALTER TABLE public.giocatori_squadra DISABLE TRIGGER enforce_giocatori_squadra_update;
|
||||
|
||||
-- Ha la precedenza sui valori hardcoded di M18 per chi, tra i 17 storici, ha anche corretto
|
||||
-- la propria data dal Profilo nel frattempo: `profili_giocatore` è la fonte più aggiornata.
|
||||
UPDATE public.giocatori_squadra gs
|
||||
SET nascita = pg.data_nascita
|
||||
FROM public.profili_giocatore pg
|
||||
WHERE pg.giocatore_id = gs.id;
|
||||
|
||||
ALTER TABLE public.giocatori_squadra ENABLE TRIGGER enforce_giocatori_squadra_update;
|
||||
@@ -5,6 +5,12 @@
|
||||
-- I path dei file puntano a oggetti che nel bucket non esistono: i pulsanti di download
|
||||
-- falliscono finché non carichi qualcosa dall'app o dallo Studio (http://127.0.0.1:54323).
|
||||
|
||||
-- Il trigger di sync M18 (`sincronizza_nascita_pubblica`) aggiorna `giocatori_squadra.nascita`
|
||||
-- appena questo INSERT tocca `data_nascita`, ma qui gira senza un JWT reale: né il ramo admin
|
||||
-- né quello del proprietario dello slot del trigger di sicurezza passerebbero. Va disabilitato
|
||||
-- solo per questo seed, mai un problema in produzione dove il seed non gira.
|
||||
ALTER TABLE public.giocatori_squadra DISABLE TRIGGER enforce_giocatori_squadra_update;
|
||||
|
||||
INSERT INTO public.profili_giocatore
|
||||
(giocatore_id, data_nascita, luogo_nascita, indirizzo, telefono, email,
|
||||
documento_tipo, documento_numero, documento_rilasciato_da,
|
||||
@@ -29,6 +35,8 @@ VALUES
|
||||
'2027-09-15', 'g2/certificato.pdf', NULL)
|
||||
ON CONFLICT (giocatore_id) DO NOTHING;
|
||||
|
||||
ALTER TABLE public.giocatori_squadra ENABLE TRIGGER enforce_giocatori_squadra_update;
|
||||
|
||||
-- Il primo amministratore non si può seminare qui: `user_roles.user_id` punta a un utente
|
||||
-- di `auth.users`, che su un database appena creato non esiste ancora. Dopo il primo login
|
||||
-- (in locale come in produzione) basta una riga:
|
||||
|
||||
Reference in new issue
Block a user