-- Migration : création de la table `photobooth.events` -- Réf. : docs/modele-donnees.md (section "Table `events`"), -- docs/decisions.md (expiration J+7, admin_token dans l'URL) -- -- Table déplacée de `public.pq_events` (2026-09-15) vers `photobooth.events` -- (2026-09-16) : l'isolation multi-projets vient désormais du schéma dédié -- `photobooth` (jamais exposé via PostgREST, cf. -- 20260916090000_create_photobooth_schema.sql), plus besoin de préfixe de -- table. -- -- ⚠️ Ne pas appliquer depuis cet agent (Kévin applique lui-même). -- pgcrypto est nécessaire pour gen_random_bytes() (génération de l'admin_token). create extension if not exists pgcrypto with schema extensions; create table if not exists photobooth.events ( id uuid primary key default gen_random_uuid(), slug text not null unique, nom text not null, date_evenement date not null, created_at timestamptz not null default now(), -- expire_at est calculé automatiquement (trigger ci-dessous) à partir de -- date_evenement + 7 jours, conformément à docs/decisions.md. expire_at timestamptz, -- Statut modélisé en texte + CHECK (plutôt qu'un type ENUM Postgres) car -- modele-donnees.md indique explicitement "à affiner" : un CHECK est plus -- simple à faire évoluer par migration qu'un type ENUM. statut text not null default 'actif' check (statut in ('actif', 'expire', 'archive')), -- Token secret d'accès admin. 32 octets aléatoires (256 bits) encodés en -- hexadécimal (pas de caractères spéciaux, utilisable tel quel dans une URL). admin_token text not null unique default encode(extensions.gen_random_bytes(32), 'hex'), client_nom text, facture_montant numeric check (facture_montant is null or facture_montant >= 0) ); comment on table photobooth.events is 'Un événement (mariage, soirée, etc.) : point d''entrée multi-tenant du service photobooth-qr. Schéma photobooth jamais exposé via PostgREST, cf. 20260916090000_create_photobooth_schema.sql.'; comment on column photobooth.events.slug is 'Identifiant lisible utilisé dans l''URL publique d''upload (/e/).'; comment on column photobooth.events.expire_at is 'Calculé automatiquement = date_evenement + 7 jours (trigger trg_events_set_expire_at). RGPD : suppression à cette échéance, cf. docs/rgpd.md.'; comment on column photobooth.events.statut is 'Statut de l''événement : actif, expire, archive.'; comment on column photobooth.events.admin_token is 'Token secret donnant accès à la galerie admin (/admin/?token=...). Jamais exposé à anon (seule la fonction public.get_event_for_admin, réservée à service_role, le renvoie).'; comment on column photobooth.events.client_nom is 'Nom du client, utile en V2/V3 (vente du service).'; comment on column photobooth.events.facture_montant is 'Montant facturé au client, utile en V2/V3.'; -- Calcule systématiquement expire_at à partir de date_evenement, pour -- garantir que la règle "J+7 après la date de l'événement" (docs/decisions.md) -- est toujours respectée, indépendamment de ce que l'appelant tente d'insérer. -- Fonction interne au schéma photobooth (pas un point de contrat public, -- pas besoin d'être dans public). create or replace function photobooth.events_set_expire_at() returns trigger language plpgsql as $$ begin new.expire_at := new.date_evenement + interval '7 days'; return new; end; $$; drop trigger if exists trg_events_set_expire_at on photobooth.events; create trigger trg_events_set_expire_at before insert or update of date_evenement on photobooth.events for each row execute function photobooth.events_set_expire_at(); -- Index utile pour le futur job d'expiration (recherche des événements -- actifs dont expire_at est dépassé). create index if not exists idx_events_statut_expire_at on photobooth.events (statut, expire_at);