From f06cd3e8a5c33f4d3f9e891bee84461b042adc70 Mon Sep 17 00:00:00 2001 From: Manus Sandbox Date: Mon, 17 Nov 2025 16:53:32 -0500 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Am=C3=A9lioration=20du=20syst?= =?UTF-8?q?=C3=A8me=20d'envoi=20d'emails=20:=20int=C3=A9gration=20de=20Res?= =?UTF-8?q?end=20pour=20l'envoi=20r=C3=A9el=20d'emails=20avec=20fallback?= =?UTF-8?q?=20vers=20simulation=20si=20non=20configur=C3=A9.=20Documentati?= =?UTF-8?q?on=20compl=C3=A8te=20ajout=C3=A9e=20dans=20EMAIL=5FSETUP.md=20p?= =?UTF-8?q?our=20guider=20la=20configuration.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- EMAIL_SETUP.md | 109 +++++++++++++++++++++++++++ server/_core/emailSender.ts | 145 ++++++++++++++++++++++++++++++++++++ server/emailService.ts | 33 +------- todo.md | 7 ++ 4 files changed, 265 insertions(+), 29 deletions(-) create mode 100644 EMAIL_SETUP.md create mode 100644 server/_core/emailSender.ts diff --git a/EMAIL_SETUP.md b/EMAIL_SETUP.md new file mode 100644 index 0000000..bc8a0ef --- /dev/null +++ b/EMAIL_SETUP.md @@ -0,0 +1,109 @@ +# Configuration de l'envoi d'emails + +## État actuel + +Par défaut, l'application **simule** l'envoi d'emails en créant des notifications pour le propriétaire du projet. Les emails ne sont pas réellement envoyés aux utilisateurs finaux. + +## Configuration de l'envoi réel d'emails + +Pour activer l'envoi réel d'emails, vous devez configurer un service d'envoi d'emails externe. L'application supporte **Resend**, un service moderne et simple d'utilisation. + +### Étape 1 : Créer un compte Resend + +1. Allez sur [https://resend.com](https://resend.com) +2. Créez un compte gratuit (100 emails/jour inclus) +3. Vérifiez votre adresse email + +### Étape 2 : Obtenir une clé API + +1. Dans le tableau de bord Resend, allez dans **API Keys** +2. Cliquez sur **Create API Key** +3. Donnez un nom à votre clé (ex: "Manager Itinova Production") +4. Copiez la clé API générée (elle commence par `re_`) + +### Étape 3 : Configurer votre domaine (optionnel mais recommandé) + +Pour envoyer des emails depuis votre propre domaine (ex: `noreply@itinova.org`) : + +1. Dans Resend, allez dans **Domains** +2. Cliquez sur **Add Domain** +3. Entrez votre domaine (ex: `itinova.org`) +4. Suivez les instructions pour ajouter les enregistrements DNS +5. Attendez la vérification (quelques minutes à quelques heures) + +**Note:** Sans domaine vérifié, les emails seront envoyés depuis `noreply@manusvm.computer` + +### Étape 4 : Ajouter les secrets dans Manus + +1. Ouvrez l'interface de gestion de votre projet Manus +2. Allez dans **Settings** → **Secrets** +3. Ajoutez les secrets suivants : + +| Nom du secret | Valeur | Description | +|---------------|--------|-------------| +| `RESEND_API_KEY` | `re_xxxxx...` | Votre clé API Resend | +| `RESEND_FROM_EMAIL` | `noreply@itinova.org` | Adresse email d'envoi (doit être vérifiée dans Resend) | + +4. Sauvegardez les modifications +5. Redémarrez l'application (elle détectera automatiquement la nouvelle configuration) + +### Étape 5 : Tester l'envoi + +1. Allez dans **Utilisateurs** +2. Cliquez sur le bouton de réinitialisation de mot de passe pour un utilisateur +3. Vérifiez que l'email a bien été reçu + +## Vérification de la configuration + +Vous pouvez vérifier si l'envoi d'emails réel est activé en consultant les logs du serveur : + +- **Emails simulés** : `[Email] RESEND_API_KEY non configurée, email simulé` +- **Emails réels** : `[Email] Email envoyé via Resend: ` + +## Alternatives à Resend + +Si vous préférez utiliser un autre service d'envoi d'emails, vous pouvez modifier le fichier `server/_core/emailSender.ts` pour intégrer : + +- **SendGrid** : Service populaire avec 100 emails/jour gratuits +- **Mailgun** : Service robuste avec 5000 emails/mois gratuits +- **AWS SES** : Service Amazon très économique pour gros volumes +- **Votre propre serveur SMTP** : Configuration personnalisée + +## Limites et quotas + +### Plan gratuit Resend +- 100 emails par jour +- 3000 emails par mois +- Support email uniquement + +### Plans payants Resend +- À partir de 20$/mois pour 50 000 emails +- Support prioritaire +- Webhooks et analytics avancés + +## Dépannage + +### Les emails n'arrivent pas + +1. Vérifiez que `RESEND_API_KEY` est correctement configurée +2. Vérifiez que `RESEND_FROM_EMAIL` utilise un domaine vérifié +3. Consultez les logs du serveur pour voir les erreurs +4. Vérifiez le dossier spam/courrier indésirable +5. Consultez le tableau de bord Resend pour voir l'état des envois + +### Erreur "Domain not verified" + +Vous devez vérifier votre domaine dans Resend en ajoutant les enregistrements DNS requis. + +### Quota dépassé + +Si vous dépassez le quota gratuit, vous devrez : +- Attendre le lendemain (quota journalier) +- Passer à un plan payant +- Utiliser un autre service d'envoi + +## Support + +Pour toute question sur la configuration de l'envoi d'emails, consultez : +- [Documentation Resend](https://resend.com/docs) +- [API Reference Resend](https://resend.com/docs/api-reference) diff --git a/server/_core/emailSender.ts b/server/_core/emailSender.ts new file mode 100644 index 0000000..7e14d9c --- /dev/null +++ b/server/_core/emailSender.ts @@ -0,0 +1,145 @@ +/** + * Service d'envoi d'emails réels via Resend API + * + * Configuration requise: + * - RESEND_API_KEY: Clé API Resend (obtenir sur https://resend.com) + * - RESEND_FROM_EMAIL: Adresse email d'envoi (doit être vérifiée dans Resend) + * + * Si ces variables ne sont pas configurées, les emails seront simulés + * et envoyés comme notifications au propriétaire du projet. + */ + +import { ENV } from "./env"; +import { notifyOwner } from "./notification"; + +interface EmailParams { + to: string; + subject: string; + html: string; + attachments?: Array<{ + filename: string; + content: string; + contentType: string; + }>; +} + +interface ResendEmailRequest { + from: string; + to: string[]; + subject: string; + html: string; + attachments?: Array<{ + filename: string; + content: string; + }>; +} + +/** + * Envoie un email via Resend API + */ +async function sendViaResend(params: EmailParams): Promise { + const resendApiKey = process.env.RESEND_API_KEY; + const fromEmail = process.env.RESEND_FROM_EMAIL || 'noreply@manusvm.computer'; + + if (!resendApiKey) { + console.warn('[Email] RESEND_API_KEY non configurée, email simulé'); + return false; + } + + try { + const payload: ResendEmailRequest = { + from: fromEmail, + to: [params.to], + subject: params.subject, + html: params.html, + }; + + // Ajouter les pièces jointes si présentes + if (params.attachments && params.attachments.length > 0) { + payload.attachments = params.attachments.map(att => ({ + filename: att.filename, + content: att.content, // Resend accepte base64 ou string + })); + } + + const response = await fetch('https://api.resend.com/emails', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${resendApiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + const error = await response.text(); + console.error('[Email] Erreur Resend:', response.status, error); + return false; + } + + const result = await response.json(); + console.log('[Email] Email envoyé via Resend:', result.id); + return true; + } catch (error) { + console.error('[Email] Exception lors de l\'envoi via Resend:', error); + return false; + } +} + +/** + * Simule l'envoi d'un email en créant une notification pour le propriétaire + */ +async function simulateEmail(params: EmailParams): Promise { + console.log('=== EMAIL SIMULÉ ==='); + console.log('To:', params.to); + console.log('Subject:', params.subject); + + const emailContent = ` +Destinataire: ${params.to} +Sujet: ${params.subject} + +${params.html.replace(/<[^>]*>/g, '').substring(0, 500)}... + +${params.attachments ? `Pièces jointes: ${params.attachments.map(a => a.filename).join(', ')}` : ''} + +⚠️ Cet email est simulé. Pour envoyer de vrais emails: +1. Créez un compte sur https://resend.com +2. Ajoutez RESEND_API_KEY dans les secrets du projet +3. Ajoutez RESEND_FROM_EMAIL (ex: noreply@votredomaine.com)`; + + try { + await notifyOwner({ + title: `📧 Email simulé: ${params.subject}`, + content: emailContent, + }); + console.log('✓ Email simulé (notification envoyée au propriétaire)'); + } catch (error) { + console.error('Erreur lors de la simulation d\'email:', error); + } + + console.log('==================='); + + return true; +} + +/** + * Envoie un email (réel ou simulé selon la configuration) + */ +export async function sendEmail(params: EmailParams): Promise { + // Tenter d'envoyer via Resend + const sent = await sendViaResend(params); + + // Si l'envoi échoue (pas de clé API ou erreur), simuler + if (!sent) { + return await simulateEmail(params); + } + + return true; +} + +/** + * Vérifie si le service d'envoi d'emails réel est configuré + */ +export function isEmailServiceConfigured(): boolean { + return !!process.env.RESEND_API_KEY; +} diff --git a/server/emailService.ts b/server/emailService.ts index f559cfa..7adb488 100644 --- a/server/emailService.ts +++ b/server/emailService.ts @@ -4,7 +4,7 @@ */ import { generateFormationICS } from "./icsGenerator"; -import { notifyOwner } from "./_core/notification"; +import { sendEmail as sendEmailViaService, isEmailServiceConfigured } from "./_core/emailSender"; interface EmailParams { to: string; @@ -18,36 +18,11 @@ interface EmailParams { } /** - * Envoie un email en utilisant le système de notification Manus - * Note: Pour un envoi SMTP réel, intégrer un service comme SendGrid ou Mailgun + * Envoie un email via le service d'envoi configuré + * Utilise Resend si RESEND_API_KEY est configuré, sinon simule l'envoi */ async function sendEmail(params: EmailParams): Promise { - console.log('=== EMAIL ENVOYÉ ==='); - console.log('To:', params.to); - console.log('Subject:', params.subject); - - // Envoyer une notification au propriétaire du projet - const emailContent = ` -Destinataire: ${params.to} -Sujet: ${params.subject} - -${params.html.replace(/<[^>]*>/g, '').substring(0, 500)}... - -${params.attachments ? `Pièces jointes: ${params.attachments.map(a => a.filename).join(', ')}` : ''}`; - - try { - await notifyOwner({ - title: `Email envoyé: ${params.subject}`, - content: emailContent, - }); - console.log('✓ Email traité avec succès'); - } catch (error) { - console.error('Erreur lors de l\'envoi de la notification:', error); - } - - console.log('==================='); - - return true; + return await sendEmailViaService(params); } /** diff --git a/todo.md b/todo.md index d319566..6a4c63f 100644 --- a/todo.md +++ b/todo.md @@ -209,3 +209,10 @@ - [x] Intégrer la page AdminUsers dans le DashboardLayout - [x] Vérifier l'affichage du menu latéral sur la page utilisateurs + +## Correction de l'envoi d'emails pour réinitialisation de mot de passe + +- [x] Analyser le service d'envoi d'emails actuel +- [x] Rechercher l'API d'envoi d'emails disponible dans Manus +- [x] Intégrer un vrai service d'envoi d'emails (Resend) +- [x] Créer la documentation de configuration EMAIL_SETUP.md