@dax-crafta/auth
Version:
A powerful, flexible, and secure authentication plugin for the Crafta framework. Supports JWT, social login, 2FA, RBAC, audit logging, and enterprise-grade security features.
144 lines (123 loc) • 4 kB
JavaScript
// packages/auth/src/utils/email.js
const nodemailer = require('nodemailer');
const fs = require('fs');
const path = require('path');
class EmailService {
constructor(config) {
this.config = config;
this.smtpEnabled = !!(
config.smtp &&
config.smtp.host &&
config.smtp.port &&
config.smtp.auth &&
config.smtp.auth.user &&
config.smtp.auth.pass &&
config.smtp.from
);
this.transporter = this.smtpEnabled ? nodemailer.createTransport(config.smtp) : null;
this.from = this.smtpEnabled ? config.smtp.from : 'noreply@localhost';
this.templateDir = config.emailTemplateDir || path.join(process.cwd(), "templates/email");
}
// Reusable sender
async send({ to, subject, html, text }) {
if (!this.smtpEnabled) {
return { sent: false, reason: 'smtp_disabled' };
}
try {
await this.transporter.sendMail({
from: this.from,
to,
subject,
html,
text: text || html.replace(/<[^>]+>/g, '')
});
return { sent: true };
} catch (err) {
console.error("Email send error:", err);
return { sent: false, reason: err.message };
}
}
// Load an HTML template safely
loadTemplate(name, variables = {}) {
const filePath = path.join(this.templateDir, `${name}.html`);
if (!fs.existsSync(filePath)) return null;
let content = fs.readFileSync(filePath, "utf8");
// Replace {{variable}}
for (const key of Object.keys(variables)) {
content = content.replace(new RegExp(`{{${key}}}`, "g"), variables[key]);
}
return content;
}
async sendVerificationEmail(user, token) {
const verifyPath = this.config.routes?.verify || '/verify';
const url = `${this.config.baseUrl}${verifyPath}?token=${token}`;
const html = this.loadTemplate("email-verification", {
name: user.name || user.email,
url
}) || `Click to verify your email: <a href="${url}">${url}</a>`;
return this.send({
to: user.email,
subject: "Verify Your Email",
html,
text: `Verify your email: ${url}`
});
}
async sendPasswordResetEmail(user, token) {
const resetPath = this.config.routes?.resetPassword || '/reset-password';
const url = `${this.config.baseUrl}${resetPath}?token=${token}`;
const html = this.loadTemplate("password-reset", {
name: user.name || user.email,
url
}) || `Reset password: <a href="${url}">${url}</a>`;
return this.send({
to: user.email,
subject: "Reset Your Password",
html,
text: `Reset your password: ${url}`
});
}
async sendLoginAlert(user, info) {
const html = this.loadTemplate("login-alert", {
name: user.name || user.email,
ip: info.ip,
agent: info.userAgent
}) || `
New login detected.<br>
IP: ${info.ip}<br>
User Agent: ${info.userAgent}
`;
return this.send({
to: user.email,
subject: "New Login Detected",
html,
text: `New login from IP ${info.ip}`
});
}
async send2FACode(user, code) {
const html = this.loadTemplate("twofa-code", {
name: user.name || user.email,
code
}) || `Your 2FA code: <b>${code}</b>`;
return this.send({
to: user.email,
subject: "Your 2FA Code",
html,
text: `Your 2FA code is: ${code}`
});
}
async sendAccountLockEmail(user) {
const html = this.loadTemplate("account-locked", {
name: user.name || user.email
}) || `
Your account has been locked due to too many failed login attempts.<br>
Try again after 1 hour.
`;
return this.send({
to: user.email,
subject: "Your Account Has Been Locked",
html,
text: "Your account is locked for 1 hour due to failed login attempts."
});
}
}
module.exports = EmailService;