-- Script de migration vers le schéma le plus récent avec liens courts -- À exécuter dans votre base de données Supabase -- ======================================== -- ÉTAPE 1: Ajout des colonnes manquantes -- ======================================== -- Ajouter la colonne slug aux campagnes si elle n'existe pas DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'campaigns' AND column_name = 'slug' ) THEN ALTER TABLE campaigns ADD COLUMN slug TEXT UNIQUE; RAISE NOTICE 'Colonne slug ajoutée à la table campaigns'; ELSE RAISE NOTICE 'Colonne slug existe déjà dans la table campaigns'; END IF; END $$; -- Ajouter la colonne short_id aux participants si elle n'existe pas DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'participants' AND column_name = 'short_id' ) THEN ALTER TABLE participants ADD COLUMN short_id TEXT UNIQUE; RAISE NOTICE 'Colonne short_id ajoutée à la table participants'; ELSE RAISE NOTICE 'Colonne short_id existe déjà dans la table participants'; END IF; END $$; -- ======================================== -- ÉTAPE 2: Création des fonctions utilitaires -- ======================================== -- Fonction pour générer un slug à partir d'un titre CREATE OR REPLACE FUNCTION generate_slug(title TEXT) RETURNS TEXT AS $$ DECLARE generated_slug TEXT; counter INTEGER := 0; base_slug TEXT; BEGIN -- Convertir en minuscules et remplacer les caractères spéciaux base_slug := lower(regexp_replace(title, '[^a-zA-Z0-9\s]', '', 'g')); base_slug := regexp_replace(base_slug, '\s+', '-', 'g'); base_slug := trim(both '-' from base_slug); -- Si le slug est vide, utiliser 'campagne' IF base_slug = '' THEN base_slug := 'campagne'; END IF; generated_slug := base_slug; -- Vérifier si le slug existe déjà et ajouter un numéro si nécessaire WHILE EXISTS (SELECT 1 FROM campaigns WHERE campaigns.slug = generated_slug) LOOP counter := counter + 1; generated_slug := base_slug || '-' || counter; END LOOP; RETURN generated_slug; END; $$ LANGUAGE plpgsql; -- Fonction pour générer un short_id unique CREATE OR REPLACE FUNCTION generate_short_id() RETURNS TEXT AS $$ DECLARE chars TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; result TEXT := ''; i INTEGER; generated_short_id TEXT; counter INTEGER := 0; BEGIN LOOP -- Générer un identifiant de 6 caractères result := ''; FOR i IN 1..6 LOOP result := result || substr(chars, floor(random() * length(chars))::integer + 1, 1); END LOOP; generated_short_id := result; -- Vérifier si le short_id existe déjà IF NOT EXISTS (SELECT 1 FROM participants WHERE participants.short_id = generated_short_id) THEN RETURN generated_short_id; END IF; -- Éviter les boucles infinies counter := counter + 1; IF counter > 100 THEN RAISE EXCEPTION 'Impossible de générer un short_id unique après 100 tentatives'; END IF; END LOOP; END; $$ LANGUAGE plpgsql; -- ======================================== -- ÉTAPE 3: Mise à jour des données existantes -- ======================================== -- Générer des slugs pour les campagnes qui n'en ont pas UPDATE campaigns SET slug = generate_slug(title) WHERE slug IS NULL; -- Générer des short_ids pour les participants qui n'en ont pas UPDATE participants SET short_id = generate_short_id() WHERE short_id IS NULL; -- ======================================== -- ÉTAPE 4: Création des index manquants -- ======================================== -- Index pour les slugs de campagnes DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_indexes WHERE indexname = 'idx_campaigns_slug' ) THEN CREATE INDEX idx_campaigns_slug ON campaigns(slug); RAISE NOTICE 'Index idx_campaigns_slug créé'; ELSE RAISE NOTICE 'Index idx_campaigns_slug existe déjà'; END IF; END $$; -- Index pour les short_ids de participants DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_indexes WHERE indexname = 'idx_participants_short_id' ) THEN CREATE INDEX idx_participants_short_id ON participants(short_id); RAISE NOTICE 'Index idx_participants_short_id créé'; ELSE RAISE NOTICE 'Index idx_participants_short_id existe déjà'; END IF; END $$; -- ======================================== -- ÉTAPE 5: Fonction pour remplacer les votes -- ======================================== -- Fonction pour remplacer tous les votes d'un participant de manière atomique CREATE OR REPLACE FUNCTION replace_participant_votes( p_campaign_id UUID, p_participant_id UUID, p_votes JSONB ) RETURNS VOID AS $$ DECLARE vote_record RECORD; BEGIN -- Commencer une transaction BEGIN -- Supprimer tous les votes existants pour ce participant dans cette campagne DELETE FROM votes WHERE campaign_id = p_campaign_id AND participant_id = p_participant_id; -- Insérer les nouveaux votes FOR vote_record IN SELECT * FROM jsonb_array_elements(p_votes) LOOP INSERT INTO votes (campaign_id, participant_id, proposition_id, amount) VALUES ( p_campaign_id, p_participant_id, (vote_record.value->>'proposition_id')::UUID, (vote_record.value->>'amount')::INTEGER ); END LOOP; -- La transaction sera automatiquement commitée si tout va bien EXCEPTION WHEN OTHERS THEN -- En cas d'erreur, la transaction sera automatiquement rollbackée RAISE EXCEPTION 'Erreur lors du remplacement des votes: %', SQLERRM; END; END; $$ LANGUAGE plpgsql SECURITY DEFINER; -- ======================================== -- ÉTAPE 6: Vérification et rapport -- ======================================== -- Afficher un rapport de la migration DO $$ DECLARE campaign_count INTEGER; participant_count INTEGER; campaign_with_slug INTEGER; participant_with_short_id INTEGER; BEGIN -- Compter les campagnes SELECT COUNT(*) INTO campaign_count FROM campaigns; SELECT COUNT(*) INTO campaign_with_slug FROM campaigns WHERE slug IS NOT NULL; -- Compter les participants SELECT COUNT(*) INTO participant_count FROM participants; SELECT COUNT(*) INTO participant_with_short_id FROM participants WHERE short_id IS NOT NULL; RAISE NOTICE '=== RAPPORT DE MIGRATION ==='; RAISE NOTICE 'Campagnes totales: %', campaign_count; RAISE NOTICE 'Campagnes avec slug: %', campaign_with_slug; RAISE NOTICE 'Participants totaux: %', participant_count; RAISE NOTICE 'Participants avec short_id: %', participant_with_short_id; IF campaign_count = campaign_with_slug AND participant_count = participant_with_short_id THEN RAISE NOTICE '✅ Migration réussie ! Toutes les données ont été migrées.'; ELSE RAISE NOTICE '⚠️ Attention: Certaines données n''ont pas été migrées.'; END IF; END $$;