-> Refonte du système de GESTION DES ROULEMENTS de matériels roulants -> Optimisation rendue BDD
1608 lines
77 KiB
PL/PgSQL
1608 lines
77 KiB
PL/PgSQL
-- ============================================================================
|
|
-- FERROVIA PANEL — SCHÉMA COMPLET ET UNIFIÉ DE LA BASE DE DONNÉES
|
|
-- Fichier : SCHEME.sql (Racine du projet)
|
|
-- Compatible : Supabase PostgreSQL (Cloud & Self-Hosted), Supabase Auth (GoTrue), Supabase Studio
|
|
-- Date de révision : 2026-10-03
|
|
-- ============================================================================
|
|
-- Ce script SQL contient l'intégralité de l'infrastructure de la base de données :
|
|
-- 1. Extensions (pgcrypto, uuid-ossp)
|
|
-- 2. Types ENUM (service_type, station_type, transport_type)
|
|
-- 3. Fonctions utilitaires & Triggers updated_at
|
|
-- 4. Fonction d'audit (audit_logs)
|
|
-- 5. Les 37 Tables complètes avec types exacts et contraintes
|
|
-- 6. Index de performance (B-Tree, GIN JSONB, clés étrangères)
|
|
-- 7. Synchronisation native Supabase Auth (auth.users <-> public.utilisateurs)
|
|
-- 8. Fonctions RPC métier (authenticate_user, get_gare_details, etc.)
|
|
-- 9. Politiques de sécurité RLS (Row Level Security)
|
|
-- 10. Configuration des Buckets Supabase Storage (fiches-horaires, materiel-roulant, banque-annonces)
|
|
-- 11. Privilèges & Grants (anon, authenticated, service_role)
|
|
-- 12. Données initiales (Comptes Administrateurs, Paramètres, Types de train)
|
|
-- ============================================================================
|
|
|
|
-- ============================================================================
|
|
-- 1. EXTENSIONS POSTGRESQL
|
|
-- ============================================================================
|
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA extensions;
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions;
|
|
|
|
-- Rendre les fonctions accessibles sans préfixe
|
|
SET search_path = public, extensions, auth, pg_temp;
|
|
|
|
-- ============================================================================
|
|
-- 2. TYPES ENUM
|
|
-- ============================================================================
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."service_type" AS ENUM ('TER', 'TGV', 'Intercités', 'Fret');
|
|
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."station_type" AS ENUM ('interurbaine', 'ville');
|
|
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."transport_type" AS ENUM ('bus', 'tram', 'metro', 'tram-train', 'train');
|
|
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- 3. FONCTIONS UTILITAIRES & TRIGGERS UPDATED_AT
|
|
-- ============================================================================
|
|
CREATE OR REPLACE FUNCTION "public"."set_updated_at"()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION "public"."handle_updated_at"()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION "public"."update_updated_at_column"()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION "public"."update_travaux_updated_at"()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION "public"."update_utilisateurs_updated_at"()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
-- Stub RPC GTFS
|
|
CREATE OR REPLACE FUNCTION "public"."import_gtfs_rpc"(payload json)
|
|
RETURNS json
|
|
LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
RETURN json_build_object('status', 'ok', 'created', 0, 'warnings', json_build_array());
|
|
EXCEPTION WHEN OTHERS THEN
|
|
RETURN json_build_object('status', 'error', 'message', SQLERRM);
|
|
END;
|
|
$$;
|
|
|
|
-- ============================================================================
|
|
-- 4. FONCTION D'AUDIT
|
|
-- ============================================================================
|
|
CREATE OR REPLACE FUNCTION "public"."log_audit"()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql SECURITY DEFINER
|
|
SET search_path = public, extensions, auth, pg_temp
|
|
AS $$
|
|
DECLARE
|
|
v_changed_by_text text;
|
|
v_changed_by uuid;
|
|
BEGIN
|
|
-- Tenter de récupérer l'UUID de l'utilisateur connecté via JWT
|
|
v_changed_by_text := COALESCE(
|
|
current_setting('request.jwt.claim.sub', true),
|
|
current_setting('jwt.claims.user_id', true),
|
|
current_setting('jwt.claims.sub', true)
|
|
);
|
|
|
|
IF v_changed_by_text IS NOT NULL AND v_changed_by_text <> '' THEN
|
|
BEGIN
|
|
v_changed_by := v_changed_by_text::uuid;
|
|
EXCEPTION WHEN others THEN
|
|
v_changed_by := NULL;
|
|
END;
|
|
ELSE
|
|
v_changed_by := NULL;
|
|
END IF;
|
|
|
|
IF (TG_OP = 'INSERT') THEN
|
|
INSERT INTO public.audit_logs(table_name, operation, record_id, changed_by, new_data, changed_at)
|
|
VALUES (TG_TABLE_NAME, 'INSERT', NEW.id, v_changed_by, row_to_json(NEW)::jsonb, NOW());
|
|
RETURN NEW;
|
|
ELSIF (TG_OP = 'UPDATE') THEN
|
|
INSERT INTO public.audit_logs(table_name, operation, record_id, changed_by, old_data, new_data, changed_at)
|
|
VALUES (TG_TABLE_NAME, 'UPDATE', NEW.id, v_changed_by, row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb, NOW());
|
|
RETURN NEW;
|
|
ELSIF (TG_OP = 'DELETE') THEN
|
|
INSERT INTO public.audit_logs(table_name, operation, record_id, changed_by, old_data, changed_at)
|
|
VALUES (TG_TABLE_NAME, 'DELETE', OLD.id, v_changed_by, row_to_json(OLD)::jsonb, NOW());
|
|
RETURN OLD;
|
|
END IF;
|
|
RETURN NULL;
|
|
EXCEPTION WHEN OTHERS THEN
|
|
-- Ne jamais bloquer la transaction principale en cas d'erreur de journalisation
|
|
RETURN COALESCE(NEW, OLD);
|
|
END;
|
|
$$;
|
|
|
|
-- ============================================================================
|
|
-- 5. TABLES (ORDONNÉES PAR DÉPENDANCES)
|
|
-- ============================================================================
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.01 audit_logs
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."audit_logs" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"table_name" text NOT NULL,
|
|
"operation" text NOT NULL,
|
|
"record_id" uuid,
|
|
"changed_by" uuid,
|
|
"old_data" jsonb,
|
|
"new_data" jsonb,
|
|
"changed_at" timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_audit_logs_table" ON "public"."audit_logs" ("table_name");
|
|
CREATE INDEX IF NOT EXISTS "idx_audit_logs_record" ON "public"."audit_logs" ("record_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_audit_logs_changed_by" ON "public"."audit_logs" ("changed_by");
|
|
CREATE INDEX IF NOT EXISTS "idx_audit_logs_changed_at" ON "public"."audit_logs" ("changed_at" DESC);
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.02 utilisateurs (Profils métier et administrateurs du panel)
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."utilisateurs" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"email" varchar(255) NOT NULL UNIQUE,
|
|
"nom" varchar(100) NOT NULL DEFAULT '',
|
|
"prenom" varchar(100) NOT NULL DEFAULT '',
|
|
"role" varchar(50) NOT NULL DEFAULT 'utilisateur',
|
|
"actif" boolean NOT NULL DEFAULT true,
|
|
"password_hash" varchar(255),
|
|
"photo_url" text,
|
|
"region_admin" boolean DEFAULT false,
|
|
"region_id" varchar(100),
|
|
"region_nom" varchar(255),
|
|
"permissions" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "utilisateurs_email_lower_idx" ON "public"."utilisateurs" (lower(email::text));
|
|
CREATE INDEX IF NOT EXISTS "idx_utilisateurs_email" ON "public"."utilisateurs" ("email");
|
|
CREATE INDEX IF NOT EXISTS "idx_utilisateurs_role" ON "public"."utilisateurs" ("role");
|
|
CREATE INDEX IF NOT EXISTS "idx_utilisateurs_actif" ON "public"."utilisateurs" ("actif");
|
|
CREATE INDEX IF NOT EXISTS "idx_utilisateurs_region_id" ON "public"."utilisateurs" ("region_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.03 profils_utilisateurs (Voyageurs / App mobile & site public)
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."profils_utilisateurs" (
|
|
"id" uuid NOT NULL PRIMARY KEY,
|
|
"email" text NOT NULL,
|
|
"prenom" text,
|
|
"nom" text,
|
|
"date_naissance" date,
|
|
"telephone" text,
|
|
"carte_reduction" text,
|
|
"gares_favorites" text[] DEFAULT '{}'::text[],
|
|
"lignes_favorites" text[] DEFAULT '{}'::text[],
|
|
"cartes_bancaires" jsonb DEFAULT '[]'::jsonb,
|
|
"mode_paiement_favori" text,
|
|
"commandes" jsonb DEFAULT '[]'::jsonb,
|
|
"panier" jsonb DEFAULT '[]'::jsonb,
|
|
"trajets_favoris" text[] DEFAULT '{}'::text[],
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_profils_email" ON "public"."profils_utilisateurs" ("email");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.04 gares
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."gares" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"nom" text NOT NULL,
|
|
"type" varchar(50) NOT NULL DEFAULT 'ville',
|
|
"services" "public"."service_type"[] NOT NULL DEFAULT '{}'::service_type[],
|
|
"informations" jsonb DEFAULT '{}'::jsonb,
|
|
"quais_hall" jsonb DEFAULT '[]'::jsonb,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_gares_nom" ON "public"."gares" ("nom");
|
|
CREATE INDEX IF NOT EXISTS "idx_gares_type" ON "public"."gares" ("type");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.05 gare_quais
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."gare_quais" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE,
|
|
"nom" text NOT NULL,
|
|
"distance_metres" integer NOT NULL DEFAULT 0,
|
|
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_gare_quais_gare_id" ON "public"."gare_quais" ("gare_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.06 gare_transports
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."gare_transports" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE,
|
|
"type" "public"."transport_type" NOT NULL,
|
|
"ligne" text,
|
|
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_gare_transports_gare_id" ON "public"."gare_transports" ("gare_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.07 types_train
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."types_train" (
|
|
"id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
"nom" varchar(100) NOT NULL,
|
|
"code" varchar(20),
|
|
"description" text,
|
|
"logo_url" text,
|
|
"logo_color_url" text,
|
|
"couleur" varchar(50),
|
|
"type_train" varchar(100),
|
|
"color" varchar(50),
|
|
"actif" boolean DEFAULT true,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_types_train_nom" ON "public"."types_train" ("nom");
|
|
CREATE INDEX IF NOT EXISTS "idx_types_train_actif" ON "public"."types_train" ("actif");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.08 depots_technicentres (Dépôts, Technicentres et Ateliers de Maintenance)
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."depots_technicentres" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"nom" varchar(255) NOT NULL,
|
|
"code" varchar(50) UNIQUE,
|
|
"ville" varchar(100) NOT NULL,
|
|
"gare_proche" varchar(255),
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"capacite_max" integer NOT NULL DEFAULT 20,
|
|
"voies_disponibles" integer,
|
|
"specialites" jsonb DEFAULT '[]'::jsonb,
|
|
"region_id" varchar(100),
|
|
"region_nom" varchar(255),
|
|
"adresse" text,
|
|
"description" text,
|
|
"actif" boolean NOT NULL DEFAULT true,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_depots_nom" ON "public"."depots_technicentres" ("nom");
|
|
CREATE INDEX IF NOT EXISTS "idx_depots_code" ON "public"."depots_technicentres" ("code");
|
|
CREATE INDEX IF NOT EXISTS "idx_depots_ville" ON "public"."depots_technicentres" ("ville");
|
|
CREATE INDEX IF NOT EXISTS "idx_depots_actif" ON "public"."depots_technicentres" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_depots_region" ON "public"."depots_technicentres" ("region_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.09 "materiel-roulant"
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."materiel-roulant" (
|
|
"id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
"nom" varchar(255) NOT NULL,
|
|
"nom_technique" varchar(255),
|
|
"capacite" integer,
|
|
"type_train" varchar(100),
|
|
"numero_serie" varchar(100),
|
|
"image_url" text,
|
|
"images" jsonb DEFAULT '[]'::jsonb,
|
|
"serie" varchar(100),
|
|
"type" varchar(100),
|
|
"places_assises" integer,
|
|
"vitesse_max" integer,
|
|
"alimentation" varchar(100),
|
|
"photo_url" text,
|
|
"description" text,
|
|
"actif" boolean DEFAULT true,
|
|
"depot_id" uuid REFERENCES "public"."depots_technicentres"("id") ON DELETE SET NULL,
|
|
"depot_attache" varchar(255),
|
|
"statut_maintenance" varchar(100) DEFAULT 'OPERATIONNEL',
|
|
"date_prochaine_maintenance" date,
|
|
"kilometrage" integer DEFAULT 0,
|
|
"couplage_famille" varchar(100),
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
|
|
-- Migration idempotente pour tables déjà créées
|
|
DO $$
|
|
BEGIN
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "nom_technique" varchar(255);
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "capacite" integer;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "type_train" varchar(100);
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "numero_serie" varchar(100);
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "image_url" text;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "images" jsonb DEFAULT '[]'::jsonb;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "places_assises" integer;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "vitesse_max" integer;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "alimentation" varchar(100);
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "photo_url" text;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "description" text;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "actif" boolean DEFAULT true;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "depot_id" uuid REFERENCES "public"."depots_technicentres"("id") ON DELETE SET NULL;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "depot_attache" varchar(255);
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "statut_maintenance" varchar(100) DEFAULT 'OPERATIONNEL';
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "date_prochaine_maintenance" date;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "kilometrage" integer DEFAULT 0;
|
|
ALTER TABLE "public"."materiel-roulant" ADD COLUMN IF NOT EXISTS "couplage_famille" varchar(100);
|
|
EXCEPTION WHEN OTHERS THEN
|
|
NULL;
|
|
END $$;
|
|
|
|
CREATE INDEX IF NOT EXISTS "idx_materiel_serie" ON "public"."materiel-roulant" ("serie");
|
|
CREATE INDEX IF NOT EXISTS "idx_materiel_actif" ON "public"."materiel-roulant" ("actif");
|
|
CREATE INDEX IF NOT EXISTS "idx_materiel_type_train" ON "public"."materiel-roulant" ("type_train");
|
|
CREATE INDEX IF NOT EXISTS "idx_materiel_numero_serie" ON "public"."materiel-roulant" ("numero_serie");
|
|
CREATE INDEX IF NOT EXISTS "idx_materiel_depot_id" ON "public"."materiel-roulant" ("depot_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_materiel_depot_attache" ON "public"."materiel-roulant" ("depot_attache");
|
|
CREATE INDEX IF NOT EXISTS "idx_materiel_statut_maintenance" ON "public"."materiel-roulant" ("statut_maintenance");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.09 services_annuels
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."services_annuels" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"nom" varchar(255) NOT NULL,
|
|
"code" varchar(50),
|
|
"date_debut" date NOT NULL,
|
|
"date_fin" date NOT NULL,
|
|
"description" text,
|
|
"actif" boolean DEFAULT false,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_services_annuels_dates" ON "public"."services_annuels" ("date_debut", "date_fin");
|
|
CREATE INDEX IF NOT EXISTS "idx_services_annuels_actif" ON "public"."services_annuels" ("actif") WHERE deleted_at IS NULL;
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.10 lignes
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."lignes" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"nom" varchar(255) NOT NULL,
|
|
"code" varchar(50),
|
|
"gare_depart_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"gare_arrivee_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"actif" boolean DEFAULT true,
|
|
"fh_url" text,
|
|
"region" varchar(100),
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_lignes_actif" ON "public"."lignes" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_lignes_fh_url" ON "public"."lignes" ("fh_url");
|
|
CREATE INDEX IF NOT EXISTS "idx_lignes_region" ON "public"."lignes" ("region");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.11 lignes_gares
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."lignes_gares" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"ligne_id" uuid NOT NULL REFERENCES "public"."lignes"("id") ON DELETE CASCADE,
|
|
"gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE,
|
|
"ordre" integer NOT NULL DEFAULT 1,
|
|
"desserte" varchar(50) DEFAULT 'tous',
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_lignes_gares_ligne_id" ON "public"."lignes_gares" ("ligne_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_lignes_gares_gare_id" ON "public"."lignes_gares" ("gare_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_lignes_gares_ordre" ON "public"."lignes_gares" ("ligne_id", "ordre");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.12 horaires (Circulations principales)
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."horaires" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"numero_train" varchar(50) NOT NULL,
|
|
"nom_train" varchar(255),
|
|
"code_mission" varchar(50),
|
|
"sens" varchar(20) DEFAULT 'aller',
|
|
"type_train" varchar(100),
|
|
"type_train_id" bigint REFERENCES "public"."types_train"("id") ON DELETE SET NULL,
|
|
"transporteur" varchar(100) DEFAULT 'SNCF Voyageurs',
|
|
"gare_depart_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"gare_depart_nom" varchar(255) NOT NULL,
|
|
"heure_depart" time,
|
|
"gare_arrivee_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"gare_arrivee_nom" varchar(255) NOT NULL,
|
|
"heure_arrivee" time,
|
|
"gares_desservies" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"attributions_quais" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
"snapshot_gares_desservies" jsonb DEFAULT '[]'::jsonb,
|
|
"circule_lundi" boolean DEFAULT true,
|
|
"circule_mardi" boolean DEFAULT true,
|
|
"circule_mercredi" boolean DEFAULT true,
|
|
"circule_jeudi" boolean DEFAULT true,
|
|
"circule_vendredi" boolean DEFAULT true,
|
|
"circule_samedi" boolean DEFAULT true,
|
|
"circule_dimanche" boolean DEFAULT true,
|
|
"circule_jours_feries" boolean DEFAULT false,
|
|
"circule_dimanches_feries" boolean DEFAULT false,
|
|
"date_debut" timestamptz,
|
|
"date_fin" timestamptz,
|
|
"jours_personnalises" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"jours_non_circulation" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"calendrier_circulation" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"periode_validite" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
"materiel_roulant_id" bigint REFERENCES "public"."materiel-roulant"("id") ON DELETE SET NULL,
|
|
"composition_train" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL,
|
|
"ligne_nom" varchar(255),
|
|
"region_id" varchar(100),
|
|
"region" varchar(255),
|
|
"service_annuel_id" uuid REFERENCES "public"."services_annuels"("id") ON DELETE SET NULL,
|
|
"service_annuel_nom" varchar(255),
|
|
"service_type" varchar(50) DEFAULT 'TER',
|
|
"est_substitution" boolean NOT NULL DEFAULT false,
|
|
"motif_substitution" varchar(255),
|
|
"substitution_disponible" boolean NOT NULL DEFAULT false,
|
|
"horaire_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL,
|
|
"substitutions_associees" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"sillon_id" text,
|
|
"tranche" varchar(50),
|
|
"gestionnaire_infrastructure" varchar(100) DEFAULT 'SNCF Réseau',
|
|
"vitesse_max_kmh" integer,
|
|
"statut" varchar(50) NOT NULL DEFAULT 'a_l_heure',
|
|
"retard_minutes" integer NOT NULL DEFAULT 0,
|
|
"retard_motif" text,
|
|
"informations_voyageurs" text,
|
|
"actif" boolean NOT NULL DEFAULT true,
|
|
"metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
"notes" text,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_numero_train" ON "public"."horaires" ("numero_train");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_gare_depart" ON "public"."horaires" ("gare_depart_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_gare_arrivee" ON "public"."horaires" ("gare_arrivee_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_departs" ON "public"."horaires" ("gare_depart_id", "heure_depart");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_arrivees" ON "public"."horaires" ("gare_arrivee_id", "heure_arrivee");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_actif" ON "public"."horaires" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_type_train" ON "public"."horaires" ("type_train_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_materiel" ON "public"."horaires" ("materiel_roulant_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_ligne" ON "public"."horaires" ("ligne_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_service_annuel" ON "public"."horaires" ("service_annuel_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_substitution" ON "public"."horaires" ("est_substitution");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_region_id" ON "public"."horaires" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_dates" ON "public"."horaires" ("date_debut", "date_fin");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_statut" ON "public"."horaires" ("statut");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_gares_desservies_gin" ON "public"."horaires" USING gin ("gares_desservies" jsonb_path_ops);
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_attributions_quais_gin" ON "public"."horaires" USING gin ("attributions_quais");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_calendrier_gin" ON "public"."horaires" USING gin ("calendrier_circulation" jsonb_path_ops);
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_metadata_gin" ON "public"."horaires" USING gin ("metadata");
|
|
|
|
DO $$
|
|
BEGIN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN IF NOT EXISTS "couplage_train_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL;
|
|
ALTER TABLE "public"."horaires" ADD COLUMN IF NOT EXISTS "couplage_numero_train" varchar(50);
|
|
ALTER TABLE "public"."horaires" ADD COLUMN IF NOT EXISTS "manoeuvres" jsonb DEFAULT '[]'::jsonb;
|
|
EXCEPTION WHEN OTHERS THEN
|
|
NULL;
|
|
END $$;
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.13 horaires_gares_desservies
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."horaires_gares_desservies" (
|
|
"id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
"horaire_id" uuid REFERENCES "public"."horaires"("id") ON DELETE CASCADE,
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"gare_nom" varchar(255) NOT NULL,
|
|
"heure_arrivee" time,
|
|
"heure_depart" time,
|
|
"ordre" integer NOT NULL DEFAULT 0,
|
|
"quai" varchar(50),
|
|
"quai_id" uuid REFERENCES "public"."gare_quais"("id") ON DELETE SET NULL,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_gares_desservies_horaire" ON "public"."horaires_gares_desservies" ("horaire_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_gares_desservies_ordre" ON "public"."horaires_gares_desservies" ("horaire_id", "ordre");
|
|
CREATE INDEX IF NOT EXISTS "idx_hgd_gare_id" ON "public"."horaires_gares_desservies" ("gare_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.14 horaires_substitutions
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."horaires_substitutions" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"horaire_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL,
|
|
"region_id" varchar(100),
|
|
"region" varchar(255),
|
|
"numero_train" varchar(50),
|
|
"type_train" varchar(100),
|
|
"type_train_id" bigint REFERENCES "public"."types_train"("id") ON DELETE SET NULL,
|
|
"gare_depart_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"gare_depart_nom" varchar(255),
|
|
"heure_depart" time,
|
|
"gare_arrivee_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"gare_arrivee_nom" varchar(255),
|
|
"heure_arrivee" time,
|
|
"date_debut" timestamptz,
|
|
"date_fin" timestamptz,
|
|
"circule_lundi" boolean DEFAULT true,
|
|
"circule_mardi" boolean DEFAULT true,
|
|
"circule_mercredi" boolean DEFAULT true,
|
|
"circule_jeudi" boolean DEFAULT true,
|
|
"circule_vendredi" boolean DEFAULT true,
|
|
"circule_samedi" boolean DEFAULT true,
|
|
"circule_dimanche" boolean DEFAULT true,
|
|
"circule_jours_feries" boolean DEFAULT false,
|
|
"jours_personnalises" jsonb DEFAULT '[]'::jsonb,
|
|
"jours_non_circulation" jsonb DEFAULT '[]'::jsonb,
|
|
"materiel_roulant_id" bigint REFERENCES "public"."materiel-roulant"("id") ON DELETE SET NULL,
|
|
"ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL,
|
|
"ligne_nom" varchar(255),
|
|
"service_annuel_id" uuid REFERENCES "public"."services_annuels"("id") ON DELETE SET NULL,
|
|
"service_annuel_nom" varchar(255),
|
|
"est_substitution" boolean DEFAULT true,
|
|
"motif_substitution" varchar(255),
|
|
"gares_desservies" jsonb DEFAULT '[]'::jsonb,
|
|
"composition_train" jsonb DEFAULT '[]'::jsonb,
|
|
"actif" boolean DEFAULT true,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_substitutions_region" ON "public"."horaires_substitutions" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_substitutions_horaire" ON "public"."horaires_substitutions" ("horaire_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_substitutions_actif" ON "public"."horaires_substitutions" ("actif") WHERE deleted_at IS NULL;
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.15 perturbations
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."perturbations" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"horaire_id" uuid NOT NULL REFERENCES "public"."horaires"("id") ON DELETE CASCADE,
|
|
"type_perturbation" varchar(50) NOT NULL CHECK (type_perturbation IN ('retard','suppression','modification_parcours')),
|
|
"duree_retard_minutes" integer,
|
|
"cause" varchar(100) NOT NULL CHECK (cause IN ('incident_technique','accident_personne','conditions_meteorologiques','travaux','mouvement_social','intervention_police','affluence','malveillance','autre')),
|
|
"cause_details" text,
|
|
"sillon_substitution_id" uuid REFERENCES "public"."horaires"("id") ON DELETE SET NULL,
|
|
"date_debut" timestamptz NOT NULL DEFAULT now(),
|
|
"date_fin" timestamptz,
|
|
"actif" boolean DEFAULT true,
|
|
"notes" text,
|
|
"region_id" varchar(100),
|
|
"created_by" uuid,
|
|
"perturbations_arrets" jsonb,
|
|
"jours_perturbation" jsonb,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_perturbations_horaire" ON "public"."perturbations" ("horaire_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_perturbations_type" ON "public"."perturbations" ("type_perturbation");
|
|
CREATE INDEX IF NOT EXISTS "idx_perturbations_actif" ON "public"."perturbations" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_perturbations_dates" ON "public"."perturbations" ("date_debut","date_fin");
|
|
CREATE INDEX IF NOT EXISTS "idx_perturbations_sillon_substitution" ON "public"."perturbations" ("sillon_substitution_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_perturbations_region_id" ON "public"."perturbations" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_perturbations_created_by" ON "public"."perturbations" ("created_by");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.16 perturbations_modifications_parcours
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."perturbations_modifications_parcours" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"perturbation_id" uuid NOT NULL REFERENCES "public"."perturbations"("id") ON DELETE CASCADE,
|
|
"type_modification" varchar(20) NOT NULL CHECK (type_modification IN ('gare_supprimee','gare_ajoutee','horaire_modifie')),
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"gare_nom" varchar(255) NOT NULL,
|
|
"nouvelle_heure_arrivee" time,
|
|
"nouvelle_heure_depart" time,
|
|
"ordre" integer,
|
|
"created_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_modif_parcours_perturbation" ON "public"."perturbations_modifications_parcours" ("perturbation_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_modif_parcours_gare" ON "public"."perturbations_modifications_parcours" ("gare_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.17 info_trafic
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."info_trafic" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" varchar(255) NOT NULL,
|
|
"description" text,
|
|
"type" varchar(50) NOT NULL CHECK (type IN ('perturbation','travaux','greve','meteo','autre')),
|
|
"severite" varchar(20) NOT NULL CHECK (severite IN ('info','warning','critical')),
|
|
"ligne" varchar(100),
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"date_debut" timestamptz NOT NULL DEFAULT now(),
|
|
"date_fin" timestamptz,
|
|
"actif" boolean DEFAULT true,
|
|
"region_id" varchar(100),
|
|
"created_by" uuid,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_info_trafic_actif" ON "public"."info_trafic" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_info_trafic_dates" ON "public"."info_trafic" ("date_debut","date_fin");
|
|
CREATE INDEX IF NOT EXISTS "idx_info_trafic_region_id" ON "public"."info_trafic" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_info_trafic_created_by" ON "public"."info_trafic" ("created_by");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.18 evenements
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."evenements" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" varchar(255) NOT NULL,
|
|
"description" text,
|
|
"lieu" varchar(255),
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"date_debut" timestamptz NOT NULL,
|
|
"date_fin" timestamptz,
|
|
"image_url" varchar(500),
|
|
"lien_externe" varchar(500),
|
|
"actif" boolean DEFAULT true,
|
|
"visible_site" boolean DEFAULT true,
|
|
"region_id" varchar(100),
|
|
"created_by" uuid,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_evenements_actif" ON "public"."evenements" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_evenements_dates" ON "public"."evenements" ("date_debut","date_fin");
|
|
CREATE INDEX IF NOT EXISTS "idx_evenements_region_id" ON "public"."evenements" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_evenements_created_by" ON "public"."evenements" ("created_by");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.19 actualites
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."actualites" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" varchar(255) NOT NULL,
|
|
"contenu" text NOT NULL,
|
|
"resume" varchar(500),
|
|
"categorie" varchar(50) CHECK (categorie IN ('general','service','promotion','partenariat','autre')),
|
|
"image_url" varchar(500),
|
|
"image_couverture" text,
|
|
"date_publication" timestamptz NOT NULL DEFAULT now(),
|
|
"auteur" varchar(100),
|
|
"actif" boolean DEFAULT true,
|
|
"mise_en_avant" boolean DEFAULT false,
|
|
"region_id" varchar(100),
|
|
"created_by" uuid,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_actualites_actif" ON "public"."actualites" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_actualites_publication" ON "public"."actualites" ("date_publication");
|
|
CREATE INDEX IF NOT EXISTS "idx_actualites_region_id" ON "public"."actualites" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_actualites_created_by" ON "public"."actualites" ("created_by");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.20 travaux
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."travaux" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" text NOT NULL,
|
|
"description" text,
|
|
"date_debut" date NOT NULL,
|
|
"date_fin" date NOT NULL,
|
|
"jours_travaux" text[] NOT NULL,
|
|
"heure_debut" time NOT NULL,
|
|
"heure_fin" time NOT NULL,
|
|
"actif" boolean DEFAULT true,
|
|
"ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL,
|
|
"ligne_nom" text,
|
|
"region_id" varchar(100),
|
|
"created_by" uuid REFERENCES "public"."utilisateurs"("id") ON DELETE SET NULL,
|
|
"substitutions_attribuees" jsonb DEFAULT '[]'::jsonb,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz,
|
|
CONSTRAINT "valid_dates" CHECK (date_fin >= date_debut)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_dates" ON "public"."travaux" ("date_debut","date_fin");
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_actif" ON "public"."travaux" ("actif");
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_deleted" ON "public"."travaux" ("deleted_at");
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_ligne_id" ON "public"."travaux" ("ligne_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_region_id" ON "public"."travaux" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_created_by" ON "public"."travaux" ("created_by");
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_substitutions_attribuees_gin" ON "public"."travaux" USING gin ("substitutions_attribuees");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.21 travaux_substitutions
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."travaux_substitutions" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"travaux_id" uuid NOT NULL REFERENCES "public"."travaux"("id") ON DELETE CASCADE,
|
|
"horaire_id" uuid NOT NULL REFERENCES "public"."horaires"("id") ON DELETE CASCADE,
|
|
"ordre" integer DEFAULT 0,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
UNIQUE ("travaux_id","horaire_id")
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_substitutions_travaux" ON "public"."travaux_substitutions" ("travaux_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_travaux_substitutions_horaire" ON "public"."travaux_substitutions" ("horaire_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.22 infos_sillons
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."infos_sillons" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" varchar(255) NOT NULL,
|
|
"description" text,
|
|
"type" varchar(50) DEFAULT 'information',
|
|
"ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL,
|
|
"ligne_nom" varchar(255),
|
|
"travaux_id" uuid REFERENCES "public"."travaux"("id") ON DELETE SET NULL,
|
|
"date_debut" timestamptz,
|
|
"date_fin" timestamptz,
|
|
"actif" boolean DEFAULT true,
|
|
"auto_generated" boolean DEFAULT false,
|
|
"region_id" varchar(100),
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_infos_sillons_ligne_id" ON "public"."infos_sillons" ("ligne_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_infos_sillons_travaux_id" ON "public"."infos_sillons" ("travaux_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_infos_sillons_region_id" ON "public"."infos_sillons" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_infos_sillons_actif" ON "public"."infos_sillons" ("actif");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.23 fiches_horaires
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."fiches_horaires" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" varchar(255) NOT NULL,
|
|
"ligne_id" varchar(100),
|
|
"ligne_nom" varchar(255),
|
|
"code_ligne" varchar(50),
|
|
"service_annuel_id" varchar(100),
|
|
"service_annuel_nom" varchar(255),
|
|
"code_service_annuel" varchar(50),
|
|
"direction" varchar(50) DEFAULT 'aller',
|
|
"statut" varchar(50) DEFAULT 'Projet (Brouillon)',
|
|
"pdf_url" text,
|
|
"pdf_filename" varchar(255),
|
|
"pdf_storage_path" text,
|
|
"config" jsonb DEFAULT '{}'::jsonb,
|
|
"circulations_count" integer DEFAULT 0,
|
|
"gares_count" integer DEFAULT 0,
|
|
"region_id" varchar(100),
|
|
"created_by" varchar(255),
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_ligne_id" ON "public"."fiches_horaires" ("ligne_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_service_id" ON "public"."fiches_horaires" ("service_annuel_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_statut" ON "public"."fiches_horaires" ("statut");
|
|
CREATE INDEX IF NOT EXISTS "idx_fiches_horaires_region" ON "public"."fiches_horaires" ("region_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.24 tarifs
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."tarifs" (
|
|
"id" integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
"nom" varchar(255) NOT NULL,
|
|
"type" varchar(50) NOT NULL,
|
|
"description" text,
|
|
"prix_minimum" numeric(10,2),
|
|
"prix_par_km" numeric(10,3),
|
|
"age_maximum" integer,
|
|
"regions" text[] NOT NULL,
|
|
"date_debut" date,
|
|
"date_fin" date,
|
|
"ordre" integer NOT NULL DEFAULT 0,
|
|
"page_dediee" boolean DEFAULT false,
|
|
"infos" jsonb DEFAULT '{}'::jsonb,
|
|
"details" jsonb,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_tarifs_ordre" ON "public"."tarifs" ("ordre");
|
|
CREATE INDEX IF NOT EXISTS "idx_tarifs_regions" ON "public"."tarifs" USING gin ("regions");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.25 parametres
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."parametres" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"cle" varchar(100) NOT NULL UNIQUE,
|
|
"valeur" jsonb,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_parametres_cle" ON "public"."parametres" ("cle");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.26 menus_regionaux
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."menus_regionaux" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"nom" varchar(200) NOT NULL,
|
|
"description" text,
|
|
"actif" boolean DEFAULT false,
|
|
"structure" jsonb DEFAULT '[]'::jsonb,
|
|
"region_id" varchar(64),
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_menus_regionaux_region_id" ON "public"."menus_regionaux" ("region_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.27 pages (CMS)
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."pages" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" text NOT NULL,
|
|
"slug" text,
|
|
"contenu" jsonb DEFAULT '[]'::jsonb,
|
|
"statut" text DEFAULT 'brouillon',
|
|
"region_id" varchar(255),
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_pages_slug" ON "public"."pages" ("slug");
|
|
CREATE INDEX IF NOT EXISTS "idx_pages_region_id" ON "public"."pages" ("region_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.28 page_personnalisation
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."page_personnalisation" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"page" text NOT NULL UNIQUE,
|
|
"config" jsonb DEFAULT '{}'::jsonb,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.29 info_gares
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."info_gares" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"titre" text,
|
|
"description" text,
|
|
"debut_affichage" timestamptz,
|
|
"fin_affichage" timestamptz,
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"ligne_id" uuid REFERENCES "public"."lignes"("id") ON DELETE SET NULL,
|
|
"gare_ids" uuid[] DEFAULT '{}'::uuid[],
|
|
"ligne_ids" uuid[] DEFAULT '{}'::uuid[],
|
|
"type" text DEFAULT 'Information',
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_info_gares_gare_id" ON "public"."info_gares" ("gare_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_info_gares_ligne_id" ON "public"."info_gares" ("ligne_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_info_gares_gare_ids" ON "public"."info_gares" USING gin ("gare_ids");
|
|
CREATE INDEX IF NOT EXISTS "idx_info_gares_ligne_ids" ON "public"."info_gares" USING gin ("ligne_ids");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.30 regles_quais
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."regles_quais" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"gare_id" uuid NOT NULL REFERENCES "public"."gares"("id") ON DELETE CASCADE,
|
|
"quai_id" uuid NOT NULL REFERENCES "public"."gare_quais"("id") ON DELETE CASCADE,
|
|
"type_train" varchar(50),
|
|
"sens" varchar(20),
|
|
"priorite" integer DEFAULT 1,
|
|
"actif" boolean DEFAULT true,
|
|
"description" text,
|
|
"created_at" timestamptz DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_regles_quais_gare" ON "public"."regles_quais" ("gare_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_regles_quais_quai" ON "public"."regles_quais" ("quai_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.31 messagerie_threads
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."messagerie_threads" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.32 messagerie_participants
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."messagerie_participants" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"thread_id" uuid NOT NULL REFERENCES "public"."messagerie_threads"("id") ON DELETE CASCADE,
|
|
"user_id" uuid NOT NULL REFERENCES "public"."utilisateurs"("id") ON DELETE CASCADE,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE ("thread_id","user_id")
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_messagerie_participants_user_id" ON "public"."messagerie_participants" ("user_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.33 messagerie_messages
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."messagerie_messages" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"thread_id" uuid NOT NULL REFERENCES "public"."messagerie_threads"("id") ON DELETE CASCADE,
|
|
"sender_id" uuid NOT NULL REFERENCES "public"."utilisateurs"("id") ON DELETE CASCADE,
|
|
"contenu" text NOT NULL,
|
|
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_messagerie_messages_thread_id" ON "public"."messagerie_messages" ("thread_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_messagerie_messages_created_at" ON "public"."messagerie_messages" ("created_at" DESC);
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.34 banque_mots (Annonces sonores)
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."banque_mots" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"label" text,
|
|
"nom_fichier" text,
|
|
"storage_path" text,
|
|
"categorie" text DEFAULT 'general',
|
|
"format" text DEFAULT 'mp3',
|
|
"taille_bytes" bigint,
|
|
"description" text,
|
|
"duree_ms" integer,
|
|
"actif" boolean NOT NULL DEFAULT true,
|
|
"created_by" uuid,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_banque_mots_categorie" ON "public"."banque_mots" ("categorie");
|
|
CREATE INDEX IF NOT EXISTS "idx_banque_mots_label" ON "public"."banque_mots" ("label");
|
|
CREATE INDEX IF NOT EXISTS "idx_banque_mots_actif" ON "public"."banque_mots" ("actif") WHERE deleted_at IS NULL;
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.35 annonces_templates
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."annonces_templates" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"nom" text NOT NULL,
|
|
"type_annonce" text NOT NULL,
|
|
"description" text,
|
|
"sequence" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"variables" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
"tts_texte" text,
|
|
"actif" boolean NOT NULL DEFAULT true,
|
|
"created_by" uuid,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_templates_type" ON "public"."annonces_templates" ("type_annonce");
|
|
CREATE INDEX IF NOT EXISTS "idx_templates_actif" ON "public"."annonces_templates" ("actif") WHERE deleted_at IS NULL;
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.36 annonces_enregistrees
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."annonces_enregistrees" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"nom" text,
|
|
"type_annonce" text,
|
|
"template_id" uuid REFERENCES "public"."annonces_templates"("id") ON DELETE SET NULL,
|
|
"storage_path" text,
|
|
"nom_fichier" text,
|
|
"variables_values" jsonb DEFAULT '{}'::jsonb,
|
|
"mode_generation" text DEFAULT 'tts',
|
|
"duree_ms" integer,
|
|
"taille_bytes" bigint,
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"diffuse" boolean DEFAULT false,
|
|
"date_diffusion" timestamptz,
|
|
"notes" text,
|
|
"created_by" uuid,
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
"updated_at" timestamptz DEFAULT now(),
|
|
"deleted_at" timestamptz
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_annonces_enregistrees_template" ON "public"."annonces_enregistrees" ("template_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_annonces_enregistrees_gare" ON "public"."annonces_enregistrees" ("gare_id");
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- 5.37 annonces_diffusions
|
|
-- ----------------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS "public"."annonces_diffusions" (
|
|
"id" uuid DEFAULT gen_random_uuid() NOT NULL PRIMARY KEY,
|
|
"annonce_id" uuid REFERENCES "public"."annonces_enregistrees"("id") ON DELETE SET NULL,
|
|
"gare_id" uuid REFERENCES "public"."gares"("id") ON DELETE SET NULL,
|
|
"audio_path" text,
|
|
"tts_texte" text,
|
|
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "idx_annonces_diffusions_annonce" ON "public"."annonces_diffusions" ("annonce_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_annonces_diffusions_gare" ON "public"."annonces_diffusions" ("gare_id");
|
|
|
|
-- ============================================================================
|
|
-- 6. TRIGGERS AUTOMATIQUES UPDATED_AT & AUDIT
|
|
-- ============================================================================
|
|
|
|
-- updated_at triggers
|
|
DO $$
|
|
DECLARE
|
|
t text;
|
|
tables text[] := ARRAY[
|
|
'utilisateurs', 'gares', 'types_train', 'depots_technicentres', 'materiel-roulant', 'services_annuels',
|
|
'lignes', 'lignes_gares', 'horaires', 'horaires_gares_desservies', 'horaires_substitutions',
|
|
'perturbations', 'info_trafic', 'evenements', 'actualites', 'travaux',
|
|
'infos_sillons', 'fiches_horaires', 'tarifs', 'parametres', 'menus_regionaux',
|
|
'pages', 'page_personnalisation', 'info_gares', 'regles_quais', 'messagerie_threads',
|
|
'profils_utilisateurs', 'banque_mots', 'annonces_templates', 'annonces_enregistrees'
|
|
];
|
|
BEGIN
|
|
FOREACH t IN ARRAY tables LOOP
|
|
EXECUTE format('DROP TRIGGER IF EXISTS "tr_%s_set_updated_at" ON "public".%I;', t, t);
|
|
EXECUTE format('CREATE TRIGGER "tr_%s_set_updated_at" BEFORE UPDATE ON "public".%I FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();', t, t);
|
|
END LOOP;
|
|
END $$;
|
|
|
|
-- audit_logs triggers on sensitive tables
|
|
DO $$
|
|
DECLARE
|
|
t text;
|
|
tables text[] := ARRAY[
|
|
'utilisateurs', 'gares', 'lignes', 'horaires', 'perturbations',
|
|
'info_trafic', 'evenements', 'actualites', 'travaux', 'tarifs', 'parametres'
|
|
];
|
|
BEGIN
|
|
FOREACH t IN ARRAY tables LOOP
|
|
EXECUTE format('DROP TRIGGER IF EXISTS "tr_%s_audit" ON "public".%I;', t, t);
|
|
EXECUTE format('CREATE TRIGGER "tr_%s_audit" AFTER INSERT OR UPDATE OR DELETE ON "public".%I FOR EACH ROW EXECUTE FUNCTION "public"."log_audit"();', t, t);
|
|
END LOOP;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- 7. SYNCHRONISATION NATIVE SUPABASE AUTH (auth.users <-> public.utilisateurs)
|
|
-- ============================================================================
|
|
-- Cette fonction est déclenchée lors de la création ou modification d'un compte
|
|
-- dans Supabase Auth (via Dashboard Supabase Studio ou supabase.auth.signUp).
|
|
-- Elle met à jour automatiquement public.utilisateurs sans jamais bloquer GoTrue.
|
|
|
|
CREATE OR REPLACE FUNCTION public.handle_new_auth_user()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = public, extensions, auth, pg_temp
|
|
AS $$
|
|
DECLARE
|
|
v_nom text;
|
|
v_prenom text;
|
|
v_role text;
|
|
v_region_id text;
|
|
v_region_nom text;
|
|
v_photo_url text;
|
|
v_actif boolean;
|
|
v_region_admin boolean;
|
|
v_permissions jsonb;
|
|
BEGIN
|
|
-- Extraction sécurisée des métadonnées Supabase Auth
|
|
v_nom := COALESCE(
|
|
NEW.raw_user_meta_data->>'nom',
|
|
NEW.raw_user_meta_data->>'last_name',
|
|
split_part(NEW.email, '@', 1),
|
|
'Utilisateur'
|
|
);
|
|
v_prenom := COALESCE(
|
|
NEW.raw_user_meta_data->>'prenom',
|
|
NEW.raw_user_meta_data->>'first_name',
|
|
''
|
|
);
|
|
v_role := COALESCE(NEW.raw_user_meta_data->>'role', 'utilisateur');
|
|
v_region_id := NEW.raw_user_meta_data->>'region_id';
|
|
v_region_nom := NEW.raw_user_meta_data->>'region_nom';
|
|
v_photo_url := NEW.raw_user_meta_data->>'photo_url';
|
|
v_actif := COALESCE((NEW.raw_user_meta_data->>'actif')::boolean, true);
|
|
v_region_admin := COALESCE((NEW.raw_user_meta_data->>'region_admin')::boolean, false);
|
|
v_permissions := COALESCE(NEW.raw_user_meta_data->'permissions', '{}'::jsonb);
|
|
|
|
-- Insertion ou synchronisation dans public.utilisateurs
|
|
INSERT INTO public.utilisateurs (
|
|
id, email, nom, prenom, role, actif, region_admin,
|
|
region_id, region_nom, permissions, photo_url, updated_at
|
|
) VALUES (
|
|
NEW.id,
|
|
lower(NEW.email),
|
|
v_nom,
|
|
v_prenom,
|
|
v_role,
|
|
v_actif,
|
|
v_region_admin,
|
|
v_region_id,
|
|
v_region_nom,
|
|
v_permissions,
|
|
v_photo_url,
|
|
NOW()
|
|
)
|
|
ON CONFLICT (email) DO UPDATE SET
|
|
id = EXCLUDED.id,
|
|
nom = CASE WHEN EXCLUDED.nom <> '' AND EXCLUDED.nom <> 'Utilisateur' THEN EXCLUDED.nom ELSE public.utilisateurs.nom END,
|
|
prenom = CASE WHEN EXCLUDED.prenom <> '' THEN EXCLUDED.prenom ELSE public.utilisateurs.prenom END,
|
|
role = CASE WHEN public.utilisateurs.role = 'admin' THEN 'admin' ELSE EXCLUDED.role END,
|
|
actif = EXCLUDED.actif,
|
|
region_admin = COALESCE(public.utilisateurs.region_admin, EXCLUDED.region_admin),
|
|
region_id = COALESCE(EXCLUDED.region_id, public.utilisateurs.region_id),
|
|
region_nom = COALESCE(EXCLUDED.region_nom, public.utilisateurs.region_nom),
|
|
permissions = CASE WHEN public.utilisateurs.permissions <> '{}'::jsonb THEN public.utilisateurs.permissions ELSE EXCLUDED.permissions END,
|
|
photo_url = COALESCE(EXCLUDED.photo_url, public.utilisateurs.photo_url),
|
|
updated_at = NOW();
|
|
|
|
RETURN NEW;
|
|
EXCEPTION WHEN OTHERS THEN
|
|
-- Ne JAMAIS faire échouer l'insertion dans auth.users
|
|
RAISE WARNING 'handle_new_auth_user warning: %', SQLERRM;
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
-- Attacher le trigger sur auth.users si le schéma auth existe
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'auth' AND table_name = 'users') THEN
|
|
DROP TRIGGER IF EXISTS "on_auth_user_created" ON auth.users;
|
|
CREATE TRIGGER "on_auth_user_created"
|
|
AFTER INSERT OR UPDATE ON auth.users
|
|
FOR EACH ROW EXECUTE FUNCTION public.handle_new_auth_user();
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- 8. FONCTIONS RPC MÉTIER
|
|
-- ============================================================================
|
|
|
|
-- Authentification personnalisée de secours (RPC authenticate_user)
|
|
CREATE OR REPLACE FUNCTION "public"."authenticate_user"(p_email text, p_password text)
|
|
RETURNS TABLE(id uuid, email text, nom text, prenom text, name text, role text, region_admin boolean, region_id text)
|
|
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
|
SET search_path = public, extensions, auth, pg_temp
|
|
AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT
|
|
u.id::uuid,
|
|
u.email::text,
|
|
u.nom::text,
|
|
u.prenom::text,
|
|
(COALESCE(u.nom,'') || ' ' || COALESCE(u.prenom,''))::text as name,
|
|
u.role::text,
|
|
COALESCE(u.region_admin, false)::boolean as region_admin,
|
|
u.region_id::text
|
|
FROM public.utilisateurs u
|
|
WHERE lower(u.email) = lower(p_email)
|
|
AND u.password_hash IS NOT NULL
|
|
AND u.password_hash = crypt(p_password, u.password_hash)
|
|
AND COALESCE(u.actif, true) = true
|
|
AND u.deleted_at IS NULL;
|
|
END;
|
|
$$;
|
|
|
|
-- Création utilisateur avec mot de passe hashé
|
|
CREATE OR REPLACE FUNCTION "public"."create_user_with_password"(
|
|
p_email text, p_nom text, p_prenom text,
|
|
p_role text DEFAULT 'utilisateur', p_actif boolean DEFAULT true,
|
|
p_password text DEFAULT NULL, p_photo_url text DEFAULT NULL,
|
|
p_region_admin boolean DEFAULT false, p_region_id text DEFAULT NULL
|
|
)
|
|
RETURNS TABLE(id uuid, email text, nom text, prenom text, role text, actif boolean, photo_url text, region_admin boolean, region_id text)
|
|
LANGUAGE plpgsql SECURITY DEFINER
|
|
SET search_path = public, extensions, auth, pg_temp
|
|
AS $$
|
|
DECLARE
|
|
new_id uuid;
|
|
BEGIN
|
|
INSERT INTO public.utilisateurs (
|
|
email, nom, prenom, role, actif, password_hash, photo_url, region_admin, region_id, created_at
|
|
) VALUES (
|
|
lower(p_email), p_nom, p_prenom, p_role, p_actif,
|
|
CASE WHEN p_password IS NOT NULL AND p_password != '' THEN crypt(p_password, gen_salt('bf')) ELSE NULL END,
|
|
p_photo_url, p_region_admin, p_region_id, NOW()
|
|
)
|
|
RETURNING utilisateurs.id INTO new_id;
|
|
|
|
RETURN QUERY SELECT
|
|
u.id, u.email::text, u.nom::text, u.prenom::text, u.role::text,
|
|
u.actif, u.photo_url::text, u.region_admin, u.region_id::text
|
|
FROM public.utilisateurs u WHERE u.id = new_id;
|
|
END;
|
|
$$;
|
|
|
|
-- Mise à jour mot de passe
|
|
CREATE OR REPLACE FUNCTION "public"."update_user_password"(p_user_id uuid, p_password text)
|
|
RETURNS void
|
|
LANGUAGE plpgsql SECURITY DEFINER
|
|
SET search_path = public, extensions, auth, pg_temp
|
|
AS $$
|
|
BEGIN
|
|
IF p_password IS NULL OR p_password = '' THEN
|
|
RAISE EXCEPTION 'Le mot de passe ne peut pas être vide';
|
|
END IF;
|
|
|
|
UPDATE public.utilisateurs
|
|
SET password_hash = crypt(p_password, gen_salt('bf')),
|
|
updated_at = NOW()
|
|
WHERE id = p_user_id;
|
|
|
|
IF NOT FOUND THEN
|
|
RAISE EXCEPTION 'Utilisateur non trouvé';
|
|
END IF;
|
|
END;
|
|
$$;
|
|
|
|
-- Détails agrégés d'une gare (quais + transports)
|
|
CREATE OR REPLACE FUNCTION "public"."get_gare_details"(p_gare_id uuid)
|
|
RETURNS TABLE(id uuid, nom text, type text, services "public"."service_type"[], quais jsonb, transports jsonb, created_at timestamptz, updated_at timestamptz)
|
|
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
|
SET search_path = public, extensions, auth, pg_temp
|
|
AS $$
|
|
BEGIN
|
|
RETURN QUERY SELECT
|
|
g.id,
|
|
g.nom,
|
|
g.type::text,
|
|
g.services,
|
|
COALESCE((
|
|
SELECT jsonb_agg(jsonb_build_object('id', q.id, 'nom', q.nom, 'distance_metres', q.distance_metres))
|
|
FROM public.gare_quais q WHERE q.gare_id = g.id
|
|
), '[]'::jsonb) as quais,
|
|
COALESCE((
|
|
SELECT jsonb_agg(jsonb_build_object('id', t.id, 'type', t.type, 'ligne', t.ligne))
|
|
FROM public.gare_transports t WHERE t.gare_id = g.id
|
|
), '[]'::jsonb) as transports,
|
|
g.created_at,
|
|
g.updated_at
|
|
FROM public.gares g
|
|
WHERE g.id = p_gare_id AND g.deleted_at IS NULL;
|
|
END;
|
|
$$;
|
|
|
|
-- ============================================================================
|
|
-- 9. ROW LEVEL SECURITY (RLS) & POLICIES
|
|
-- ============================================================================
|
|
-- Activer le RLS sur l'ensemble des 37 tables du schéma public
|
|
DO $$
|
|
DECLARE
|
|
t text;
|
|
tables text[] := ARRAY[
|
|
'audit_logs', 'utilisateurs', 'profils_utilisateurs', 'gares', 'gare_quais',
|
|
'gare_transports', 'types_train', 'depots_technicentres', 'materiel-roulant', 'services_annuels',
|
|
'lignes', 'lignes_gares', 'horaires', 'horaires_gares_desservies', 'horaires_substitutions',
|
|
'perturbations', 'perturbations_modifications_parcours', 'info_trafic', 'evenements',
|
|
'actualites', 'travaux', 'travaux_substitutions', 'infos_sillons', 'fiches_horaires',
|
|
'tarifs', 'parametres', 'menus_regionaux', 'pages', 'page_personnalisation',
|
|
'info_gares', 'regles_quais', 'messagerie_threads', 'messagerie_participants',
|
|
'messagerie_messages', 'banque_mots', 'annonces_templates', 'annonces_enregistrees',
|
|
'annonces_diffusions'
|
|
];
|
|
BEGIN
|
|
FOREACH t IN ARRAY tables LOOP
|
|
EXECUTE format('ALTER TABLE "public".%I ENABLE ROW LEVEL SECURITY;', t);
|
|
EXECUTE format('DROP POLICY IF EXISTS "policy_all_%s" ON "public".%I;', t, t);
|
|
EXECUTE format('CREATE POLICY "policy_all_%s" ON "public".%I FOR ALL TO anon, authenticated, service_role USING (true) WITH CHECK (true);', t, t);
|
|
END LOOP;
|
|
END $$;
|
|
|
|
-- Politiques spécifiques pour l'app mobile (profils_utilisateurs)
|
|
DROP POLICY IF EXISTS "Lecture profil propre" ON "public"."profils_utilisateurs";
|
|
DROP POLICY IF EXISTS "Insertion profil propre" ON "public"."profils_utilisateurs";
|
|
DROP POLICY IF EXISTS "Mise à jour profil propre" ON "public"."profils_utilisateurs";
|
|
DROP POLICY IF EXISTS "Suppression profil propre" ON "public"."profils_utilisateurs";
|
|
|
|
CREATE POLICY "Lecture profil propre" ON "public"."profils_utilisateurs" FOR SELECT USING (true);
|
|
CREATE POLICY "Insertion profil propre" ON "public"."profils_utilisateurs" FOR INSERT WITH CHECK (true);
|
|
CREATE POLICY "Mise à jour profil propre" ON "public"."profils_utilisateurs" FOR UPDATE USING (true) WITH CHECK (true);
|
|
CREATE POLICY "Suppression profil propre" ON "public"."profils_utilisateurs" FOR DELETE USING (true);
|
|
|
|
-- ============================================================================
|
|
-- 10. SUPABASE STORAGE BUCKETS & POLICIES
|
|
-- ============================================================================
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'storage') THEN
|
|
|
|
-- 1. Buckets fiches horaires
|
|
INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types")
|
|
VALUES ('fiches-horaires', 'fiches-horaires', true, 52428800, ARRAY['application/pdf'])
|
|
ON CONFLICT ("id") DO UPDATE SET "public" = true;
|
|
|
|
INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types")
|
|
VALUES ('Fiches_Horaires', 'Fiches_Horaires', true, 52428800, ARRAY['application/pdf'])
|
|
ON CONFLICT ("id") DO UPDATE SET "public" = true;
|
|
|
|
-- 2. Bucket matériel roulant (photos de trains)
|
|
INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types")
|
|
VALUES ('materiel-roulant', 'materiel-roulant', true, 10485760, ARRAY['image/jpeg','image/png','image/webp','image/gif','image/svg+xml'])
|
|
ON CONFLICT ("id") DO UPDATE SET "public" = true;
|
|
|
|
-- 3. Bucket annonces sonores (jingles et fichiers audio)
|
|
INSERT INTO storage.buckets ("id", "name", "public", "file_size_limit", "allowed_mime_types")
|
|
VALUES ('banque-annonces', 'banque-annonces', true, 20971520, ARRAY['audio/mpeg','audio/wav','audio/mp3','audio/ogg'])
|
|
ON CONFLICT ("id") DO UPDATE SET "public" = true;
|
|
|
|
-- Politiques Storage pour l'accès aux objets
|
|
DROP POLICY IF EXISTS "Public access on storage fiches-horaires" ON storage.objects;
|
|
DROP POLICY IF EXISTS "Public access on storage materiel-roulant" ON storage.objects;
|
|
DROP POLICY IF EXISTS "Public access on storage banque-annonces" ON storage.objects;
|
|
|
|
CREATE POLICY "Public access on storage fiches-horaires"
|
|
ON storage.objects FOR ALL TO anon, authenticated, service_role
|
|
USING (bucket_id IN ('fiches-horaires', 'Fiches_Horaires'))
|
|
WITH CHECK (bucket_id IN ('fiches-horaires', 'Fiches_Horaires'));
|
|
|
|
CREATE POLICY "Public access on storage materiel-roulant"
|
|
ON storage.objects FOR ALL TO anon, authenticated, service_role
|
|
USING (bucket_id = 'materiel-roulant')
|
|
WITH CHECK (bucket_id = 'materiel-roulant');
|
|
|
|
CREATE POLICY "Public access on storage banque-annonces"
|
|
ON storage.objects FOR ALL TO anon, authenticated, service_role
|
|
USING (bucket_id = 'banque-annonces')
|
|
WITH CHECK (bucket_id = 'banque-annonces');
|
|
|
|
END IF;
|
|
EXCEPTION WHEN OTHERS THEN
|
|
RAISE NOTICE 'Storage buckets notice: %', SQLERRM;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- 11. PRIVILÈGES & GRANTS (anon, authenticated, service_role)
|
|
-- ============================================================================
|
|
GRANT USAGE ON SCHEMA "public" TO "postgres", "anon", "authenticated", "service_role";
|
|
|
|
-- Accès complet sur toutes les tables
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA "public" TO "anon", "authenticated", "service_role";
|
|
|
|
-- Séquences
|
|
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "public" TO "anon", "authenticated", "service_role";
|
|
|
|
-- Fonctions
|
|
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "public" TO "anon", "authenticated", "service_role";
|
|
|
|
-- Privilèges par défaut pour les futures créations
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA "public" GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "anon", "authenticated", "service_role";
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA "public" GRANT USAGE, SELECT ON SEQUENCES TO "anon", "authenticated", "service_role";
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA "public" GRANT EXECUTE ON FUNCTIONS TO "anon", "authenticated", "service_role";
|
|
|
|
-- Schéma extensions
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'extensions') THEN
|
|
GRANT USAGE ON SCHEMA "extensions" TO "postgres", "anon", "authenticated", "service_role";
|
|
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA "extensions" TO "postgres", "anon", "authenticated", "service_role";
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- 12. DONNÉES INITIALES (SEEDING)
|
|
-- ============================================================================
|
|
|
|
-- 12.1 Paramètres système par défaut
|
|
INSERT INTO "public"."parametres" (cle, valeur) VALUES
|
|
('general', '{"nom_site": "Ferrovia Panel", "description_site": "Système de supervision et gestion ferroviaire", "region": "Nationale", "logo_header_url": ""}'::jsonb),
|
|
('tarifs', '{"billets": [], "abonnements": [], "remises": []}'::jsonb),
|
|
('theme', '{"primary_color": "#0088CE", "dark_mode": false}'::jsonb)
|
|
ON CONFLICT (cle) DO NOTHING;
|
|
|
|
-- 12.2 Types de train par défaut
|
|
INSERT INTO "public"."types_train" (id, nom, code, description, couleur, color, type_train, actif) VALUES
|
|
(1, 'TER', 'TER', 'Transport Express Régional', '#0088CE', '#0088CE', 'TER', true),
|
|
(2, 'TGV INOUI', 'TGV', 'Train à Grande Vitesse INOUI', '#7A9A01', '#7A9A01', 'TGV', true),
|
|
(3, 'OUIGO', 'OUIGO', 'Offre grande vitesse low-cost', '#009AA6', '#009AA6', 'OUIGO', true),
|
|
(4, 'Intercités', 'IC', 'Lignes classiques nationales et de nuit', '#CD0037', '#CD0037', 'Intercités', true),
|
|
(5, 'Transilien', 'TRA', 'Réseau de banlieue Île-de-France', '#0055A5', '#0055A5', 'Transilien', true),
|
|
(6, 'Fret', 'FRET', 'Transport ferroviaire de marchandises', '#64748B', '#64748B', 'Fret', true)
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
nom = EXCLUDED.nom,
|
|
code = EXCLUDED.code,
|
|
couleur = EXCLUDED.couleur,
|
|
color = EXCLUDED.color,
|
|
actif = EXCLUDED.actif;
|
|
|
|
-- 12.3 Dépôts et Technicentres de maintenance réseau par défaut
|
|
INSERT INTO "public"."depots_technicentres" ("nom", "code", "ville", "gare_proche", "capacite_max", "specialites") VALUES
|
|
('Technicentre Paris-Nord (La Chapelle)', 'TPN-LCP', 'Paris', 'Paris Gare du Nord', 24, '["TGV", "TER", "Eurostar", "Maintenance Lourde"]'::jsonb),
|
|
('Technicentre Atlantique (Châtillon)', 'TPA-CHT', 'Paris', 'Paris Montparnasse', 30, '["TGV InOui", "OUIGO", "Grandes Lignes"]'::jsonb),
|
|
('Technicentre Est Européen (Pantin)', 'TEE-PAN', 'Paris', 'Paris Gare de l''Est', 18, '["TGV", "ICE", "Révision mi-vie"]'::jsonb),
|
|
('Technicentre Paris-Sud-Est (Villeneuve)', 'TPSE-VLL', 'Paris', 'Paris Gare de Lyon', 28, '["TGV Sud-Est", "Automotrices", "Climatisation"]'::jsonb),
|
|
('Technicentre Bretagne (Rennes)', 'TB-REN', 'Rennes', 'Rennes', 16, '["TER BreizhGo", "Régiolis", "Z2N", "Bogies"]'::jsonb),
|
|
('Dépôt Pays de la Loire (Nantes-Blottereau)', 'DPL-NTS', 'Nantes', 'Nantes', 14, '["TER Aléop", "ZTER", "AGC Diesel/Electrique"]'::jsonb),
|
|
('Technicentre Auvergne-Rhône-Alpes (Gerland)', 'TARA-GER', 'Lyon', 'Lyon Part-Dieu', 22, '["TER AURA", "TGV", "Maintenance Préventive"]'::jsonb),
|
|
('Dépôt Marseille-Blancarde', 'DMB-MRS', 'Marseille', 'Marseille Saint-Charles', 15, '["TER Zou", "Rames Réversibles", "Electrique"]'::jsonb),
|
|
('Technicentre Nouvelle-Aquitaine (Bordeaux)', 'TNA-BDX', 'Bordeaux', 'Bordeaux Saint-Jean', 16, '["TER Nouvelle-Aquitaine", "Coradia Polyvalent"]'::jsonb),
|
|
('Technicentre Nord-Pas-de-Calais (Hellemmes)', 'TNPDC-HLM', 'Lille', 'Lille Flandres', 20, '["TER Hauts-de-France", "Grand Carénage"]'::jsonb),
|
|
('Dépôt Grand Est (Strasbourg-Cronenbourg)', 'DGE-STB', 'Strasbourg', 'Strasbourg', 14, '["TER Fluo", "Régiolis Transfrontalier"]'::jsonb)
|
|
ON CONFLICT ("code") DO UPDATE SET
|
|
nom = EXCLUDED.nom,
|
|
ville = EXCLUDED.ville,
|
|
gare_proche = EXCLUDED.gare_proche,
|
|
capacite_max = EXCLUDED.capacite_max,
|
|
specialites = EXCLUDED.specialites;
|
|
|
|
-- 12.4 Création des comptes Administrateur dans Supabase Auth (auth.users)
|
|
-- Mot de passe par défaut : password (crypté en bcrypt bf $2a$10$)
|
|
DO $$
|
|
DECLARE
|
|
v_admin1_id uuid := 'a0000000-0000-0000-0000-000000000001'::uuid;
|
|
v_admin2_id uuid := 'a0000000-0000-0000-0000-000000000002'::uuid;
|
|
v_pw_hash text;
|
|
BEGIN
|
|
-- Hash bcrypt compatible Supabase Auth / GoTrue
|
|
v_pw_hash := crypt('password', gen_salt('bf', 10));
|
|
|
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'auth' AND table_name = 'users') THEN
|
|
|
|
-- Administrateur 1 : admin@ferrovia.fr
|
|
IF NOT EXISTS (SELECT 1 FROM auth.users WHERE email = 'admin@ferrovia.fr') THEN
|
|
INSERT INTO auth.users (
|
|
instance_id, id, aud, role, email, encrypted_password,
|
|
email_confirmed_at, recovery_sent_at, last_sign_in_at,
|
|
raw_app_meta_data, raw_user_meta_data, created_at, updated_at,
|
|
confirmation_token, email_change, email_change_token_new, recovery_token
|
|
) VALUES (
|
|
'00000000-0000-0000-0000-000000000000',
|
|
v_admin1_id,
|
|
'authenticated',
|
|
'authenticated',
|
|
'admin@ferrovia.fr',
|
|
v_pw_hash,
|
|
NOW(), NOW(), NOW(),
|
|
'{"provider":"email","providers":["email"]}'::jsonb,
|
|
'{"nom":"Admin","prenom":"Ferrovia","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb,
|
|
NOW(), NOW(),
|
|
'', '', '', ''
|
|
);
|
|
|
|
BEGIN
|
|
INSERT INTO auth.identities (
|
|
id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at
|
|
) VALUES (
|
|
v_admin1_id::text,
|
|
v_admin1_id,
|
|
jsonb_build_object('sub', v_admin1_id::text, 'email', 'admin@ferrovia.fr'),
|
|
'email',
|
|
NOW(), NOW(), NOW()
|
|
);
|
|
EXCEPTION WHEN OTHERS THEN NULL;
|
|
END;
|
|
ELSE
|
|
UPDATE auth.users
|
|
SET encrypted_password = v_pw_hash,
|
|
email_confirmed_at = COALESCE(email_confirmed_at, NOW()),
|
|
raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || '{"nom":"Admin","prenom":"Ferrovia","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb
|
|
WHERE email = 'admin@ferrovia.fr';
|
|
END IF;
|
|
|
|
-- Administrateur 2 : admin@mrpatator.fr
|
|
IF NOT EXISTS (SELECT 1 FROM auth.users WHERE email = 'admin@mrpatator.fr') THEN
|
|
INSERT INTO auth.users (
|
|
instance_id, id, aud, role, email, encrypted_password,
|
|
email_confirmed_at, recovery_sent_at, last_sign_in_at,
|
|
raw_app_meta_data, raw_user_meta_data, created_at, updated_at,
|
|
confirmation_token, email_change, email_change_token_new, recovery_token
|
|
) VALUES (
|
|
'00000000-0000-0000-0000-000000000000',
|
|
v_admin2_id,
|
|
'authenticated',
|
|
'authenticated',
|
|
'admin@mrpatator.fr',
|
|
v_pw_hash,
|
|
NOW(), NOW(), NOW(),
|
|
'{"provider":"email","providers":["email"]}'::jsonb,
|
|
'{"nom":"Admin","prenom":"MrPatator","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb,
|
|
NOW(), NOW(),
|
|
'', '', '', ''
|
|
);
|
|
|
|
BEGIN
|
|
INSERT INTO auth.identities (
|
|
id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at
|
|
) VALUES (
|
|
v_admin2_id::text,
|
|
v_admin2_id,
|
|
jsonb_build_object('sub', v_admin2_id::text, 'email', 'admin@mrpatator.fr'),
|
|
'email',
|
|
NOW(), NOW(), NOW()
|
|
);
|
|
EXCEPTION WHEN OTHERS THEN NULL;
|
|
END;
|
|
ELSE
|
|
UPDATE auth.users
|
|
SET encrypted_password = v_pw_hash,
|
|
email_confirmed_at = COALESCE(email_confirmed_at, NOW()),
|
|
raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || '{"nom":"Admin","prenom":"MrPatator","role":"admin","region_admin":true,"permissions":{"admin":true,"super_admin":true}}'::jsonb
|
|
WHERE email = 'admin@mrpatator.fr';
|
|
END IF;
|
|
|
|
END IF;
|
|
EXCEPTION WHEN OTHERS THEN
|
|
RAISE NOTICE 'Auth seeding notice: %', SQLERRM;
|
|
END $$;
|
|
|
|
-- 12.4 Insertion / Synchronisation dans public.utilisateurs
|
|
INSERT INTO "public"."utilisateurs" (
|
|
id, email, nom, prenom, role, actif, region_admin, password_hash, permissions
|
|
) VALUES (
|
|
'a0000000-0000-0000-0000-000000000001'::uuid,
|
|
'admin@ferrovia.fr',
|
|
'Admin',
|
|
'Ferrovia',
|
|
'admin',
|
|
true,
|
|
true,
|
|
crypt('password', gen_salt('bf', 10)),
|
|
'{"admin": true, "super_admin": true, "all_regions": true}'::jsonb
|
|
) ON CONFLICT (email) DO UPDATE SET
|
|
nom = EXCLUDED.nom,
|
|
prenom = EXCLUDED.prenom,
|
|
role = 'admin',
|
|
actif = true,
|
|
region_admin = true,
|
|
password_hash = EXCLUDED.password_hash,
|
|
permissions = EXCLUDED.permissions,
|
|
updated_at = NOW();
|
|
|
|
INSERT INTO "public"."utilisateurs" (
|
|
id, email, nom, prenom, role, actif, region_admin, password_hash, permissions
|
|
) VALUES (
|
|
'a0000000-0000-0000-0000-000000000002'::uuid,
|
|
'admin@mrpatator.fr',
|
|
'Admin',
|
|
'MrPatator',
|
|
'admin',
|
|
true,
|
|
true,
|
|
crypt('password', gen_salt('bf', 10)),
|
|
'{"admin": true, "super_admin": true, "all_regions": true}'::jsonb
|
|
) ON CONFLICT (email) DO UPDATE SET
|
|
nom = EXCLUDED.nom,
|
|
prenom = EXCLUDED.prenom,
|
|
role = 'admin',
|
|
actif = true,
|
|
region_admin = true,
|
|
password_hash = EXCLUDED.password_hash,
|
|
permissions = EXCLUDED.permissions,
|
|
updated_at = NOW();
|
|
|
|
-- ============================================================================
|
|
-- FIN DU FICHIER SCHEME.sql
|
|
-- Base Ferrovia prête : 37 Tables, 3 ENUMs, Triggers Supabase Auth,
|
|
-- 4 Buckets Storage, RLS & Rôles anon/authenticated configurés.
|
|
-- ============================================================================
|