- Add slug/short_id fields to database with auto-generation
- Create migration script for existing data - Update admin interface to show only short URLs - Implement redirect system to avoid code duplication - Maintain backward compatibility with old URLs
This commit is contained in:
196
scripts/migrate-short-links.js
Normal file
196
scripts/migrate-short-links.js
Normal file
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Script de migration pour générer les slugs et short_ids pour les données existantes
|
||||
*
|
||||
* Usage: node scripts/migrate-short-links.js
|
||||
*/
|
||||
|
||||
const { createClient } = require('@supabase/supabase-js');
|
||||
require('dotenv').config({ path: '.env.local' });
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
||||
|
||||
if (!supabaseUrl || !supabaseServiceKey) {
|
||||
console.error('❌ Variables d\'environnement manquantes');
|
||||
console.error('NEXT_PUBLIC_SUPABASE_URL et SUPABASE_SERVICE_ROLE_KEY sont requis');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
||||
|
||||
async function generateSlug(title) {
|
||||
// Convertir en minuscules et remplacer les caractères spéciaux
|
||||
let slug = title.toLowerCase()
|
||||
.replace(/[^a-z0-9\s]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.trim();
|
||||
|
||||
// Si le slug est vide, utiliser 'campagne'
|
||||
if (!slug) {
|
||||
slug = 'campagne';
|
||||
}
|
||||
|
||||
// Vérifier si le slug existe déjà et ajouter un numéro si nécessaire
|
||||
let counter = 0;
|
||||
let finalSlug = slug;
|
||||
|
||||
while (true) {
|
||||
const { data, error } = await supabase
|
||||
.from('campaigns')
|
||||
.select('id')
|
||||
.eq('slug', finalSlug)
|
||||
.single();
|
||||
|
||||
if (error && error.code === 'PGRST116') {
|
||||
// Aucune campagne trouvée avec ce slug, on peut l'utiliser
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Le slug existe déjà, ajouter un numéro
|
||||
counter++;
|
||||
finalSlug = `${slug}-${counter}`;
|
||||
}
|
||||
|
||||
return finalSlug;
|
||||
}
|
||||
|
||||
async function generateShortId() {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let counter = 0;
|
||||
|
||||
while (counter < 100) {
|
||||
// Générer un identifiant de 6 caractères
|
||||
let result = '';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
|
||||
// Vérifier si le short_id existe déjà
|
||||
const { data, error } = await supabase
|
||||
.from('participants')
|
||||
.select('id')
|
||||
.eq('short_id', result)
|
||||
.single();
|
||||
|
||||
if (error && error.code === 'PGRST116') {
|
||||
// Aucun participant trouvé avec ce short_id, on peut l'utiliser
|
||||
return result;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
throw new Error('Impossible de générer un short_id unique après 100 tentatives');
|
||||
}
|
||||
|
||||
async function migrateCampaigns() {
|
||||
console.log('🔄 Migration des campagnes...');
|
||||
|
||||
// Récupérer toutes les campagnes sans slug
|
||||
const { data: campaigns, error } = await supabase
|
||||
.from('campaigns')
|
||||
.select('id, title')
|
||||
.is('slug', null);
|
||||
|
||||
if (error) {
|
||||
console.error('❌ Erreur lors de la récupération des campagnes:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`📋 ${campaigns.length} campagnes à migrer`);
|
||||
|
||||
for (const campaign of campaigns) {
|
||||
try {
|
||||
const slug = await generateSlug(campaign.title);
|
||||
|
||||
const { error: updateError } = await supabase
|
||||
.from('campaigns')
|
||||
.update({ slug })
|
||||
.eq('id', campaign.id);
|
||||
|
||||
if (updateError) {
|
||||
console.error(`❌ Erreur lors de la mise à jour de la campagne ${campaign.id}:`, updateError);
|
||||
} else {
|
||||
console.log(`✅ Campagne "${campaign.title}" -> slug: ${slug}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ Erreur lors de la génération du slug pour "${campaign.title}":`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Migration des campagnes terminée');
|
||||
}
|
||||
|
||||
async function migrateParticipants() {
|
||||
console.log('🔄 Migration des participants...');
|
||||
|
||||
// Récupérer tous les participants sans short_id
|
||||
const { data: participants, error } = await supabase
|
||||
.from('participants')
|
||||
.select('id, first_name, last_name')
|
||||
.is('short_id', null);
|
||||
|
||||
if (error) {
|
||||
console.error('❌ Erreur lors de la récupération des participants:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`📋 ${participants.length} participants à migrer`);
|
||||
|
||||
for (const participant of participants) {
|
||||
try {
|
||||
const shortId = await generateShortId();
|
||||
|
||||
const { error: updateError } = await supabase
|
||||
.from('participants')
|
||||
.update({ short_id: shortId })
|
||||
.eq('id', participant.id);
|
||||
|
||||
if (updateError) {
|
||||
console.error(`❌ Erreur lors de la mise à jour du participant ${participant.id}:`, updateError);
|
||||
} else {
|
||||
console.log(`✅ Participant "${participant.first_name} ${participant.last_name}" -> short_id: ${shortId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ Erreur lors de la génération du short_id pour "${participant.first_name} ${participant.last_name}":`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Migration des participants terminée');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🚀 Début de la migration des liens courts...\n');
|
||||
|
||||
try {
|
||||
await migrateCampaigns();
|
||||
console.log('');
|
||||
await migrateParticipants();
|
||||
|
||||
console.log('\n🎉 Migration terminée avec succès !');
|
||||
console.log('\n📝 Résumé des nouvelles routes :');
|
||||
console.log('- Dépôt de propositions : /p/[slug]');
|
||||
console.log('- Vote : /v/[shortId]');
|
||||
console.log('- Les anciennes routes restent fonctionnelles pour la compatibilité');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de la migration:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { migrateCampaigns, migrateParticipants };
|
||||
Reference in New Issue
Block a user