fix: corriger le flux de réinitialisation par e-mail
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 2m22s

This commit is contained in:
manus-admin
2026-09-02 11:30:37 +02:00
parent 166729bdbc
commit 2cac56b8c6
8 changed files with 247 additions and 58 deletions

View File

@@ -23,6 +23,8 @@ import {
Apprenant,
Formation,
EmailTemplate,
EmailTemplateType,
isEmailTemplateType,
InsertEmailTemplate,
formateurs,
InsertFormateur,
@@ -451,7 +453,14 @@ export async function updateApprenant(id: number, data: Partial<InsertApprenant>
const db = await getDb();
if (!db) throw new Error("Database not available");
await db.update(apprenants).set(data).where(eq(apprenants.id, id));
try {
await db.update(apprenants).set(data).where(eq(apprenants.id, id));
} catch (error: any) {
if (error?.code === 'ER_DUP_ENTRY' || error?.message?.includes('Duplicate entry')) {
throw new Error("Cet email est déjà utilisé par un autre apprenant.");
}
throw error;
}
}
export async function deleteApprenant(id: number) {
@@ -676,22 +685,14 @@ export async function deleteUser(id: number) {
// ==================== GESTION DES TOKENS DE RÉINITIALISATION ====================
export async function createPasswordResetToken(email: string, token: string) {
export async function createPasswordResetToken(userId: number, token: string) {
const db = await getDb();
if (!db) throw new Error("Database not available");
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24); // Token valide 24h
// Un Date est converti par Drizzle avec le fuseau du serveur sans manipulation de chaîne.
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);
// Convertir la date au format MySQL DATETIME (YYYY-MM-DD HH:MM:SS)
const mysqlDate = expiresAt.toISOString().slice(0, 19).replace('T', ' ');
// Utiliser une requête SQL brute pour éviter les problèmes avec Drizzle
const result = await db.execute(sql`
INSERT INTO passwordResetTokens (email, token, expiresAt)
VALUES (${email}, ${token}, ${mysqlDate})
`);
return result;
return db.insert(passwordResetTokens).values({ userId, token, expiresAt });
}
export async function getPasswordResetToken(token: string) {
@@ -744,6 +745,10 @@ export async function getAllEmailTemplates() {
export async function getEmailTemplateByType(type: string) {
const db = await getDb();
if (!db) return null;
// Les services de notification peuvent demander un template non persistant.
// Dans ce cas, le générateur retombe volontairement sur son HTML de secours.
if (!isEmailTemplateType(type)) return null;
const results = await db.select().from(emailTemplates).where(eq(emailTemplates.type, type)).limit(1);
return results.length > 0 ? results[0] : null;
@@ -785,6 +790,10 @@ export async function upsertEmailTemplate(template: InsertEmailTemplate) {
export async function deleteEmailTemplate(type: string) {
const db = await getDb();
if (!db) throw new Error("Database not available");
if (!isEmailTemplateType(type)) {
throw new Error("Type de template email invalide");
}
await db.delete(emailTemplates).where(eq(emailTemplates.type, type));
}
@@ -958,7 +967,14 @@ export async function initializeDefaultEmailTemplates() {
{
type: "reset_password",
titre: "Réinitialisation de mot de passe",
bodyContent: "<p>Contenu par défaut</p>",
// Le lien doit rester une variable : chaque demande génère un token différent.
bodyContent: `<p>Bonjour {{prenomApprenant}} {{nomApprenant}},</p>
<p>Vous avez demandé la réinitialisation de votre mot de passe pour votre compte Manager Itinova.</p>
<p>Cliquez sur le bouton ci-dessous pour choisir un nouveau mot de passe :</p>
<p style="text-align: center;"><a href="{{lienReinitialisation}}" class="button">Réinitialiser mon mot de passe</a></p>
<p>Si le bouton ne fonctionne pas, copiez ce lien dans votre navigateur :</p>
<p style="word-break: break-all;">{{lienReinitialisation}}</p>
<p>Ce lien est valide pendant 24 heures et ne peut être utilisé quune seule fois.</p>`,
couleurPrincipale: "#283581",
couleurSecondaire: "#0578BE",
piedDePage: "Cet email a été envoyé automatiquement par le système de gestion des formations Itinova. Pour toute question, veuillez contacter le service RH.",