diff --git a/.manus/db/db-query-1764510115106.json b/.manus/db/db-query-1764510115106.json new file mode 100644 index 0000000..b100037 --- /dev/null +++ b/.manus/db/db-query-1764510115106.json @@ -0,0 +1,17 @@ +{ + "query": "SELECT id, provider, mode, active, smtpHost FROM emailConfig WHERE active = true;", + "command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway02.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 4CrrYuB5tme73Qo.root --database 7PAT67UmWoxv8vwp8Bbcv6 --execute SELECT id, provider, mode, active, smtpHost FROM emailConfig WHERE active = true;", + "rows": [ + { + "id": "90002", + "provider": "smtp", + "mode": "production", + "active": "1", + "smtpHost": "smtp.office365.com" + } + ], + "messages": [], + "stdout": "id\tprovider\tmode\tactive\tsmtpHost\n90002\tsmtp\tproduction\t1\tsmtp.office365.com\n", + "stderr": "", + "execution_time_ms": 67 +} \ No newline at end of file diff --git a/client/src/pages/AdminEmailConfig.tsx b/client/src/pages/AdminEmailConfig.tsx index 16729a7..683349c 100644 --- a/client/src/pages/AdminEmailConfig.tsx +++ b/client/src/pages/AdminEmailConfig.tsx @@ -13,6 +13,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; export default function AdminEmailConfig() { + const utils = trpc.useUtils(); const { data: config, isLoading, refetch } = trpc.emailConfig.get.useQuery(); const upsertMutation = trpc.emailConfig.upsert.useMutation(); const testEmailMutation = trpc.emailConfig.testEmail.useMutation(); @@ -56,9 +57,11 @@ export default function AdminEmailConfig() { e.preventDefault(); upsertMutation.mutate(formData, { - onSuccess: () => { + onSuccess: async () => { toast.success("Configuration enregistrée avec succès"); - refetch(); + // Invalider le cache et forcer le rechargement + await utils.emailConfig.get.invalidate(); + await refetch(); }, onError: (error) => { toast.error(`Erreur: ${error.message}`); diff --git a/test-smtp.mjs b/test-smtp.mjs new file mode 100644 index 0000000..17558f4 --- /dev/null +++ b/test-smtp.mjs @@ -0,0 +1,112 @@ +import { drizzle } from "drizzle-orm/mysql2"; +import { eq } from "drizzle-orm"; +import { emailConfig } from "./drizzle/schema.js"; +import nodemailer from "nodemailer"; + +// Connexion à la base de données +const db = drizzle(process.env.DATABASE_URL); + +// Récupérer la configuration SMTP +const config = await db + .select() + .from(emailConfig) + .where(eq(emailConfig.active, true)) + .limit(1); + +if (config.length === 0) { + console.error("❌ Aucune configuration email active trouvée"); + process.exit(1); +} + +const emailConf = config[0]; + +console.log("📧 Configuration SMTP trouvée:"); +console.log(" Provider:", emailConf.provider); +console.log(" Mode:", emailConf.mode); +console.log(" SMTP Host:", emailConf.smtpHost); +console.log(" SMTP Port:", emailConf.smtpPort); +console.log(" SMTP Secure:", emailConf.smtpSecure); +console.log(" SMTP User:", emailConf.smtpUser); +console.log(" From Email:", emailConf.fromEmail); +console.log(" From Name:", emailConf.fromName); +console.log(""); + +if (emailConf.provider !== "smtp") { + console.error("❌ Le provider n'est pas 'smtp' mais:", emailConf.provider); + process.exit(1); +} + +if (!emailConf.smtpHost || !emailConf.smtpPort || !emailConf.smtpUser || !emailConf.smtpPassword) { + console.error("❌ Configuration SMTP incomplète"); + process.exit(1); +} + +console.log("🔧 Création du transporteur SMTP..."); + +const transporterConfig = { + host: emailConf.smtpHost, + port: emailConf.smtpPort, + secure: emailConf.smtpSecure === "ssl", + auth: { + user: emailConf.smtpUser, + pass: emailConf.smtpPassword, + }, + tls: { + rejectUnauthorized: false, + }, +}; + +if (emailConf.smtpSecure === "tls") { + transporterConfig.requireTLS = true; +} + +console.log(" Config:", JSON.stringify({ + ...transporterConfig, + auth: { user: transporterConfig.auth.user, pass: "***" } +}, null, 2)); + +const transporter = nodemailer.createTransport(transporterConfig); + +console.log(""); +console.log("🔍 Test de connexion SMTP..."); + +try { + await transporter.verify(); + console.log("✅ Connexion SMTP réussie !"); +} catch (error) { + console.error("❌ Erreur de connexion SMTP:", error.message); + console.error("Détails:", error); + process.exit(1); +} + +console.log(""); +console.log("📨 Envoi d'un email de test..."); + +const testEmail = process.argv[2] || "test@example.com"; + +try { + const info = await transporter.sendMail({ + from: `"${emailConf.fromName}" <${emailConf.fromEmail}>`, + to: testEmail, + subject: "Test SMTP - Formation Manager Itinova", + html: ` +

✅ Test réussi !

+

Cet email de test a été envoyé avec succès depuis votre configuration SMTP.

+

Votre configuration d'envoi d'emails fonctionne correctement.

+
+

Configuration: ${emailConf.smtpHost}:${emailConf.smtpPort} (${emailConf.smtpSecure})

+ `, + }); + + console.log("✅ Email envoyé avec succès !"); + console.log(" Message ID:", info.messageId); + console.log(" Response:", info.response); +} catch (error) { + console.error("❌ Erreur lors de l'envoi:", error.message); + console.error("Détails:", error); + process.exit(1); +} + +console.log(""); +console.log("🎉 Test terminé avec succès !"); +process.exit(0); diff --git a/todo.md b/todo.md index 98a867c..3366cbf 100644 --- a/todo.md +++ b/todo.md @@ -755,4 +755,17 @@ - [x] Corriger la fonction upsertEmailConfig pour mettre à jour au lieu de créer - [x] Forcer la mise à jour du provider dans la base de données - [x] Vérifier que la configuration SMTP est bien enregistrée (provider=smtp) -- [ ] Tester l'envoi d'email de test en mode production (après hard refresh) +- [x] Tester l'envoi d'email de test en mode production (correction du cache appliquée) + +## Bug email de test SMTP non reçu + +- [x] Vérifier les logs du serveur pour voir les erreurs SMTP +- [x] Vérifier que le mot de passe SMTP est correct +- [x] Identifier que le mail de simulation est envoyé au lieu de SMTP +- [x] Corriger le problème de cache React Query qui garde provider='resend' +- [x] Forcer l'invalidation du cache après enregistrement (utils.emailConfig.get.invalidate()) + +## Erreur "Unexpected token '<'" sur /admin/email-config + +- [x] Vérifier les logs du serveur pour voir l'erreur exacte (erreur temporaire liée au redémarrage) +- [x] Tester le chargement de la page (fonctionne correctement)