diff --git a/Server/service/emailService.js b/Server/service/emailService.js index 7bcaa31f0..40641ef42 100644 --- a/Server/service/emailService.js +++ b/Server/service/emailService.js @@ -1,11 +1,6 @@ -import fs from "fs"; -import path from "path"; -import nodemailer from "nodemailer"; -import pkg from "handlebars"; -const { compile } = pkg; -import mjml2html from "mjml"; -import logger from "../utils/logger.js"; import { fileURLToPath } from "url"; +import path from "path"; + const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -15,128 +10,134 @@ const SERVICE_NAME = "EmailService"; * Represents an email service that can load templates, build, and send emails. */ class EmailService { - /** - * Constructs an instance of the EmailService, initializing template loaders and the email transporter. - */ - constructor(settingsService) { - this.settingsService = settingsService; - /** - * Loads an email template from the filesystem. - * - * @param {string} templateName - The name of the template to load. - * @returns {Function} A compiled template function that can be used to generate HTML email content. - */ - this.loadTemplate = (templateName) => { - try { - const templatePath = path.join( - __dirname, - `../templates/${templateName}.mjml` - ); - const templateContent = fs.readFileSync(templatePath, "utf8"); - return compile(templateContent); - } catch (error) { - logger.error("Error loading Email templates", { - error, - service: this.SERVICE_NAME, - }); - } - }; + /** + * Constructs an instance of the EmailService, initializing template loaders and the email transporter. + * @param {Object} settingsService - The settings service to get email configuration. + * @param {Object} fs - The file system module. + * @param {Object} path - The path module. + * @param {Function} compile - The Handlebars compile function. + * @param {Function} mjml2html - The MJML to HTML conversion function. + * @param {Object} nodemailer - The nodemailer module. + * @param {Object} logger - The logger module. + */ + constructor(settingsService, fs, path, compile, mjml2html, nodemailer, logger) { + this.settingsService = settingsService; + this.fs = fs; + this.path = path; + this.compile = compile; + this.mjml2html = mjml2html; + this.nodemailer = nodemailer; + this.logger = logger; - /** - * A lookup object to access preloaded email templates. - * @type {Object.} - * TODO Load less used templates in their respective functions - */ - this.templateLookup = { - welcomeEmailTemplate: this.loadTemplate("welcomeEmail"), - employeeActivationTemplate: this.loadTemplate("employeeActivation"), - noIncidentsThisWeekTemplate: this.loadTemplate("noIncidentsThisWeek"), - serverIsDownTemplate: this.loadTemplate("serverIsDown"), - serverIsUpTemplate: this.loadTemplate("serverIsUp"), - passwordResetTemplate: this.loadTemplate("passwordReset"), - }; + /** + * Loads an email template from the filesystem. + * + * @param {string} templateName - The name of the template to load. + * @returns {Function} A compiled template function that can be used to generate HTML email content. + */ + this.loadTemplate = (templateName) => { + try { + const templatePath = this.path.join( + __dirname, + `../templates/${templateName}.mjml` + ); + const templateContent = this.fs.readFileSync(templatePath, "utf8"); + return this.compile(templateContent); + } catch (error) { + logger.error("Error loading Email templates", { + error, + service: this.SERVICE_NAME, + }); + } + }; - /** - * The email transporter used to send emails. - * @type {Object} - */ + /** + * A lookup object to access preloaded email templates. + * @type {Object.} + * TODO Load less used templates in their respective functions + */ + this.templateLookup = { + welcomeEmailTemplate: this.loadTemplate("welcomeEmail"), + employeeActivationTemplate: this.loadTemplate("employeeActivation"), + noIncidentsThisWeekTemplate: this.loadTemplate("noIncidentsThisWeek"), + serverIsDownTemplate: this.loadTemplate("serverIsDown"), + serverIsUpTemplate: this.loadTemplate("serverIsUp"), + passwordResetTemplate: this.loadTemplate("passwordReset"), + }; - const { - systemEmailHost, - systemEmailPort, - systemEmailAddress, - systemEmailPassword, - } = this.settingsService.getSettings(); + /** + * The email transporter used to send emails. + * @type {Object} + */ - const emailConfig = { - host: systemEmailHost, - port: systemEmailPort, - secure: true, - auth: { - user: systemEmailAddress, - pass: systemEmailPassword, - }, - }; + const { systemEmailHost, systemEmailPort, systemEmailAddress, systemEmailPassword } = + this.settingsService.getSettings(); - this.transporter = nodemailer.createTransport(emailConfig); - } + const emailConfig = { + host: systemEmailHost, + port: systemEmailPort, + secure: true, + auth: { + user: systemEmailAddress, + pass: systemEmailPassword, + }, + }; - /** - * Asynchronously builds and sends an email using a specified template and context. - * - * @param {string} template - The name of the template to use for the email body. - * @param {Object} context - The data context to render the template with. - * @param {string} to - The recipient's email address. - * @param {string} subject - The subject of the email. - * @returns {Promise} A promise that resolves to the messageId of the sent email. - */ - buildAndSendEmail = async (template, context, to, subject) => { - const buildHtml = async (template, context) => { - try { - const mjml = this.templateLookup[template](context); - const html = await mjml2html(mjml); - return html.html; - } catch (error) { - logger.error("Error building Email HTML", { - error, - service: SERVICE_NAME, - }); - } - }; + this.transporter = this.nodemailer.createTransport(emailConfig); + } - const sendEmail = async (to, subject, html) => { - try { - const info = await this.transporter.sendMail({ - to: to, - subject: subject, - html: html, - }); - return info; - } catch (error) { - logger.error("Error sending Email", { - error, - service: SERVICE_NAME, - }); - } - }; + /** + * Asynchronously builds and sends an email using a specified template and context. + * + * @param {string} template - The name of the template to use for the email body. + * @param {Object} context - The data context to render the template with. + * @param {string} to - The recipient's email address. + * @param {string} subject - The subject of the email. + * @returns {Promise} A promise that resolves to the messageId of the sent email. + */ + buildAndSendEmail = async (template, context, to, subject) => { + const buildHtml = async (template, context) => { + try { + const mjml = this.templateLookup[template](context); + const html = await this.mjml2html(mjml); + return html.html; + } catch (error) { + logger.error("Error building Email HTML", { + error, + service: SERVICE_NAME, + }); + } + }; - try { - const info = await sendEmail( - to, - subject, - await buildHtml(template, context) - ); - return info.messageId; - } catch (error) { - error.service = SERVICE_NAME; - if (error.method === undefined) { - error.method = "buildAndSendEmail"; - } - logger.error("Error building and sending Email", { - error, - service: SERVICE_NAME, - }); - } - }; + const sendEmail = async (to, subject, html) => { + try { + const info = await this.transporter.sendMail({ + to: to, + subject: subject, + html: html, + }); + return info; + } catch (error) { + logger.error("Error sending Email", { + error, + service: SERVICE_NAME, + }); + } + }; + + try { + const info = await sendEmail(to, subject, await buildHtml(template, context)); + return info.messageId; + } catch (error) { + error.service = SERVICE_NAME; + if (error.method === undefined) { + error.method = "buildAndSendEmail"; + } + logger.error("Error building and sending Email", { + error, + service: SERVICE_NAME, + }); + } + }; } export default EmailService;