zyx-nodemailer
Version:
Implements email sending service using nodemailer
132 lines (117 loc) • 3.5 kB
JavaScript
// nodeEmailer.js:
;
/**
* NodeEmailer - SMTP email service using nodemailer.
* Extends BaseEmailer, which itself extends BaseService.
*/
const { BaseEmailer } = require("zyx-base");
const Schema = require("zyx-schema");
const system = require("zyx-system");
const nodemailer = require("nodemailer");
const { stringType, booleanType, integerType } = Schema.types;
/**
* Email sender class using SMTP via nodemailer.
* Inherits connection lifecycle and message templating from BaseEmailer.
*
* @extends BaseEmailer
*/
class NodeEmailer extends BaseEmailer {
/**
* Validates SMTP configuration using a strict schema.
* Automatically called from Base constructor.
*
* @override
* @throws {Error} If configuration is invalid.
*/
verifyConfig() {
const { errors } = new Schema({
smtp_host: stringType({ min: 1, max: 255, required: true }),
smtp_port: integerType({ min: 1, max: 65535, required: true }),
smtp_secure: booleanType({ required: true }),
smtp_username: stringType({ min: 1, max: 255, required: true }),
smtp_password: stringType({ min: 1, max: 255, required: true }),
}).validate(this.config);
if (errors.length > 0) {
const message = errors.map(e => e.message).join(", ");
system.log?.error?.(`SMTP config validation failed: ${message}`);
throw new Error("SMTP config invalid: " + message);
}
}
/**
* Establishes SMTP transport via nodemailer.
* Required by BaseService.
*
* @override
* @returns {Promise<void>}
*/
async connect() {
await this.createTransport();
}
/**
* Closes the current SMTP transport, if available.
* Required by BaseService.
*
* @override
* @returns {Promise<void>}
*/
async disconnect() {
const conn = this.connection;
if (conn?.close) {
const maybePromise = conn.close();
if (maybePromise instanceof Promise) await maybePromise;
}
this.setConnection(undefined);
}
/**
* Creates and verifies a nodemailer transport.
*
* @override
* @returns {Promise<void>}
* @throws {Error} If connection fails.
*/
async createTransport() {
const { smtp_host, smtp_port, smtp_secure, smtp_username, smtp_password } =
this.config;
const transporter = nodemailer.createTransport({
host: smtp_host,
port: smtp_port,
secure: smtp_secure,
auth: {
user: smtp_username,
pass: smtp_password,
},
requireTLS: true,
tls: {
rejectUnauthorized: false, // ⚠️ Consider making this configurable for stricter environments
},
logger: false,
debug: false,
name: "localhost",
});
try {
await transporter.verify();
this.transport = transporter;
this.setConnection(transporter);
} catch (err) {
transporter.close?.();
this.setConnection(undefined);
system.log?.error?.("SMTP connection failed: " + err.message);
throw new Error("Unable to connect to SMTP server: " + err.message);
}
}
/**
* Sends an email using the SMTP transport.
*
* @override
* @param {object} [data={}] - Data for templating email content.
* @returns {Promise<object>} Result from nodemailer `sendMail`.
*/
async send(data = {}) {
if (!this.connection) {
await this.connect();
}
const msg = await this.buildMessageObject(data);
return this.connection.sendMail(msg);
}
}
module.exports = NodeEmailer;