MIGRATION VERS GITEA
This commit is contained in:
commit
5f9d991c15
333 files changed
+108514
No files matched your search
@@ -0,0 +1,7 @@
|
||||
-- Migration : Ajout de la colonne fh_url à la table lignes
|
||||
-- Stocke l'URL publique du PDF de la fiche horaire associée à la ligne
|
||||
|
||||
ALTER TABLE lignes
|
||||
ADD COLUMN IF NOT EXISTS fh_url TEXT DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN lignes.fh_url IS 'URL publique du fichier PDF de la fiche horaire de la ligne (stocké dans Supabase Storage)';
|
||||
@@ -0,0 +1,88 @@
|
||||
-- Migration: create table horaires_substitutions
|
||||
-- Cette table est utilisée par l'API front-end (src/api/horairesSubstitutions.js)
|
||||
CREATE TABLE IF NOT EXISTS horaires_substitutions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Référence éventuelle vers un horaire source
|
||||
horaire_id UUID REFERENCES horaires(id) ON DELETE SET NULL,
|
||||
|
||||
-- Région (filtrage RBAC)
|
||||
region_id UUID,
|
||||
region VARCHAR(255),
|
||||
|
||||
-- Informations générales (copie similaire à `horaires`)
|
||||
numero_train VARCHAR(50),
|
||||
type_train VARCHAR(100),
|
||||
type_train_id BIGINT,
|
||||
|
||||
gare_depart_id UUID,
|
||||
gare_depart_nom VARCHAR(255),
|
||||
heure_depart TIME,
|
||||
|
||||
gare_arrivee_id UUID,
|
||||
gare_arrivee_nom VARCHAR(255),
|
||||
heure_arrivee TIME,
|
||||
|
||||
-- Période de validité de la substitution
|
||||
date_debut TIMESTAMP WITH TIME ZONE,
|
||||
date_fin TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
-- Jours / exceptions
|
||||
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,
|
||||
|
||||
ligne_id UUID,
|
||||
ligne_nom VARCHAR(255),
|
||||
|
||||
service_annuel_id UUID,
|
||||
service_annuel_nom VARCHAR(255),
|
||||
|
||||
-- Indique qu'il s'agit d'une substitution
|
||||
est_substitution BOOLEAN DEFAULT true,
|
||||
motif_substitution VARCHAR(255),
|
||||
|
||||
-- Snapshot des gares desservies et composition
|
||||
gares_desservies JSONB DEFAULT '[]'::jsonb,
|
||||
composition_train JSONB DEFAULT '[]'::jsonb,
|
||||
|
||||
-- Métadonnées
|
||||
actif BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
-- Indexes utiles
|
||||
CREATE INDEX IF NOT EXISTS idx_horaires_substitutions_region ON horaires_substitutions(region_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_horaires_substitutions_horaire ON horaires_substitutions(horaire_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_horaires_substitutions_actif ON horaires_substitutions(actif) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_horaires_substitutions_datefin ON horaires_substitutions(date_fin);
|
||||
|
||||
-- Trigger pour updated_at si la fonction existe
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'update_updated_at_column') THEN
|
||||
DROP TRIGGER IF EXISTS update_horaires_substitutions_updated_at ON horaires_substitutions;
|
||||
CREATE TRIGGER update_horaires_substitutions_updated_at
|
||||
BEFORE UPDATE ON horaires_substitutions
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
END IF;
|
||||
END$$;
|
||||
|
||||
COMMENT ON TABLE horaires_substitutions IS 'Table des horaires de substitution (autocar / remplacement)';
|
||||
COMMENT ON COLUMN horaires_substitutions.gares_desservies IS 'Liste JSON des gares desservies par la substitution';
|
||||
COMMENT ON COLUMN horaires_substitutions.composition_train IS 'Composition JSON du matériel roulant (position, id, nom)';
|
||||
COMMENT ON COLUMN horaires_substitutions.date_fin IS 'Date/heure de fin de validité de la substitution';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
-- ============================================================================
|
||||
-- MIGRATION : Ajout de la colonne composition_train (JSONB) dans la table horaires
|
||||
-- Date : 2026-03-01
|
||||
-- Description : Stocke la composition complète et ordonnée des matériels roulants
|
||||
-- affectés à un horaire (sillon). Chaque élément contient l'id,
|
||||
-- le nom, le nom technique, l'URL de l'image et la position.
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER dans l'éditeur SQL Supabase :
|
||||
-- https://supabase.com/dashboard → votre projet → SQL Editor
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE public.horaires
|
||||
ADD COLUMN IF NOT EXISTS composition_train JSONB DEFAULT '[]'::jsonb;
|
||||
|
||||
COMMENT ON COLUMN public.horaires.composition_train IS
|
||||
'Composition ordonnée du train : liste JSON des matériels roulants affectés au sillon. '
|
||||
'Format : [{"materiel_id": 1, "materiel_nom": "Régiolis", "materiel_nom_technique": "X73500", "materiel_image_url": "https://...", "position": 1}, ...]';
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
-- ============================================================================
|
||||
-- MIGRATION : Ajout de la colonne informations (JSONB) dans la table gares
|
||||
-- Date : 2026-03-01
|
||||
-- Description : Stocke toutes les informations enrichies d'une gare :
|
||||
-- adresse, horaires, accessibilité, services, voiture, vélo,
|
||||
-- et lignes de transports en commun (bus, métro, tramway, car régional).
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER dans l'éditeur SQL Supabase :
|
||||
-- https://supabase.com/dashboard → votre projet → SQL Editor
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE public.gares
|
||||
ADD COLUMN IF NOT EXISTS informations JSONB DEFAULT '{}'::jsonb;
|
||||
|
||||
COMMENT ON COLUMN public.gares.informations IS
|
||||
'Informations enrichies de la gare au format JSON. Structure :
|
||||
{
|
||||
-- Informations Générales
|
||||
"adresse": "20 Bd Diderot, 75012 Paris",
|
||||
"horaires_guichet": "6h00 - 21h30",
|
||||
"horaires_gare": "5h00 - 00h30",
|
||||
"distributeur_ter": true,
|
||||
|
||||
-- Accessibilité
|
||||
"service_assistance": true,
|
||||
|
||||
-- Services
|
||||
"espace_attente": true,
|
||||
"distributeur_tgv": false,
|
||||
"objets_trouves": true,
|
||||
"toilettes": true,
|
||||
"wifi_gratuit": true,
|
||||
"photomaton": false,
|
||||
"defibrillateur": true,
|
||||
"galeries_marchandes": false,
|
||||
|
||||
-- Voiture
|
||||
"depose_minute": true,
|
||||
"espace_taxis": true,
|
||||
"espace_vtc": false,
|
||||
"location_voiture": false,
|
||||
"parking": true,
|
||||
|
||||
-- Vélo
|
||||
"velo_libre_service": true,
|
||||
"parking_velos": true,
|
||||
|
||||
-- Transports en commun enrichis
|
||||
"lignes_bus": [{"nom": "29", "terminus_a": "Gare Saint-Lazare", "terminus_b": "Montempoivre"}],
|
||||
"lignes_metro": [{"nom": "1", "terminus_a": "La Défense", "terminus_b": "Château de Vincennes"}],
|
||||
"lignes_tramway": [],
|
||||
"lignes_car_regional": []
|
||||
}';
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
-- ============================================================================
|
||||
-- MIGRATION : Ajout de la colonne gares_desservies (JSONB) dans la table horaires
|
||||
-- Date : 2026-03-01
|
||||
-- Description : Ajoute une colonne JSONB "gares_desservies" dans la table horaires
|
||||
-- afin de stocker un snapshot dénormalisé des gares desservies.
|
||||
-- Cette colonne est alimentée à chaque création/modification d'horaire
|
||||
-- et est synchronisée avec la table relationnelle horaires_gares_desservies.
|
||||
-- ============================================================================
|
||||
|
||||
-- Ajout de la colonne si elle n'existe pas encore
|
||||
ALTER TABLE public.horaires
|
||||
ADD COLUMN IF NOT EXISTS gares_desservies JSONB DEFAULT '[]'::jsonb;
|
||||
|
||||
-- Commentaire explicatif sur la colonne
|
||||
COMMENT ON COLUMN public.horaires.gares_desservies IS
|
||||
'Snapshot JSON des gares desservies (copie dénormalisée synchronisée avec horaires_gares_desservies). '
|
||||
'Format : [{"gare_id": "uuid", "gare_nom": "Nom Gare", "heure_arrivee": "HH:MM", "heure_depart": "HH:MM", "ordre": 1}, ...]';
|
||||
|
||||
-- ============================================================================
|
||||
-- Rétro-remplissage : synchroniser la colonne JSON depuis la table relationnelle
|
||||
-- pour tous les horaires existants ayant déjà des gares desservies.
|
||||
-- ============================================================================
|
||||
UPDATE public.horaires h
|
||||
SET gares_desservies = (
|
||||
SELECT COALESCE(
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'gare_id', gd.gare_id,
|
||||
'gare_nom', gd.gare_nom,
|
||||
'heure_arrivee', gd.heure_arrivee,
|
||||
'heure_depart', gd.heure_depart,
|
||||
'ordre', gd.ordre
|
||||
) ORDER BY gd.ordre
|
||||
),
|
||||
'[]'::jsonb
|
||||
)
|
||||
FROM public.horaires_gares_desservies gd
|
||||
WHERE gd.horaire_id = h.id
|
||||
)
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM public.horaires_gares_desservies gd WHERE gd.horaire_id = h.id
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- (Optionnel) Trigger pour maintenir automatiquement la colonne JSON à jour
|
||||
-- à chaque INSERT / UPDATE / DELETE dans horaires_gares_desservies.
|
||||
-- Décommenter si vous souhaitez une synchronisation automatique côté base.
|
||||
-- ============================================================================
|
||||
|
||||
-- CREATE OR REPLACE FUNCTION public.sync_horaire_gares_desservies_json()
|
||||
-- RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
-- DECLARE
|
||||
-- v_horaire_id UUID;
|
||||
-- BEGIN
|
||||
-- IF TG_OP = 'DELETE' THEN
|
||||
-- v_horaire_id := OLD.horaire_id;
|
||||
-- ELSE
|
||||
-- v_horaire_id := NEW.horaire_id;
|
||||
-- END IF;
|
||||
--
|
||||
-- UPDATE public.horaires
|
||||
-- SET gares_desservies = (
|
||||
-- SELECT COALESCE(
|
||||
-- jsonb_agg(
|
||||
-- jsonb_build_object(
|
||||
-- 'gare_id', gd.gare_id,
|
||||
-- 'gare_nom', gd.gare_nom,
|
||||
-- 'heure_arrivee', gd.heure_arrivee,
|
||||
-- 'heure_depart', gd.heure_depart,
|
||||
-- 'ordre', gd.ordre
|
||||
-- ) ORDER BY gd.ordre
|
||||
-- ),
|
||||
-- '[]'::jsonb
|
||||
-- )
|
||||
-- FROM public.horaires_gares_desservies gd
|
||||
-- WHERE gd.horaire_id = v_horaire_id
|
||||
-- )
|
||||
-- WHERE id = v_horaire_id;
|
||||
--
|
||||
-- RETURN NULL;
|
||||
-- END;
|
||||
-- $$;
|
||||
--
|
||||
-- DROP TRIGGER IF EXISTS tr_sync_gares_desservies_json ON public.horaires_gares_desservies;
|
||||
-- CREATE TRIGGER tr_sync_gares_desservies_json
|
||||
-- AFTER INSERT OR UPDATE OR DELETE ON public.horaires_gares_desservies
|
||||
-- FOR EACH ROW EXECUTE FUNCTION public.sync_horaire_gares_desservies_json();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Migration : ajout de la colonne jours_perturbation à la table perturbations
|
||||
-- Permet de stocker plusieurs jours uniques ou périodes pour une perturbation
|
||||
|
||||
ALTER TABLE perturbations
|
||||
ADD COLUMN IF NOT EXISTS jours_perturbation JSONB DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN perturbations.jours_perturbation IS
|
||||
'Liste des jours/périodes de perturbation. Format : [{type: "jour"|"periode", date: "YYYY-MM-DD", dateDebut: "YYYY-MM-DD", dateFin: "YYYY-MM-DD"}]';
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Ajoute un champ JSONB pour stocker les permissions par fonctionnalite.
|
||||
ALTER TABLE IF EXISTS public.utilisateurs
|
||||
ADD COLUMN IF NOT EXISTS permissions jsonb NOT NULL DEFAULT '{}'::jsonb;
|
||||
|
||||
-- Securise les lignes existantes si la colonne etait nullable.
|
||||
UPDATE public.utilisateurs
|
||||
SET permissions = '{}'::jsonb
|
||||
WHERE permissions IS NULL;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
-- ============================================================================
|
||||
-- MIGRATION : Ajout de la colonne perturbations_arrets (JSONB) dans perturbations
|
||||
-- Date : 2026-03-01
|
||||
-- Description : Stocke l'état complet des perturbations par arrêt tel que
|
||||
-- configuré dans le formulaire de création / modification.
|
||||
-- Ceci permet au formulaire d'édition de restaurer exactement
|
||||
-- le même état que le formulaire de création.
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER dans l'éditeur SQL Supabase :
|
||||
-- https://supabase.com/dashboard → votre projet → SQL Editor
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE public.perturbations
|
||||
ADD COLUMN IF NOT EXISTS perturbations_arrets JSONB DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN public.perturbations.perturbations_arrets IS
|
||||
'Snapshot complet des perturbations par arrêt (état du formulaire). '
|
||||
'Format : [{
|
||||
"index": 0,
|
||||
"gare_nom": "Paris Gare de Lyon",
|
||||
"heure_arrivee": null,
|
||||
"heure_depart": "07:30",
|
||||
"type_perturbation": "retard",
|
||||
"retard_minutes": 15,
|
||||
"retard_cumule": 0,
|
||||
"arret_prolonge_minutes": 0,
|
||||
"cause_arret": "Incident technique",
|
||||
"is_nouveau_terminus": false,
|
||||
"is_nouvelle_origine": false,
|
||||
"isFirst": true,
|
||||
"isLast": false,
|
||||
"manual_retard": true,
|
||||
"manual_arret_prolonge": false
|
||||
}, ...]';
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
-- ============================================================================
|
||||
-- MIGRATION: Ajout du système de gestion des perturbations
|
||||
-- Date: 2026-02-06
|
||||
-- Description: Ajoute les tables nécessaires pour gérer les perturbations
|
||||
-- affectant les horaires de trains
|
||||
-- ============================================================================
|
||||
|
||||
-- Vérifier que les tables n'existent pas déjà
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'perturbations') THEN
|
||||
RAISE NOTICE 'La table perturbations existe déjà, migration annulée';
|
||||
RETURN;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Exécuter le script principal
|
||||
supabase_perturbations_tables.sql
|
||||
|
||||
-- Vérification post-migration
|
||||
DO $$
|
||||
DECLARE
|
||||
perturbations_count INTEGER;
|
||||
modifications_count INTEGER;
|
||||
BEGIN
|
||||
-- Vérifier que les tables ont été créées
|
||||
SELECT COUNT(*) INTO perturbations_count
|
||||
FROM information_schema.tables
|
||||
WHERE table_name = 'perturbations';
|
||||
|
||||
SELECT COUNT(*) INTO modifications_count
|
||||
FROM information_schema.tables
|
||||
WHERE table_name = 'perturbations_modifications_parcours';
|
||||
|
||||
IF perturbations_count = 1 AND modifications_count = 1 THEN
|
||||
RAISE NOTICE '✓ Migration réussie : Tables créées avec succès';
|
||||
ELSE
|
||||
RAISE EXCEPTION '✗ Échec de la migration : Les tables n''ont pas été créées correctement';
|
||||
END IF;
|
||||
|
||||
-- Afficher les colonnes créées
|
||||
RAISE NOTICE 'Colonnes de la table perturbations:';
|
||||
FOR rec IN (
|
||||
SELECT column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'perturbations'
|
||||
ORDER BY ordinal_position
|
||||
) LOOP
|
||||
RAISE NOTICE ' - % (%)', rec.column_name, rec.data_type;
|
||||
END LOOP;
|
||||
|
||||
END $$;
|
||||
|
||||
-- Test d'insertion (rollback automatique)
|
||||
DO $$
|
||||
DECLARE
|
||||
test_horaire_id UUID;
|
||||
test_perturbation_id UUID;
|
||||
BEGIN
|
||||
-- Récupérer un horaire existant pour le test
|
||||
SELECT id INTO test_horaire_id FROM horaires LIMIT 1;
|
||||
|
||||
IF test_horaire_id IS NULL THEN
|
||||
RAISE NOTICE 'Aucun horaire disponible pour le test';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- Test d'insertion d'une perturbation
|
||||
INSERT INTO perturbations (
|
||||
horaire_id,
|
||||
type_perturbation,
|
||||
duree_retard_minutes,
|
||||
cause,
|
||||
cause_details,
|
||||
actif
|
||||
) VALUES (
|
||||
test_horaire_id,
|
||||
'retard',
|
||||
15,
|
||||
'incident_technique',
|
||||
'Test de migration',
|
||||
false
|
||||
) RETURNING id INTO test_perturbation_id;
|
||||
|
||||
IF test_perturbation_id IS NOT NULL THEN
|
||||
RAISE NOTICE '✓ Test d''insertion réussi';
|
||||
-- Supprimer le test
|
||||
DELETE FROM perturbations WHERE id = test_perturbation_id;
|
||||
ELSE
|
||||
RAISE EXCEPTION '✗ Échec du test d''insertion';
|
||||
END IF;
|
||||
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE NOTICE '✗ Erreur lors du test : %', SQLERRM;
|
||||
END $$;
|
||||
|
||||
-- Afficher un résumé
|
||||
SELECT
|
||||
'Migration terminée' as statut,
|
||||
NOW() as date_migration,
|
||||
(SELECT COUNT(*) FROM perturbations) as nombre_perturbations,
|
||||
(SELECT COUNT(*) FROM perturbations_modifications_parcours) as nombre_modifications;
|
||||
|
||||
-- ============================================================================
|
||||
-- ROLLBACK (à utiliser en cas de problème)
|
||||
-- ============================================================================
|
||||
-- DROP TABLE IF EXISTS perturbations_modifications_parcours CASCADE;
|
||||
-- DROP TABLE IF EXISTS perturbations CASCADE;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- ============================================================================
|
||||
-- Migration : Ajout du champ "quai" dans horaires_gares_desservies
|
||||
-- Date : 2026-03-02
|
||||
-- ============================================================================
|
||||
|
||||
-- 1. Ajouter la colonne quai à la table horaires_gares_desservies
|
||||
ALTER TABLE horaires_gares_desservies
|
||||
ADD COLUMN IF NOT EXISTS quai VARCHAR(50) DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN horaires_gares_desservies.quai IS 'Numéro ou nom du quai attribué à cet arrêt (ex: "Voie 3", "Quai A")';
|
||||
|
||||
-- 2. Mettre à jour l'index pour accélérer les recherches par gare_id
|
||||
CREATE INDEX IF NOT EXISTS idx_hgd_gare_id ON horaires_gares_desservies(gare_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_hgd_horaire_id ON horaires_gares_desservies(horaire_id);
|
||||
|
||||
-- Note : Le snapshot JSONB horaires.gares_desservies sera mis à jour
|
||||
-- automatiquement via l'application lors de la sauvegarde des quais.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- ============================================================================
|
||||
-- MIGRATION : Ajout de la colonne quais_hall (JSONB) dans la table gares
|
||||
-- Date : 2026-06-10
|
||||
-- Description : Stocke l'association entre les halls et les quais au format JSON.
|
||||
-- Structure :
|
||||
-- {
|
||||
-- "hall": {
|
||||
-- "Nom du hall": ["Voie A", "Voie B"]
|
||||
-- }
|
||||
-- }
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER dans l'éditeur SQL Supabase :
|
||||
-- https://supabase.com/dashboard → votre projet → SQL Editor
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE public.gares
|
||||
ADD COLUMN IF NOT EXISTS quais_hall JSONB DEFAULT '{"hall": {}}'::jsonb;
|
||||
|
||||
COMMENT ON COLUMN public.gares.quais_hall IS
|
||||
'Association des quais aux halls sous forme JSON : {"hall": {"Nom du Hall": ["Quai 1", "Quai 2"]}}';
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Ajout du champ region_id (slug) à la table menus_regionaux
|
||||
ALTER TABLE menus_regionaux ADD COLUMN IF NOT EXISTS region_id VARCHAR(64);
|
||||
-- Index pour accélérer la recherche par région
|
||||
CREATE INDEX IF NOT EXISTS idx_menus_regionaux_region_id ON menus_regionaux(region_id);
|
||||
@@ -0,0 +1,32 @@
|
||||
-- Migration: Ajouter la colonne "region" à la table lignes
|
||||
-- et créer la table "parametres" pour les paramètres du site
|
||||
|
||||
-- 1. Ajout de la colonne region à la table lignes
|
||||
ALTER TABLE lignes ADD COLUMN IF NOT EXISTS region VARCHAR(100);
|
||||
|
||||
-- 2. Création de la table parametres
|
||||
CREATE TABLE IF NOT EXISTS parametres (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
cle VARCHAR(100) NOT NULL UNIQUE,
|
||||
valeur JSONB,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- Insérer les paramètres par défaut
|
||||
INSERT INTO parametres (cle, valeur) VALUES
|
||||
('general', '{"nom_site": "", "description_site": "", "region": "", "logo_header_url": ""}'),
|
||||
('tarifs', '{"billets": [], "abonnements": [], "remises": []}')
|
||||
ON CONFLICT (cle) DO NOTHING;
|
||||
|
||||
-- 3. Ajout de la colonne quai_defaut à horaires_gares_desservies (pour attribution des quais)
|
||||
ALTER TABLE horaires_gares_desservies ADD COLUMN IF NOT EXISTS quai_id UUID REFERENCES gare_quais(id);
|
||||
|
||||
-- 4. Politique RLS pour parametres (accès complet pour les utilisateurs authentifiés)
|
||||
ALTER TABLE parametres ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "parametres_select_all" ON parametres FOR SELECT USING (true);
|
||||
CREATE POLICY "parametres_insert_all" ON parametres FOR INSERT WITH CHECK (true);
|
||||
CREATE POLICY "parametres_update_all" ON parametres FOR UPDATE USING (true);
|
||||
CREATE POLICY "parametres_delete_all" ON parametres FOR DELETE USING (true);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE tarifs
|
||||
ADD COLUMN IF NOT EXISTS details JSONB;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
-- Migration : table menus_regionaux
|
||||
CREATE TABLE IF NOT EXISTS menus_regionaux (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
nom VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
actif BOOLEAN DEFAULT false,
|
||||
structure JSONB DEFAULT '[]'::jsonb,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- RLS
|
||||
ALTER TABLE menus_regionaux ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY "menus_select" ON menus_regionaux FOR SELECT USING (true);
|
||||
CREATE POLICY "menus_insert" ON menus_regionaux FOR INSERT WITH CHECK (true);
|
||||
CREATE POLICY "menus_update" ON menus_regionaux FOR UPDATE USING (true);
|
||||
CREATE POLICY "menus_delete" ON menus_regionaux FOR DELETE USING (true);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
-- Tables de messagerie interne (V1)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.messagerie_threads (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.messagerie_participants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
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 TABLE IF NOT EXISTS public.messagerie_messages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
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_participants_user_id ON public.messagerie_participants(user_id);
|
||||
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);
|
||||
|
||||
-- RLS simple: participant du thread uniquement
|
||||
ALTER TABLE public.messagerie_threads ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.messagerie_participants ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.messagerie_messages ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS messagerie_threads_select ON public.messagerie_threads;
|
||||
CREATE POLICY messagerie_threads_select ON public.messagerie_threads
|
||||
FOR SELECT TO anon
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.messagerie_participants p
|
||||
WHERE p.thread_id = messagerie_threads.id
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS messagerie_threads_insert ON public.messagerie_threads;
|
||||
CREATE POLICY messagerie_threads_insert ON public.messagerie_threads
|
||||
FOR INSERT TO anon
|
||||
WITH CHECK (true);
|
||||
|
||||
DROP POLICY IF EXISTS messagerie_threads_update ON public.messagerie_threads;
|
||||
CREATE POLICY messagerie_threads_update ON public.messagerie_threads
|
||||
FOR UPDATE TO anon
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.messagerie_participants p
|
||||
WHERE p.thread_id = messagerie_threads.id
|
||||
)
|
||||
)
|
||||
WITH CHECK (true);
|
||||
|
||||
DROP POLICY IF EXISTS messagerie_participants_select ON public.messagerie_participants;
|
||||
CREATE POLICY messagerie_participants_select ON public.messagerie_participants
|
||||
FOR SELECT TO anon
|
||||
USING (true);
|
||||
|
||||
DROP POLICY IF EXISTS messagerie_participants_insert ON public.messagerie_participants;
|
||||
CREATE POLICY messagerie_participants_insert ON public.messagerie_participants
|
||||
FOR INSERT TO anon
|
||||
WITH CHECK (true);
|
||||
|
||||
DROP POLICY IF EXISTS messagerie_messages_select ON public.messagerie_messages;
|
||||
CREATE POLICY messagerie_messages_select ON public.messagerie_messages
|
||||
FOR SELECT TO anon
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.messagerie_participants p
|
||||
WHERE p.thread_id = messagerie_messages.thread_id
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS messagerie_messages_insert ON public.messagerie_messages;
|
||||
CREATE POLICY messagerie_messages_insert ON public.messagerie_messages
|
||||
FOR INSERT TO anon
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.messagerie_participants p
|
||||
WHERE p.thread_id = messagerie_messages.thread_id
|
||||
AND p.user_id = messagerie_messages.sender_id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
-- ============================================================================
|
||||
-- MIGRATION : Création du bucket Storage "materiel-roulant"
|
||||
-- Date : 2026-03-01
|
||||
-- Description : Crée le bucket public "materiel-roulant" dans Supabase Storage
|
||||
-- et configure les policies RLS.
|
||||
--
|
||||
-- ⚠️ IMPORTANT — Authentification custom :
|
||||
-- Cette application utilise une auth custom (RPC authenticate_user + localStorage)
|
||||
-- et NON supabase.auth.signInWithPassword(). Par conséquent auth.uid() est
|
||||
-- toujours NULL côté Supabase. Les policies utilisent donc le rôle "anon"
|
||||
-- (clé publique anonyme) pour autoriser les opérations Storage depuis le front.
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER UNE SEULE FOIS dans l'éditeur SQL de votre projet Supabase
|
||||
-- (https://supabase.com/dashboard → votre projet → SQL Editor)
|
||||
-- ============================================================================
|
||||
|
||||
-- 1. Créer le bucket s'il n'existe pas encore
|
||||
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 NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. Policies RLS sur storage.objects
|
||||
-- ============================================================================
|
||||
|
||||
-- Lecture publique
|
||||
DROP POLICY IF EXISTS "materiel-roulant - lecture publique" ON storage.objects;
|
||||
CREATE POLICY "materiel-roulant - lecture publique"
|
||||
ON storage.objects FOR SELECT
|
||||
USING (bucket_id = 'materiel-roulant');
|
||||
|
||||
-- Upload (anon car auth custom — auth.uid() toujours NULL)
|
||||
DROP POLICY IF EXISTS "materiel-roulant - upload authentifié" ON storage.objects;
|
||||
CREATE POLICY "materiel-roulant - upload authentifié"
|
||||
ON storage.objects FOR INSERT
|
||||
WITH CHECK (bucket_id = 'materiel-roulant');
|
||||
|
||||
-- Mise à jour
|
||||
DROP POLICY IF EXISTS "materiel-roulant - update authentifié" ON storage.objects;
|
||||
CREATE POLICY "materiel-roulant - update authentifié"
|
||||
ON storage.objects FOR UPDATE
|
||||
USING (bucket_id = 'materiel-roulant');
|
||||
|
||||
-- Suppression
|
||||
DROP POLICY IF EXISTS "materiel-roulant - delete authentifié" ON storage.objects;
|
||||
CREATE POLICY "materiel-roulant - delete authentifié"
|
||||
ON storage.objects FOR DELETE
|
||||
USING (bucket_id = 'materiel-roulant');
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-- Création de la table "tarifs" pour gérer les offres de billets, abonnements, remises par région
|
||||
CREATE TABLE IF NOT EXISTS tarifs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
nom VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) NOT NULL, -- billet, abonnement, remise
|
||||
description TEXT,
|
||||
prix_minimum NUMERIC(10,2),
|
||||
prix_par_km NUMERIC(10,3),
|
||||
age_maximum INTEGER,
|
||||
regions TEXT[] NOT NULL, -- tableau d'identifiants de régions
|
||||
date_debut DATE,
|
||||
date_fin DATE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Index pour recherche rapide par région
|
||||
CREATE INDEX IF NOT EXISTS idx_tarifs_regions ON tarifs USING GIN (regions);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
-- v2: add trigger to keep updated_at in sync
|
||||
CREATE OR REPLACE FUNCTION touch_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_touch_tarifs_updated_at ON tarifs;
|
||||
CREATE TRIGGER trg_touch_tarifs_updated_at
|
||||
BEFORE UPDATE ON tarifs
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE touch_updated_at();
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
-- ============================================================================
|
||||
-- CRÉATION DES TABLES POUR LA GESTION DES TRAVAUX
|
||||
-- ============================================================================
|
||||
|
||||
-- Table principale des travaux
|
||||
CREATE TABLE IF NOT EXISTS travaux (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
titre TEXT NOT NULL,
|
||||
description TEXT,
|
||||
date_debut DATE NOT NULL,
|
||||
date_fin DATE NOT NULL,
|
||||
jours_travaux TEXT[] NOT NULL, -- ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche']
|
||||
heure_debut TIME NOT NULL,
|
||||
heure_fin TIME NOT NULL,
|
||||
actif BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
CONSTRAINT valid_dates CHECK (date_fin >= date_debut),
|
||||
CONSTRAINT valid_hours CHECK (heure_fin > heure_debut)
|
||||
);
|
||||
|
||||
-- Table de liaison entre travaux et sillons de substitution
|
||||
CREATE TABLE IF NOT EXISTS travaux_substitutions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
travaux_id UUID NOT NULL REFERENCES travaux(id) ON DELETE CASCADE,
|
||||
horaire_id UUID NOT NULL REFERENCES horaires(id) ON DELETE CASCADE,
|
||||
ordre INTEGER DEFAULT 0, -- Ordre d'affichage des substitutions
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
|
||||
UNIQUE(travaux_id, horaire_id)
|
||||
);
|
||||
|
||||
-- Index pour améliorer les performances
|
||||
CREATE INDEX IF NOT EXISTS idx_travaux_dates ON travaux(date_debut, date_fin);
|
||||
CREATE INDEX IF NOT EXISTS idx_travaux_actif ON travaux(actif);
|
||||
CREATE INDEX IF NOT EXISTS idx_travaux_deleted ON travaux(deleted_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_travaux_substitutions_travaux ON travaux_substitutions(travaux_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_travaux_substitutions_horaire ON travaux_substitutions(horaire_id);
|
||||
|
||||
-- Fonction pour mettre à jour updated_at automatiquement
|
||||
CREATE OR REPLACE FUNCTION update_travaux_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Trigger pour mettre à jour updated_at
|
||||
DROP TRIGGER IF EXISTS trigger_update_travaux_updated_at ON travaux;
|
||||
CREATE TRIGGER trigger_update_travaux_updated_at
|
||||
BEFORE UPDATE ON travaux
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_travaux_updated_at();
|
||||
|
||||
-- Commentaires sur les tables
|
||||
COMMENT ON TABLE travaux IS 'Table principale pour la gestion des travaux sur les lignes';
|
||||
COMMENT ON TABLE travaux_substitutions IS 'Table de liaison entre travaux et horaires de substitution';
|
||||
|
||||
COMMENT ON COLUMN travaux.jours_travaux IS 'Jours de la semaine où les travaux ont lieu';
|
||||
COMMENT ON COLUMN travaux.actif IS 'Indique si les travaux sont actifs';
|
||||
COMMENT ON COLUMN travaux.deleted_at IS 'Date de suppression (soft delete)';
|
||||
COMMENT ON COLUMN travaux_substitutions.ordre IS 'Ordre d''affichage des substitutions';
|
||||
|
||||
-- ============================================================================
|
||||
-- DONNÉES DE TEST (optionnel)
|
||||
-- ============================================================================
|
||||
|
||||
-- Exemple de travaux
|
||||
INSERT INTO travaux (titre, description, date_debut, date_fin, jours_travaux, heure_debut, heure_fin, actif)
|
||||
VALUES
|
||||
('Travaux ligne Paris-Lyon', 'Rénovation des voies', '2026-03-01', '2026-03-31', ARRAY['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi'], '09:00', '17:00', true),
|
||||
('Maintenance gare de Lyon', 'Travaux de maintenance planifiés', '2026-04-15', '2026-04-20', ARRAY['samedi', 'dimanche'], '06:00', '22:00', true)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Afficher le résultat
|
||||
SELECT
|
||||
'Tables travaux créées avec succès' as statut,
|
||||
(SELECT COUNT(*) FROM travaux) as nombre_travaux,
|
||||
(SELECT COUNT(*) FROM travaux_substitutions) as nombre_substitutions;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
-- ============================================================================
|
||||
-- CORRECTIF : Policies RLS du bucket "banque-annonces" et des tables SQL
|
||||
-- Date : 2026-07-02
|
||||
--
|
||||
-- Problème : L'application utilise la clé anon et une authentification custom. Les
|
||||
-- policies de banque_mots et de storage.objects qui exigent un utilisateur
|
||||
-- authentifié (TO authenticated) bloquent l'INSERT/UPDATE/DELETE et provoquent
|
||||
-- l'erreur "new row violates row-level security policy".
|
||||
--
|
||||
-- Solution : Recréer des policies pour permettre SELECT, INSERT, UPDATE, DELETE
|
||||
-- sur le bucket 'banque-annonces' et sur les tables de la base de données.
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER dans l'éditeur SQL Supabase :
|
||||
-- https://app.supabase.com → votre projet → SQL Editor
|
||||
-- ============================================================================
|
||||
|
||||
-- 1. Réglage des politiques RLS pour les tables de base de données
|
||||
-- ============================================================================
|
||||
|
||||
-- Table: banque_mots
|
||||
DROP POLICY IF EXISTS "Authenticated users can read banque_mots" ON public.banque_mots;
|
||||
DROP POLICY IF EXISTS "Authenticated users can insert banque_mots" ON public.banque_mots;
|
||||
DROP POLICY IF EXISTS "Authenticated users can update banque_mots" ON public.banque_mots;
|
||||
|
||||
CREATE POLICY "Public read banque_mots" ON public.banque_mots FOR SELECT USING (deleted_at IS NULL);
|
||||
CREATE POLICY "Public insert banque_mots" ON public.banque_mots FOR INSERT WITH CHECK (true);
|
||||
CREATE POLICY "Public update banque_mots" ON public.banque_mots FOR UPDATE USING (true);
|
||||
CREATE POLICY "Public delete banque_mots" ON public.banque_mots FOR DELETE USING (true);
|
||||
|
||||
-- Table: annonces_templates
|
||||
DROP POLICY IF EXISTS "Authenticated users can read templates" ON public.annonces_templates;
|
||||
DROP POLICY IF EXISTS "Authenticated users can insert templates" ON public.annonces_templates;
|
||||
DROP POLICY IF EXISTS "Authenticated users can update templates" ON public.annonces_templates;
|
||||
|
||||
CREATE POLICY "Public read templates" ON public.annonces_templates FOR SELECT USING (deleted_at IS NULL);
|
||||
CREATE POLICY "Public insert templates" ON public.annonces_templates FOR INSERT WITH CHECK (true);
|
||||
CREATE POLICY "Public update templates" ON public.annonces_templates FOR UPDATE USING (true);
|
||||
CREATE POLICY "Public delete templates" ON public.annonces_templates FOR DELETE USING (true);
|
||||
|
||||
-- Table: annonces_enregistrees
|
||||
DROP POLICY IF EXISTS "Authenticated users can read annonces" ON public.annonces_enregistrees;
|
||||
DROP POLICY IF EXISTS "Authenticated users can insert annonces" ON public.annonces_enregistrees;
|
||||
DROP POLICY IF EXISTS "Authenticated users can update annonces" ON public.annonces_enregistrees;
|
||||
|
||||
CREATE POLICY "Public read annonces" ON public.annonces_enregistrees FOR SELECT USING (deleted_at IS NULL);
|
||||
CREATE POLICY "Public insert annonces" ON public.annonces_enregistrees FOR INSERT WITH CHECK (true);
|
||||
CREATE POLICY "Public update annonces" ON public.annonces_enregistrees FOR UPDATE USING (true);
|
||||
CREATE POLICY "Public delete annonces" ON public.annonces_enregistrees FOR DELETE USING (true);
|
||||
|
||||
|
||||
-- 2. Réglage des politiques RLS du Bucket Storage "banque-annonces"
|
||||
-- ============================================================================
|
||||
|
||||
-- Supprimer les anciennes politiques storage si elles existent
|
||||
DROP POLICY IF EXISTS "Public read access on banque-annonces" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Authenticated users can upload to banque-annonces" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Authenticated users can delete from banque-annonces" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public upload access on banque-annonces" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public update access on banque-annonces" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public delete access on banque-annonces" ON storage.objects;
|
||||
|
||||
-- Lecture publique pour tous
|
||||
CREATE POLICY "Public read access on banque-annonces"
|
||||
ON storage.objects FOR SELECT
|
||||
USING (bucket_id = 'banque-annonces');
|
||||
|
||||
-- Insertion publique (Upload)
|
||||
CREATE POLICY "Public upload access on banque-annonces"
|
||||
ON storage.objects FOR INSERT
|
||||
WITH CHECK (bucket_id = 'banque-annonces');
|
||||
|
||||
-- Mise à jour publique (Upsert)
|
||||
CREATE POLICY "Public update access on banque-annonces"
|
||||
ON storage.objects FOR UPDATE
|
||||
USING (bucket_id = 'banque-annonces');
|
||||
|
||||
-- Suppression publique (Delete)
|
||||
CREATE POLICY "Public delete access on banque-annonces"
|
||||
ON storage.objects FOR DELETE
|
||||
USING (bucket_id = 'banque-annonces');
|
||||
@@ -0,0 +1,43 @@
|
||||
-- ============================================================================
|
||||
-- CORRECTIF : Policies RLS du bucket "fiches-horaires"
|
||||
-- Date : 2026-03-01
|
||||
--
|
||||
-- Problème : L'application utilise la clé anon et une authentification custom. Les
|
||||
-- policies de storage.objects qui exigent auth.uid() bloquent l'INSERT
|
||||
-- et provoquent l'erreur "new row violates row-level security policy".
|
||||
--
|
||||
-- Solution : Recréer des policies pour permettre l'INSERT/UPDATE/DELETE/SELECT
|
||||
-- sur le bucket 'fiches-horaires' en se basant sur bucket_id seulement.
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER dans l'éditeur SQL Supabase :
|
||||
-- https://app.supabase.com → votre projet → SQL Editor
|
||||
-- ============================================================================
|
||||
|
||||
-- Supprimer d'abord les policies existantes (si elles existent)
|
||||
DROP POLICY IF EXISTS "fiches-horaires - lecture publique" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "fiches-horaires - upload" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "fiches-horaires - update" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "fiches-horaires - delete" ON storage.objects;
|
||||
|
||||
-- Créer des policies permissives basées sur le bucket_id
|
||||
CREATE POLICY "fiches-horaires - lecture publique"
|
||||
ON storage.objects FOR SELECT
|
||||
USING (bucket_id = 'fiches-horaires');
|
||||
|
||||
CREATE POLICY "fiches-horaires - upload"
|
||||
ON storage.objects FOR INSERT
|
||||
WITH CHECK (bucket_id = 'fiches-horaires');
|
||||
|
||||
CREATE POLICY "fiches-horaires - update"
|
||||
ON storage.objects FOR UPDATE
|
||||
USING (bucket_id = 'fiches-horaires');
|
||||
|
||||
CREATE POLICY "fiches-horaires - delete"
|
||||
ON storage.objects FOR DELETE
|
||||
USING (bucket_id = 'fiches-horaires');
|
||||
|
||||
-- Note : Si vous souhaitez restreindre les uploads aux utilisateurs authentifiés,
|
||||
-- remplacez la clause WITH CHECK (bucket_id = 'fiches-horaires') par
|
||||
-- WITH CHECK (bucket_id = 'fiches-horaires' AND auth.uid() IS NOT NULL)
|
||||
-- et assurez-vous que vos utilisateurs sont bien authentifiés côté front.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
-- ============================================================================
|
||||
-- CORRECTIF : Policies RLS du bucket "materiel-roulant"
|
||||
-- Date : 2026-03-01
|
||||
--
|
||||
-- Problème : L'application utilise une authentification custom (RPC authenticate_user
|
||||
-- + localStorage). supabase.auth.signIn() n'est jamais appelé, donc
|
||||
-- auth.uid() est toujours NULL → la policy WITH CHECK auth.uid() IS NOT NULL
|
||||
-- bloque tous les uploads.
|
||||
--
|
||||
-- Solution : Policies ouvertes sur le bucket (accès par clé anon publique).
|
||||
-- Le bucket est déjà créé — ce script corrige uniquement les policies.
|
||||
--
|
||||
-- ⚠️ À EXÉCUTER dans l'éditeur SQL Supabase :
|
||||
-- https://supabase.com/dashboard → votre projet → SQL Editor
|
||||
-- ============================================================================
|
||||
|
||||
-- Supprimer toutes les policies existantes du bucket
|
||||
DROP POLICY IF EXISTS "materiel-roulant - lecture publique" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "materiel-roulant - upload authentifié" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "materiel-roulant - update authentifié" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "materiel-roulant - delete authentifié" ON storage.objects;
|
||||
|
||||
-- Recréer les policies sans condition auth.uid()
|
||||
-- (l'app utilise la clé anon, auth.uid() est toujours NULL)
|
||||
|
||||
CREATE POLICY "materiel-roulant - lecture publique"
|
||||
ON storage.objects FOR SELECT
|
||||
USING (bucket_id = 'materiel-roulant');
|
||||
|
||||
CREATE POLICY "materiel-roulant - upload authentifié"
|
||||
ON storage.objects FOR INSERT
|
||||
WITH CHECK (bucket_id = 'materiel-roulant');
|
||||
|
||||
CREATE POLICY "materiel-roulant - update authentifié"
|
||||
ON storage.objects FOR UPDATE
|
||||
USING (bucket_id = 'materiel-roulant');
|
||||
|
||||
CREATE POLICY "materiel-roulant - delete authentifié"
|
||||
ON storage.objects FOR DELETE
|
||||
USING (bucket_id = 'materiel-roulant');
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,62 @@
|
||||
-- Migration: repair foreign key between horaires_gares_desservies.horaire_id and horaires.id
|
||||
-- This script will:
|
||||
-- 1) check the column types of horaires.id and horaires_gares_desservies.horaire_id
|
||||
-- 2) if types match and FK doesn't exist, create the FK constraint
|
||||
-- 3) otherwise, print a NOTICE explaining the mismatch for manual resolution
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
t_horaire TEXT;
|
||||
t_horaire_fk TEXT;
|
||||
cnt INT;
|
||||
BEGIN
|
||||
-- ensure target tables exist
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'horaires' AND relkind = 'r') THEN
|
||||
RAISE NOTICE 'Table public.horaires does not exist in this database/schema.';
|
||||
RETURN;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'horaires_gares_desservies' AND relkind = 'r') THEN
|
||||
RAISE NOTICE 'Table public.horaires_gares_desservies does not exist in this database/schema.';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- get formatted types
|
||||
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
|
||||
INTO t_horaire
|
||||
FROM pg_attribute a
|
||||
WHERE a.attrelid = 'public.horaires'::regclass AND a.attname = 'id';
|
||||
|
||||
SELECT pg_catalog.format_type(a.atttypid, a.atttypmod)
|
||||
INTO t_horaire_fk
|
||||
FROM pg_attribute a
|
||||
WHERE a.attrelid = 'public.horaires_gares_desservies'::regclass AND a.attname = 'horaire_id';
|
||||
|
||||
RAISE NOTICE 'Detected types -> horaires.id: %, horaires_gares_desservies.horaire_id: %', t_horaire, t_horaire_fk;
|
||||
|
||||
-- check existing FK from horaires_gares_desservies to horaires
|
||||
SELECT count(*) INTO cnt
|
||||
FROM pg_constraint c
|
||||
WHERE c.conrelid = 'public.horaires_gares_desservies'::regclass
|
||||
AND c.confrelid = 'public.horaires'::regclass
|
||||
AND c.contype = 'f';
|
||||
|
||||
IF cnt > 0 THEN
|
||||
RAISE NOTICE 'A foreign-key constraint from horaires_gares_desservies to horaires already exists (count=%). No action taken.', cnt;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- If types match, create the FK
|
||||
IF t_horaire IS NOT NULL AND t_horaire_fk IS NOT NULL AND t_horaire = t_horaire_fk THEN
|
||||
EXECUTE 'ALTER TABLE public.horaires_gares_desservies
|
||||
ADD CONSTRAINT fk_horaires_gares_horaire_id
|
||||
FOREIGN KEY (horaire_id)
|
||||
REFERENCES public.horaires(id)
|
||||
ON DELETE CASCADE';
|
||||
RAISE NOTICE 'Foreign key constraint fk_horaires_gares_horaire_id created successfully.';
|
||||
ELSE
|
||||
RAISE NOTICE 'Column types do not match or could not be determined. Manual intervention required.';
|
||||
RAISE NOTICE 'Detected types -> horaires.id: %, horaires_gares_desservies.horaire_id: %', t_horaire, t_horaire_fk;
|
||||
RAISE NOTICE 'If both should be UUID, consider converting [or recreating] the columns and then re-run this script.';
|
||||
RAISE NOTICE 'If both should be BIGINT, consider altering types to bigint. Do not run automatic conversion without backup.';
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,204 @@
|
||||
-- ============================================================================
|
||||
-- DONNÉES DE TEST: Perturbations
|
||||
-- Description: Jeux de données pour tester le système de perturbations
|
||||
-- ============================================================================
|
||||
|
||||
-- Note: Ces données sont à insérer APRÈS avoir des horaires existants dans la base
|
||||
|
||||
-- Exemple 1: Retard sur un train
|
||||
-- Remplacer 'HORAIRE_ID_1' par un véritable ID d'horaire existant
|
||||
INSERT INTO perturbations (
|
||||
horaire_id,
|
||||
type_perturbation,
|
||||
duree_retard_minutes,
|
||||
cause,
|
||||
cause_details,
|
||||
date_debut,
|
||||
date_fin,
|
||||
actif,
|
||||
notes
|
||||
) VALUES (
|
||||
(SELECT id FROM horaires LIMIT 1), -- Premier horaire de la base
|
||||
'retard',
|
||||
25,
|
||||
'incident_technique',
|
||||
'Panne de signalisation à Paris-Montparnasse',
|
||||
NOW(),
|
||||
NOW() + INTERVAL '2 hours',
|
||||
true,
|
||||
'Retard estimé de 25 minutes sur l''ensemble du parcours'
|
||||
);
|
||||
|
||||
-- Exemple 2: Suppression de train avec substitution
|
||||
INSERT INTO perturbations (
|
||||
horaire_id,
|
||||
type_perturbation,
|
||||
duree_retard_minutes,
|
||||
cause,
|
||||
cause_details,
|
||||
sillon_substitution_id,
|
||||
date_debut,
|
||||
date_fin,
|
||||
actif,
|
||||
notes
|
||||
) VALUES (
|
||||
(SELECT id FROM horaires LIMIT 1 OFFSET 1), -- Deuxième horaire
|
||||
'suppression',
|
||||
NULL,
|
||||
'mouvement_social',
|
||||
'Grève nationale du personnel roulant',
|
||||
(SELECT id FROM horaires LIMIT 1 OFFSET 2), -- Train de substitution
|
||||
NOW(),
|
||||
NOW() + INTERVAL '1 day',
|
||||
true,
|
||||
'Train supprimé - Report sur le train suivant disponible'
|
||||
);
|
||||
|
||||
-- Exemple 3: Modification de parcours
|
||||
DO $$
|
||||
DECLARE
|
||||
v_perturbation_id UUID;
|
||||
v_horaire_id UUID;
|
||||
BEGIN
|
||||
-- Récupérer un horaire avec des gares desservies
|
||||
SELECT h.id INTO v_horaire_id
|
||||
FROM horaires h
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM horaires_gares_desservies hgd
|
||||
WHERE hgd.horaire_id = h.id
|
||||
)
|
||||
LIMIT 1;
|
||||
|
||||
IF v_horaire_id IS NULL THEN
|
||||
RAISE NOTICE 'Aucun horaire avec gares desservies trouvé, test ignoré';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- Créer la perturbation
|
||||
INSERT INTO perturbations (
|
||||
horaire_id,
|
||||
type_perturbation,
|
||||
duree_retard_minutes,
|
||||
cause,
|
||||
cause_details,
|
||||
date_debut,
|
||||
actif,
|
||||
notes
|
||||
) VALUES (
|
||||
v_horaire_id,
|
||||
'modification_parcours',
|
||||
NULL,
|
||||
'travaux',
|
||||
'Travaux de maintenance sur la voie entre Lyon et Valence',
|
||||
NOW(),
|
||||
true,
|
||||
'Modification du parcours : certaines gares ne seront pas desservies'
|
||||
) RETURNING id INTO v_perturbation_id;
|
||||
|
||||
-- Ajouter des modifications de parcours
|
||||
-- Supprimer une gare du parcours
|
||||
INSERT INTO perturbations_modifications_parcours (
|
||||
perturbation_id,
|
||||
type_modification,
|
||||
gare_id,
|
||||
gare_nom,
|
||||
ordre
|
||||
) SELECT
|
||||
v_perturbation_id,
|
||||
'gare_supprimee',
|
||||
gare_id,
|
||||
gare_nom,
|
||||
ordre
|
||||
FROM horaires_gares_desservies
|
||||
WHERE horaire_id = v_horaire_id
|
||||
LIMIT 1;
|
||||
|
||||
RAISE NOTICE 'Perturbation de modification de parcours créée avec succès';
|
||||
END $$;
|
||||
|
||||
-- Exemple 4: Retard causé par les conditions météorologiques
|
||||
INSERT INTO perturbations (
|
||||
horaire_id,
|
||||
type_perturbation,
|
||||
duree_retard_minutes,
|
||||
cause,
|
||||
cause_details,
|
||||
date_debut,
|
||||
date_fin,
|
||||
actif,
|
||||
notes
|
||||
) VALUES (
|
||||
(SELECT id FROM horaires LIMIT 1 OFFSET 3),
|
||||
'retard',
|
||||
45,
|
||||
'conditions_meteorologiques',
|
||||
'Fortes chutes de neige en Auvergne-Rhône-Alpes',
|
||||
NOW(),
|
||||
NOW() + INTERVAL '4 hours',
|
||||
true,
|
||||
'Circulation ralentie sur l''ensemble des lignes'
|
||||
);
|
||||
|
||||
-- Exemple 5: Perturbation inactive (historique)
|
||||
INSERT INTO perturbations (
|
||||
horaire_id,
|
||||
type_perturbation,
|
||||
duree_retard_minutes,
|
||||
cause,
|
||||
cause_details,
|
||||
date_debut,
|
||||
date_fin,
|
||||
actif,
|
||||
notes
|
||||
) VALUES (
|
||||
(SELECT id FROM horaires LIMIT 1 OFFSET 4),
|
||||
'retard',
|
||||
10,
|
||||
'affluence',
|
||||
'Affluence exceptionnelle lors du départ',
|
||||
NOW() - INTERVAL '1 day',
|
||||
NOW() - INTERVAL '1 day' + INTERVAL '30 minutes',
|
||||
false,
|
||||
'Perturbation terminée - Retard résorbé'
|
||||
);
|
||||
|
||||
-- Afficher un résumé des perturbations de test créées
|
||||
SELECT
|
||||
'Données de test créées' as statut,
|
||||
COUNT(*) as nombre_perturbations_test,
|
||||
COUNT(*) FILTER (WHERE actif = true) as perturbations_actives,
|
||||
COUNT(*) FILTER (WHERE actif = false) as perturbations_inactives
|
||||
FROM perturbations
|
||||
WHERE notes LIKE '%test%' OR notes LIKE '%Test%';
|
||||
|
||||
-- Afficher les perturbations créées
|
||||
SELECT
|
||||
p.id,
|
||||
p.type_perturbation,
|
||||
p.duree_retard_minutes,
|
||||
p.cause,
|
||||
p.actif,
|
||||
h.numero_train,
|
||||
h.gare_depart_nom,
|
||||
h.gare_arrivee_nom
|
||||
FROM perturbations p
|
||||
JOIN horaires h ON p.horaire_id = h.id
|
||||
ORDER BY p.created_at DESC
|
||||
LIMIT 10;
|
||||
|
||||
-- ============================================================================
|
||||
-- NETTOYAGE DES DONNÉES DE TEST
|
||||
-- ============================================================================
|
||||
-- Décommenter pour supprimer toutes les données de test
|
||||
/*
|
||||
DELETE FROM perturbations_modifications_parcours
|
||||
WHERE perturbation_id IN (
|
||||
SELECT id FROM perturbations
|
||||
WHERE notes LIKE '%test%' OR notes LIKE '%Test%'
|
||||
);
|
||||
|
||||
DELETE FROM perturbations
|
||||
WHERE notes LIKE '%test%' OR notes LIKE '%Test%';
|
||||
|
||||
SELECT 'Données de test supprimées' as statut;
|
||||
*/
|
||||
Reference in new issue
Block a user