/*********************************************************************************************************************************************************************** * 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) => ` ${body.trim()}

Regards,
Philproperties IT Team

This is an automated message from STARR System. Please do not reply.

`.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 };