Checkpoint: Amélioration du système d'envoi d'emails : intégration de Resend pour l'envoi réel d'emails avec fallback vers simulation si non configuré. Documentation complète ajoutée dans EMAIL_SETUP.md pour guider la configuration.

This commit is contained in:
Manus Sandbox
2025-11-17 16:53:32 -05:00
parent 6c24939d20
commit f06cd3e8a5
4 changed files with 265 additions and 29 deletions

109
EMAIL_SETUP.md Normal file
View File

@@ -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: <id>`
## 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)

145
server/_core/emailSender.ts Normal file
View File

@@ -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<boolean> {
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<boolean> {
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<boolean> {
// 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;
}

View File

@@ -4,7 +4,7 @@
*/ */
import { generateFormationICS } from "./icsGenerator"; import { generateFormationICS } from "./icsGenerator";
import { notifyOwner } from "./_core/notification"; import { sendEmail as sendEmailViaService, isEmailServiceConfigured } from "./_core/emailSender";
interface EmailParams { interface EmailParams {
to: string; to: string;
@@ -18,36 +18,11 @@ interface EmailParams {
} }
/** /**
* Envoie un email en utilisant le système de notification Manus * Envoie un email via le service d'envoi configuré
* Note: Pour un envoi SMTP réel, intégrer un service comme SendGrid ou Mailgun * Utilise Resend si RESEND_API_KEY est configuré, sinon simule l'envoi
*/ */
async function sendEmail(params: EmailParams): Promise<boolean> { async function sendEmail(params: EmailParams): Promise<boolean> {
console.log('=== EMAIL ENVOYÉ ==='); return await sendEmailViaService(params);
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;
} }
/** /**

View File

@@ -209,3 +209,10 @@
- [x] Intégrer la page AdminUsers dans le DashboardLayout - [x] Intégrer la page AdminUsers dans le DashboardLayout
- [x] Vérifier l'affichage du menu latéral sur la page utilisateurs - [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