Files
starr-philproperties/services/email.service.js
T
kennethobsequio 1d12f04967 new commits
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
2026-07-03 16:20:58 +08:00

92 lines
3.6 KiB
JavaScript

/***********************************************************************************************************************************************************************
* File Name: email.service.js
* Type of Program: Service
* Description: Nodemailer-based email service.
* Subject + body per type are loaded from the email_templates
* table (admin-editable, see controllers/admin/email_templates.controller.js).
* The outer layout (header/footer/signature) below is fixed in
* code and is NOT admin-editable — only the body content is.
* Author: rgrgogu, Kenneth Obsequio (@lash0000)
* Date Created: Oct. 6, 2025
* Date Modified: Jul. 3, 2026 — templates moved from data/email_body.data.js into the DB
***********************************************************************************************************************************************************************
* HOW TO USE:
* const emailService = require('../services/email.service');
* await emailService.sendEmail({ to: user.email, type: 'OTP', data: { otp } });
***********************************************************************************************************************************************************************/
const nodemailer = require('nodemailer');
const mdl_EmailTemplate = require('../models/email_templates/email_templates.mdl');
const { enrichEmailData } = require('../data/email_template_enrichers.data');
const { renderTemplate } = require('../utils/renderTemplate.util');
const port = Number(process.env.SMTP_PORT);
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port,
secure: port === 465,
requireTLS: port === 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
tls: {
rejectUnauthorized: false,
},
});
// Fixed layout — admins cannot change header/footer/signature via the CRUD,
// only the body content per template type.
const FONT = 'font-family: Arial, sans-serif; font-size: 14px; color: #000;';
const wrap = (body) => `
<html>
<body style="${FONT} line-height: 1.6;">
${body.trim()}
<br><br>
<p style="margin: 0;">Regards,<br>Philproperties IT Team</p>
<p style="margin: 0; font-size: 12px; color: #555;">This is an automated message from STARR System. Please do not reply.</p>
</body>
</html>`.trim();
const sendEmail = async ({ to, type, data = {} }) => {
try {
const template = await mdl_EmailTemplate.findOne({ where: { type } });
if (!template) {
throw new Error(`Email template "${type}" not found`);
}
// Draft content (or a template that's never been sent) never reaches
// real mail — only the live subject/html_body columns count as "published".
if (!template.subject || !template.html_body) {
throw new Error(`Email template "${type}" has no published (sent) version yet`);
}
const enriched = enrichEmailData(type, data);
const subject = renderTemplate(template.subject, enriched);
const html = wrap(renderTemplate(template.html_body, enriched));
return await new Promise((resolve, reject) => {
transporter.sendMail(
{
from: {
name: "STARR System",
address: process.env.EMAIL_FROM,
},
to,
subject,
html,
},
(err, info) => {
if (err) return reject(err);
resolve(info);
}
);
});
} catch (err) {
throw new Error(err.message);
}
};
const ping = () => transporter.verify();
module.exports = { sendEmail, ping };