UNPKG

@email-service/email-service

Version:

email-service is a versatile npm package designed to simplify the integration and standardization of email communications across multiple Email Service Providers (ESPs).

205 lines (204 loc) 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EmailServiceSelector = void 0; exports.getEmailService = getEmailService; exports.getWebHook = getWebHook; const normalizeEmailRecipients_js_1 = require("../utils/normalizeEmailRecipients.js"); const brevo_js_1 = require("./ESP/brevo.js"); const emailService_js_1 = require("./ESP/emailService.js"); //import { NodeMailerEmailService } from "./ESP/nodeMailer.js"; const postMark_js_1 = require("./ESP/postMark.js"); const resend_js_1 = require("./ESP/resend.js"); class EmailServiceSelector { constructor(service, opts) { switch (service.esp) { case 'postmark': this.emailService = new postMark_js_1.PostMarkEmailService(service, opts); break; case 'nodemailer': //this.emailService = new NodeMailerEmailService(service, opts); break; case 'brevo': this.emailService = new brevo_js_1.BrevoEmailService(service, opts); break; case 'emailserviceviewer': this.emailService = new emailService_js_1.ViewerEmailService(service, opts); break; case 'emailserviceviewerlocal': this.emailService = new emailService_js_1.ViewerEmailService(service, opts); break; case 'resend': this.emailService = new resend_js_1.ResendEmailService(service, opts); break; default: throw new Error('Invalid ESP'); break; } } /** * Envoi en lot — délègue à l'ESP sous-jacent (`ESP.sendBulk` de base). * Hooks + stream + rate limit appliqués automatiquement. */ async sendBulk(payload) { if (!this.emailService) { throw new Error('[sendBulk] no ESP configured'); } return this.emailService.sendBulk(payload); } async sendEmail(email) { if (!email) return ({ success: false, status: 400, error: { name: 'NO_EMAIL', message: 'No email provided' } }); const typeOfPayload = Array.isArray(email) ? 'array' : (typeof email === 'object') ? 'object' : 'unknown'; if (typeOfPayload === 'unknown') { return ({ success: false, status: 400, error: { name: 'INVALID_EMAIL_PAYLOAD', message: 'Invalid email payload type', cause: { type: typeof email } } }); } // Unify email to an array if it is a single object const emails = (Array.isArray(email) ? email : (typeof email === 'object') ? [email] : []); if (emails.length === 0) return ({ success: false, status: 400, error: { name: 'NO_EMAIL', message: 'No email provided' } }); if (this.emailService) { // Verification of the emails for (const email of emails) { /* Verification of the sender */ const from = this.emailService.checkFrom(email.from); if (!from) return ({ success: false, status: 400, error: { name: 'INVALID_SENDER', message: 'Invalid sender in the email' } }); if (!(0, normalizeEmailRecipients_js_1.isValidEmail)(from.email)) return ({ success: false, status: 400, error: { name: 'INVALID_SENDER', message: 'Invalid sender in the email', cause: { from } } }); email.from = from; /* Verification of the recipients */ // Formatting recipient addresses email.to = this.emailService.checkRecipients(email.to); // Check that there is at least one recipient in the email if (!email.to || !Array.isArray(email.to)) return ({ success: false, status: 400, error: { name: 'NO_RECIPIENT', message: 'No recipient in the email' } }); // Check that there is at least one sender in the email if (email.to.length === 0) return ({ success: false, status: 400, error: { name: 'NO_RECIPIENT', message: 'No recipient in the email' } }); // Email verification: const invalidRecipients = (0, normalizeEmailRecipients_js_1.checkValidityOfEmails)(email.to); if (invalidRecipients.length > 0) return ({ success: false, status: 400, error: { name: 'INVALID_RECIPIENT', message: 'Invalid recipient in the email', cause: invalidRecipients } }); /* Verification of CC */ if (email.cc) { email.cc = this.emailService.checkRecipients(email.cc); const invalidRecipientsCC = (0, normalizeEmailRecipients_js_1.checkValidityOfEmails)(email.cc); if (invalidRecipientsCC.length > 0) return ({ success: false, status: 400, error: { name: 'INVALID_RECIPIENT', message: 'Invalid recipient in the email', cause: invalidRecipientsCC } }); } /* Verification of BCC */ if (email.bcc) { email.bcc = this.emailService.checkRecipients(email.bcc); const invalidRecipientsBCC = (0, normalizeEmailRecipients_js_1.checkValidityOfEmails)(email.bcc); if (invalidRecipientsBCC.length > 0) return ({ success: false, status: 400, error: { name: 'INVALID_RECIPIENT', message: 'Invalid recipient in the email', cause: invalidRecipientsBCC } }); } const recipients = email.to.concat(email.cc ? email.cc : []).concat(email.bcc ? email.bcc : []); // No more than 50 recipients if (recipients.length > 50) return ({ success: false, status: 400, error: { name: 'TOO_MANY_RECIPIENTS', message: 'Too many recipients in the email (50 max)', cause: { number: recipients.length, emails: recipients } } }); // Check that there is a subject in the email if (!email.subject) return ({ success: false, status: 400, error: { name: 'NO_SUBJECT', message: 'No subject in the email' } }); // Check that there is content in the email if (!email.html || !email.text) return ({ success: false, status: 400, error: { name: 'NO_CONTENT', message: 'No content in the email' } }); } // if one email if (emails.length === 1) { const resultat = await this.emailService.sendMail(emails[0]); if (typeOfPayload === 'object') { // If the payload is a single object, return the response directly return resultat; } else { // If the payload is an array, return the response in an array return [resultat]; } } // Multiple emails else if (this.emailService.mailMultiple && this.emailService.mailMultiple === true) { // If the email service supports sending multiple emails at by 500 once console.log('******** ES-Email ******** Sending multiple emails at once'); return await this.emailService.sendMailMultiple(emails); } else { // If the email service does not support sending multiple emails at once, send them one by one console.log('******** ES-Email ******** Sending multiple emails one by one'); const responses = []; for (const email of emails) { const response = await this.emailService.sendMail(email); responses.push(response); } return responses; } } else return ({ success: false, status: 500, error: { name: 'NO_ESP', message: 'No ESP service configured' } }); } static async sendEmail(esp, email) { const emailServiceSelector = new EmailServiceSelector(esp); // Un EmailPayload unique → toujours une StandardResponse unique (pas un tableau). // Le typage union de l'instance couvre aussi le cas tableau ; on cast pour la signature statique. return await emailServiceSelector.sendEmail(email); } close() { // Nothing, as we are using only for nodemailer } static async webHook(esp, req, logger = false) { if (esp) { if (logger) console.log("******** ES-WebHook ******** esp", esp); // Prefix matching : les ESP envoient un User-Agent avec version // (ex: "Postmark HTTPClient 1.2.3", "Svix-Webhooks/1.84.0 (sender-X...)", // "SendinBlue Webhook/2.0"). Un match exact casse dès que le provider // upgrade sa version, d'où le startsWith. const config = { esp: 'emailserviceviewer', logger: logger }; if (esp.startsWith('Postmark')) { config.esp = 'postmark'; } else if (esp.startsWith('SendinBlue') || esp.startsWith('Brevo')) { config.esp = 'brevo'; } else if (esp.startsWith('Svix-Webhooks')) { // Svix est le provider de webhook utilisé par Resend. config.esp = 'resend'; } else if (esp === 'email-service-viewer') { config.esp = 'emailserviceviewer'; } else if (esp === 'nodemailer') { return ({ success: false, status: 500, error: { name: 'NO_NODEMAILER', message: 'No webhook traitement for nodemailer' } }); } else { return ({ success: false, status: 500, error: { name: 'INVALID_ESP', message: 'No ESP service configured for ' + esp } }); } if (logger) console.log("******** ES-WebHook ******** config", config); // @ts-ignore const emailESP = new EmailServiceSelector(config); if (logger) console.log("******** ES-WebHook ******** emailESP", emailESP); if (emailESP.emailService) { return await emailESP.emailService.webHookManagement(req); } else { return ({ success: false, status: 500, error: { name: 'NO_ESP', message: 'No ESP service configured' } }); } } else { return ({ success: false, status: 500, error: { name: 'NO_ESP', message: 'No ESP service configured' } }); } } } exports.EmailServiceSelector = EmailServiceSelector; function getEmailService(service, opts) { return new EmailServiceSelector(service, opts); } async function getWebHook(userAgent, req, logger = false) { if (logger) { console.log('******** ES-WebHook ******** userAgent, logger', userAgent, logger); console.log('******** ES-WebHook ******** req', req); } return await EmailServiceSelector.webHook(userAgent, req, logger); }