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).

184 lines (183 loc) 9.76 kB
import { checkValidityOfEmails, isValidEmail } from "../utils/normalizeEmailRecipients.js"; import { BrevoEmailService } from "./ESP/brevo.js"; import { ViewerEmailService } from "./ESP/emailService.js"; //import { NodeMailerEmailService } from "./ESP/nodeMailer.js"; import { PostMarkEmailService } from "./ESP/postMark.js"; import { ResendEmailService } from "./ESP/resend.js"; export class EmailServiceSelector { constructor(service) { switch (service.esp) { case 'postmark': this.emailService = new PostMarkEmailService(service); break; case 'nodemailer': //this.emailService = new NodeMailerEmailService(service); break; case 'brevo': this.emailService = new BrevoEmailService(service); break; case 'emailserviceviewer': this.emailService = new ViewerEmailService(service); break; case 'emailserviceviewerlocal': this.emailService = new ViewerEmailService(service); break; case 'resend': this.emailService = new ResendEmailService(service); break; default: throw new Error('Invalid ESP'); break; } } 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 (!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 = 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 = 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 = 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); 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); const config = { esp: 'emailserviceviewer', logger: logger }; switch (esp) { case 'Postmark': config.esp = 'postmark'; break; case 'nodemailer': return ({ success: false, status: 500, error: { name: 'NO_NODEMAILER', message: 'No webhook traitement for nodemailer' } }); break; case 'SendinBlue Webhook': config.esp = 'brevo'; break; case 'email-service-viewer': config.esp = 'emailserviceviewer'; break; case 'Svix-Webhooks/1.24.0': config.esp = 'resend'; break; default: return ({ success: false, status: 500, error: { name: 'INVALID_ESP', message: 'No ESP service configured for ' + esp } }); break; } 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' } }); } } } export function getEmailService(service) { return new EmailServiceSelector(service); } export 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); }