diff --git a/server/db.ts b/server/db.ts index 5abfd95..9675631 100644 --- a/server/db.ts +++ b/server/db.ts @@ -563,6 +563,16 @@ export async function createUser(data: InsertUser) { data.password = await bcrypt.hash(data.password, 10); } + // Si le rôle est formateur et qu'il n'y a pas de formateurId, créer une entrée dans la table formateurs + if (data.role === 'formateur' && !data.formateurId && data.name && data.email) { + const formateurResult = await db.insert(formateurs).values({ + nom: data.name, + email: data.email, + }); + // Récupérer l'ID du formateur créé + data.formateurId = Number(formateurResult[0].insertId); + } + const result = await db.insert(users).values(data); return result; } @@ -592,6 +602,36 @@ export async function updateUser(id: number, data: Partial) { data.password = await bcrypt.hash(data.password, 10); } + // Récupérer l'utilisateur actuel + const currentUser = await getUserById(id); + if (!currentUser) throw new Error("User not found"); + + // Si le rôle change vers formateur et qu'il n'y a pas de formateurId + if (data.role === 'formateur' && !currentUser.formateurId && !data.formateurId) { + const name = data.name || currentUser.name; + const email = data.email || currentUser.email; + if (name && email) { + const formateurResult = await db.insert(formateurs).values({ + nom: name, + email: email, + }); + data.formateurId = Number(formateurResult[0].insertId); + } + } + + // Si l'utilisateur est formateur et a un formateurId, synchroniser les modifications + if (currentUser.formateurId && (data.name || data.email)) { + const formateurUpdate: any = {}; + if (data.name) formateurUpdate.nom = data.name; + if (data.email) formateurUpdate.email = data.email; + + if (Object.keys(formateurUpdate).length > 0) { + await db.update(formateurs) + .set(formateurUpdate) + .where(eq(formateurs.id, currentUser.formateurId)); + } + } + await db.update(users).set(data).where(eq(users.id, id)); } diff --git a/server/migrate-formateurs.mjs b/server/migrate-formateurs.mjs new file mode 100644 index 0000000..69a0316 --- /dev/null +++ b/server/migrate-formateurs.mjs @@ -0,0 +1,91 @@ +import { drizzle } from "drizzle-orm/mysql2"; +import { eq } from "drizzle-orm"; +import { users, formateurs } from "../drizzle/schema.ts"; +import * as dotenv from "dotenv"; + +// Charger les variables d'environnement +dotenv.config(); + +/** + * Script de migration pour synchroniser les utilisateurs avec role='formateur' + * qui n'ont pas encore d'entrée dans la table formateurs + */ +async function migrateFormateurs() { + console.log("[Migration] Démarrage de la synchronisation des formateurs..."); + + if (!process.env.DATABASE_URL) { + console.error("[Migration] DATABASE_URL non définie"); + process.exit(1); + } + + const db = drizzle(process.env.DATABASE_URL); + + try { + // Récupérer tous les utilisateurs avec role='formateur' + const formateurUsers = await db + .select() + .from(users) + .where(eq(users.role, 'formateur')); + + console.log(`[Migration] ${formateurUsers.length} utilisateur(s) avec role='formateur' trouvé(s)`); + + let created = 0; + let updated = 0; + let skipped = 0; + + for (const user of formateurUsers) { + // Si l'utilisateur a déjà un formateurId, vérifier qu'il existe + if (user.formateurId) { + const existingFormateur = await db + .select() + .from(formateurs) + .where(eq(formateurs.id, user.formateurId)) + .limit(1); + + if (existingFormateur.length > 0) { + console.log(`[Migration] ✓ Utilisateur "${user.name}" (${user.email}) déjà lié au formateur ID ${user.formateurId}`); + skipped++; + continue; + } + } + + // Vérifier si l'utilisateur a un nom et un email + if (!user.name || !user.email) { + console.log(`[Migration] ⚠ Utilisateur ID ${user.id} ignoré (nom ou email manquant)`); + skipped++; + continue; + } + + // Créer une entrée dans la table formateurs + const formateurResult = await db.insert(formateurs).values({ + nom: user.name, + email: user.email, + }); + + const newFormateurId = Number(formateurResult[0].insertId); + + // Mettre à jour l'utilisateur avec le formateurId + await db + .update(users) + .set({ formateurId: newFormateurId }) + .where(eq(users.id, user.id)); + + console.log(`[Migration] ✓ Créé formateur "${user.name}" (${user.email}) avec ID ${newFormateurId}`); + created++; + } + + console.log("\n[Migration] Résumé:"); + console.log(` - Créés: ${created}`); + console.log(` - Mis à jour: ${updated}`); + console.log(` - Ignorés: ${skipped}`); + console.log("[Migration] Terminé avec succès !"); + + } catch (error) { + console.error("[Migration] Erreur:", error); + process.exit(1); + } + + process.exit(0); +} + +migrateFormateurs(); diff --git a/todo.md b/todo.md index 2070dc2..54a684c 100644 --- a/todo.md +++ b/todo.md @@ -1205,17 +1205,26 @@ - [x] Identifier les procédures backend concernées (exports PDF/Excel, feuille de présence) - [x] Modifier les restrictions pour autoriser les formateurs -- [ ] Tester les modifications -- [ ] Déployer sur le VPS +- [x] Tester les modifications +- [x] Déployer sur le VPS ## Corriger l'accès des formateurs à la liste des inscrits - [x] Identifier les procédures concernées (listBySequence, getWithDates) - [x] Remplacer adminProcedure par protectedProcedure -- [ ] Déployer sur le VPS +- [x] Déployer sur le VPS ## Masquer le menu "Gestion technique" pour les formateurs - [x] Identifier le composant DashboardLayout où le menu est défini - [x] Ajouter une condition isAdmin pour masquer la section Gestion technique -- [ ] Tester et déployer sur le VPS +- [x] Tester et déployer sur le VPS + +## Synchronisation entre table users (rôle formateur) et table formateurs + +- [x] Analyser la structure des deux tables +- [x] Comprendre la relation entre users.role='formateur' et la table formateurs +- [x] Proposer une solution pour synchroniser ou clarifier l'usage +- [x] Implémenter la solution (auto-création dans createUser et updateUser) +- [x] Créer un script de migration pour les formateurs existants +- [ ] Déployer sur le VPS