diff --git a/.env.exemple b/.env.exemple new file mode 100644 index 0000000..1c46f7f --- /dev/null +++ b/.env.exemple @@ -0,0 +1,8 @@ +#Configuration Mailchimp +MAILCHIMP_API_KEY= +MAILCHIMP_KEY= +MAILCHIMP_LIST_ID= +APP_HOST= + +#Configuration serveur +PORT= \ No newline at end of file diff --git a/package.json b/package.json index af505e7..fa4ea0f 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "dev": "nodemon src/server.js" }, "dependencies": { + "@mailchimp/mailchimp_transactional": "^1.0.59", "cors": "^2.8.5", + "dotenv": "^17.2.0", "express": "^4.18.2", "node-fetch": "^2.6.7" }, diff --git a/src/server.js b/src/server.js index 957a2a1..2562aaf 100644 --- a/src/server.js +++ b/src/server.js @@ -1,6 +1,8 @@ const express = require('express'); const cors = require('cors'); const fetch = require('node-fetch'); +const mailchimp = require('@mailchimp/mailchimp_transactional'); +require('dotenv').config(); // Initialisation de l'application Express const app = express(); @@ -174,6 +176,230 @@ app.post('/api/v1/idnot/user/:code', async (req, res) => { } }); +//------------------------------------ Email Section ------------------------------------ + + +const config = { + MAILCHIMP_API_KEY: process.env.MAILCHIMP_API_KEY, + MAILCHIMP_KEY: process.env.MAILCHIMP_KEY, + MAILCHIMP_LIST_ID: process.env.MAILCHIMP_LIST_ID, + PORT: process.env.PORT || 8080, + FROM_EMAIL: 'no-reply@lecoffre.io', + FROM_NAME: 'LeCoffre.io' +}; + +// Email storage +const pendingEmails = new Map(); + +// Email service +class EmailService { + static async sendTransactionalEmail(to, templateName, subject, templateVariables) { + try { + const mailchimpClient = mailchimp(config.MAILCHIMP_API_KEY); + + const message = { + template_name: templateName, + template_content: [], + message: { + global_merge_vars: this.buildVariables(templateVariables), + from_email: config.FROM_EMAIL, + from_name: config.FROM_NAME, + subject: subject, + to: [ + { + email: to, + type: 'to' + } + ] + } + }; + + const result = await mailchimpClient.messages.sendTemplate(message); + return { success: true, result }; + } catch (error) { + console.error('Erreur envoi email:', error); + return { success: false, error: 'Échec de l\'envoi de l\'email' }; + } + } + + static buildVariables(templateVariables) { + return Object.keys(templateVariables).map(key => ({ + name: key, + content: templateVariables[key] + })); + } + + // Add to Mailchimp diffusion list + static async addToMailchimpList(email) { + try { + const url = `https://us17.api.mailchimp.com/3.0/lists/${config.MAILCHIMP_LIST_ID}/members`; + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Authorization': `apikey ${config.MAILCHIMP_KEY}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email_address: email, + status: 'subscribed' + }) + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return { success: true, data }; + } catch (error) { + console.error('Erreur ajout à la liste:', error); + return { success: false, error: 'Échec de l\'ajout à la liste Mailchimp' }; + } + } + + static async retryFailedEmails() { + for (const [emailId, emailData] of pendingEmails) { + if (emailData.attempts >= 10) { + pendingEmails.delete(emailId); + continue; + } + + const nextRetryDate = new Date(emailData.lastAttempt); + nextRetryDate.setMinutes(nextRetryDate.getMinutes() + Math.pow(emailData.attempts, 2)); + + if (Date.now() >= nextRetryDate) { + try { + const result = await this.sendTransactionalEmail( + emailData.to, + emailData.templateName, + emailData.subject, + emailData.templateVariables + ); + + if (result.success) { + pendingEmails.delete(emailId); + } else { + emailData.attempts += 1; + emailData.lastAttempt = Date.now(); + } + } catch (error) { + emailData.attempts += 1; + emailData.lastAttempt = Date.now(); + } + } + } + } +} + +// Email validation middleware +const validateEmail = (req, res, next) => { + const { email } = req.body; + + if (!email) { + return res.status(400).json({ + success: false, + message: 'L\'adresse email est requise' + }); + } + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + return res.status(400).json({ + success: false, + message: 'Format d\'email invalide' + }); + } + + next(); +}; + +// Email templates +const ETemplates = { +DOCUMENT_ASKED: "DOCUMENT_ASKED", +DOCUMENT_REFUSED: "DOCUMENT_REFUSED", +DOCUMENT_RECAP: "DOCUMENT_RECAP", +SUBSCRIPTION_INVITATION: "SUBSCRIPTION_INVITATION", +DOCUMENT_REMINDER: "DOCUMENT_REMINDER", +DOCUMENT_SEND: "DOCUMENT_SEND", +}; + +// Routes +app.post('/api/send-email', validateEmail, async (req, res) => { + const { email, firstName, lastName, officeName, template } = req.body; + + try { + const templateVariables = { + first_name: firstName || '', + last_name: lastName || '', + office_name: officeName || '', + link: `${process.env.APP_HOST}` + }; + + const result = await EmailService.sendTransactionalEmail( + email, + ETemplates[template], + 'Votre notaire vous envoie un message', + templateVariables + ); + + if (!result.success) { + // Add to pending emails to retry later + const emailId = `${email}-${Date.now()}`; + pendingEmails.set(emailId, { + to: email, + templateName: ETemplates[template], + subject: 'Votre notaire vous envoie un message', + templateVariables, + attempts: 1, + lastAttempt: Date.now() + }); + } + + res.json({ + success: true, + message: 'Email envoyé avec succès' + }); + } catch (error) { + console.error('Erreur:', error); + res.status(500).json({ + success: false, + message: 'Erreur serveur lors de l\'envoi de l\'email' + }); + } +}); + +app.post('/api/subscribe-to-list', validateEmail, async (req, res) => { + const { email } = req.body; + + try { + const result = await EmailService.addToMailchimpList(email); + + if (result.success) { + res.json({ + success: true, + message: 'Inscription à la liste réussie' + }); + } else { + res.status(500).json({ + success: false, + message: 'Échec de l\'inscription à la liste' + }); + } + } catch (error) { + console.error('Erreur:', error); + res.status(500).json({ + success: false, + message: 'Erreur serveur lors de l\'inscription' + }); + } +}); + +// Automatic retry system +setInterval(() => { + EmailService.retryFailedEmails(); +}, 60000); // Check every minute + app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });