Files
Sathira Sri Sathara b6b345f245 feat: Implement Phase 2 cross-cutting services with email and notification enhancements
- Refactor email verification and password reset utilities to use new email service.
- Introduce email delivery queue and notification delivery model for better tracking.
- Enhance file validation and storage services for improved security and ownership management.
- Add cron job for cleaning inactive notifications with retention policy.
- Update document worker to handle document generation and storage more efficiently.
- Implement logging improvements in activity and log workers.
- Create comprehensive documentation for new API endpoints and services.
- Add unit tests for file validation and notification policies to ensure robustness.
2026-09-03 14:22:34 +05:30

97 lines
2.6 KiB
JavaScript

/**
* Copyright (c) 2026 Niolla
* All rights reserved.
*
* This source code is proprietary and confidential.
* Unauthorized copying, modification, distribution, or use
* of this file, via any medium, is strictly prohibited.
*/
// app/utils/mailer.util.js
const nodemailer = require("nodemailer");
const fs = require("fs");
const path = require("path");
const mailConfig = require("../config/mail.config");
const transporter = nodemailer.createTransport({
host: mailConfig.host,
port: mailConfig.port,
secure: mailConfig.secure,
auth: mailConfig.auth,
});
if (process.env.NODE_ENV !== "test" && process.env.ENABLE_MAIL === "true") {
transporter.verify((err) => {
if (err) console.error("Mail server connection failed", { message: err.message });
else console.log("Mail server ready");
});
}
// Utility to load template and replace placeholders
const loadTemplate = (templateName, variables = {}) => {
if (!/^[a-zA-Z0-9_-]+$/.test(templateName)) throw new Error("Invalid email template");
const templatePath = path.join(__dirname, "../templates/emails", `${templateName}.html`);
let template = fs.readFileSync(templatePath, "utf-8");
Object.keys(variables).forEach((key) => {
const regex = new RegExp(`{{${key}}}`, "g");
const escaped = String(variables[key] ?? "").replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[char]);
template = template.replace(regex, escaped);
});
return template;
};
const send = async ({ to, subject, templateName, templateVars = {}, text }) => {
if (process.env.ENABLE_MAIL !== "true") {
throw new Error("Email functionality is not enabled");
}
const html = templateName ? loadTemplate(templateName, templateVars) : undefined;
const mailOptions = {
from: mailConfig.from,
to,
subject,
text,
html,
};
try {
const info = await transporter.sendMail(mailOptions);
console.log("Email sent:", info.messageId);
return info;
} catch (err) {
console.error("Error sending email:", err);
throw err;
}
};
const sendMail = async (options) => {
const today = new Date();
// Full formatted date: "January 1, 2026"
const formattedDate = today.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
// Only year: "2026"
const currentYear = today.getFullYear();
const templateVars = {
...options.templateVars, // keep existing vars
currentDate: formattedDate,
currentYear: currentYear,
};
return send({ ...options, templateVars });
};
module.exports = { sendMail, loadTemplate };