mod_utils_mailer.js

/**
@module /utils/mailer
@description
The mailer module provides a way to send emails to clients/admins, etc.

@requires module:/utils/logger
@requires module:/utils/languageTemplates
@requires module:/provider/getFrom

@requires nodemailer
*/

import logger from './logger.js';

import languageTemplates from './languageTemplates.js';

import getFrom from '../provider/getFrom.js';

//Attempt to import node mailer
let nodeMailer, transport;

if (xyzEnv.TRANSPORT) {
  console.warn(
    'Please replace xyzEnv.TRANSPORT with TRANSPORT_HOST,TRANSPORT_EMAIL, and TRANSPORT_PASSWORD',
  );
} else if (
  xyzEnv.TRANSPORT_EMAIL ||
  xyzEnv.TRANSPORT_USERNAME ||
  xyzEnv.TRANSPORT_HOST ||
  xyzEnv.TRANSPORT_PASSWORD
) {
  try {
    nodeMailer = await import('nodemailer');
  } catch {
    console.error('Error: Missing nodemailer dependancy');
  }
}

export default mailer;

/**
@function mailer
@async

@description
Function which sends email using the nodemailer dependancy.

@param {Object} params
@property {String} params.to The email recipient.
@property {String} params.template The html that will make up the body of the email.
@property {String} params.language The language to be used.
@property {String} params.host The URL of the instance.
*/
async function mailer(params) {
  // The nodeMailer module could not be imported.
  if (!nodeMailer) return;

  if (!xyzEnv.TRANSPORT_HOST) {
    console.warn('xyzEnv.TRANSPORT_HOST missing.');
    return;
  }

  if (!xyzEnv.TRANSPORT_EMAIL) {
    console.warn('xyzEnv.TRANSPORT_EMAIL is missing.');
    return;
  }

  if (!xyzEnv.TRANSPORT_PASSWORD) {
    console.warn('xyzEnv.TRANSPORT_PASSWORD missing.');
    return;
  }

  if (!transport) {
    transport = nodeMailer.createTransport({
      host: xyzEnv.TRANSPORT_HOST,
      name: xyzEnv.TRANSPORT_NAME || xyzEnv.TRANSPORT_EMAIL.split('@')[0],
      port: xyzEnv.TRANSPORT_PORT || 587,
      secure: false,
      requireTLS: xyzEnv.TRANSPORT_TLS,
      auth: {
        user: xyzEnv.TRANSPORT_USERNAME || xyzEnv.TRANSPORT_EMAIL,
        pass: xyzEnv.TRANSPORT_PASSWORD,
      },
    });
  }

  const template = await languageTemplates(params);

  await getBody(template);

  const mailTemplate = {
    to: params.to,
    from: xyzEnv.TRANSPORT_EMAIL,
    sender: xyzEnv.TRANSPORT_EMAIL,
    subject: replaceStringParams(template.subject, params),
    html: template.html
      ? replaceStringParams(template.html, params)
      : undefined,
    text: template.text
      ? replaceStringParams(template.text, params)
      : undefined,
  };

  const result = await transport
    .sendMail(mailTemplate)
    .catch((err) => console.error(err));

  logger(result, 'mailer');
}

/**
@function getBody
@async

@description
Retrieves the body of the text from the provided url/file.

@param {Object} template
@property {String} template.text The url from which to retrieve the text content using {@link module:/provider/getFrom~flyTo}.
@property {String} template.html The url from which to retrieve the html content using {@link module:/provider/getFrom~flyTo}.
*/
async function getBody(template) {
  if (template.text) {
    // Prevent mail template from having text and html
    delete template.html;

    if (Object.hasOwn(getFrom, template.text.split(':')[0])) {
      template.text = await getFrom[template.text.split(':')[0]](template.text);
    }
  }

  if (template.html) {
    if (Object.hasOwn(getFrom, template.html.split(':')[0])) {
      template.html = await getFrom[template.html.split(':')[0]](template.html);
    }
  }
}

/**
@function replaceStringParams

@description
Substitutes supplied params into a supplied string.

@param {String} string
@property {Object} params The url from which to retrieve the text content using {@link module:/provider/getFrom~flyTo}.

@returns {String} The string with substitutions made.
*/
function replaceStringParams(string, params) {
  return string.replace(
    /\$\{(.*?)\}/g,

    // Replace matched params in string values
    (matched) => params[matched.replace(/\$\{|\}/g, '')] || '',
  );
}