/** * Script de nettoyage des calendriers de groupes orphelins * * Ce script supprime les calendriers de groupes dont le groupe n'existe plus dans Keycloak. * * Usage: * npx tsx scripts/clean-orphan-calendars.ts */ import { prisma } from '../lib/prisma'; async function getAdminToken() { try { const tokenResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'client_credentials', client_id: process.env.KEYCLOAK_CLIENT_ID!, client_secret: process.env.KEYCLOAK_CLIENT_SECRET!, }), } ); const data = await tokenResponse.json(); if (!tokenResponse.ok || !data.access_token) { console.error('❌ Erreur token:', data); return null; } return data.access_token; } catch (error) { console.error('❌ Erreur token:', error); return null; } } async function getAllKeycloakGroups(token: string): Promise> { try { const response = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/groups`, { headers: { Authorization: `Bearer ${token}`, }, } ); if (!response.ok) { throw new Error('Failed to fetch groups from Keycloak'); } const groups = await response.json(); const groupNames = new Set(); groups.forEach((group: any) => { groupNames.add(group.name); }); return groupNames; } catch (error) { console.error('❌ Erreur lors de la récupération des groupes:', error); return new Set(); } } async function cleanOrphanCalendars() { console.log('🧹 Début du nettoyage des calendriers orphelins...\n'); // 1. Récupérer le token Keycloak const token = await getAdminToken(); if (!token) { console.error('❌ Impossible d\'obtenir le token Keycloak'); process.exit(1); } // 2. Récupérer tous les groupes existants dans Keycloak console.log('📋 Récupération des groupes depuis Keycloak...'); const keycloakGroups = await getAllKeycloakGroups(token); console.log(`✅ ${keycloakGroups.size} groupes trouvés dans Keycloak\n`); // 3. Récupérer tous les calendriers de groupes console.log('📋 Récupération des calendriers de groupes depuis la base...'); const groupCalendars = await prisma.calendar.findMany({ where: { name: { startsWith: 'Groupe: ', }, }, }); console.log(`✅ ${groupCalendars.length} calendriers de groupes trouvés\n`); // 4. Identifier les calendriers orphelins const orphanCalendars = groupCalendars.filter((calendar) => { const groupName = calendar.name.replace('Groupe: ', ''); return !keycloakGroups.has(groupName); }); if (orphanCalendars.length === 0) { console.log('✅ Aucun calendrier orphelin trouvé. Base de données propre!\n'); return; } console.log(`⚠️ ${orphanCalendars.length} calendrier(s) orphelin(s) trouvé(s):\n`); orphanCalendars.forEach((cal) => { console.log(` - ${cal.name} (ID: ${cal.id})`); }); // 5. Demander confirmation (commentez cette section si vous voulez une suppression automatique) console.log('\n⚠️ ATTENTION: Les calendriers ci-dessus vont être supprimés avec tous leurs événements!'); console.log('Pour confirmer, lancez le script avec: npx tsx scripts/clean-orphan-calendars.ts --confirm\n'); const isConfirmed = process.argv.includes('--confirm'); if (!isConfirmed) { console.log('❌ Suppression annulée. Utilisez --confirm pour supprimer.'); return; } // 6. Supprimer les calendriers orphelins console.log('\n🗑️ Suppression des calendriers orphelins...\n'); for (const calendar of orphanCalendars) { try { await prisma.calendar.delete({ where: { id: calendar.id }, }); console.log(` ✅ Supprimé: ${calendar.name}`); } catch (error) { console.error(` ❌ Erreur lors de la suppression de ${calendar.name}:`, error); } } console.log(`\n✅ Nettoyage terminé! ${orphanCalendars.length} calendrier(s) supprimé(s).\n`); } // Exécuter le script cleanOrphanCalendars() .then(() => { process.exit(0); }) .catch((error) => { console.error('❌ Erreur fatale:', error); process.exit(1); });