feature: nodemailer set

This commit is contained in:
Anushlinux
2025-09-03 18:54:55 +05:30
parent 972ed81192
commit 425475c027
9 changed files with 1235 additions and 21 deletions

View File

@@ -0,0 +1,28 @@
export function statusUpdateSubject(status: string): string {
return `Your application status has been updated to ${status}`;
}
export function statusUpdateText(name: string, status: string): string {
return `Hi ${name},\n\nYour application status has been updated to ${status}.\n\nIf you have any questions, please reply to this email.\n\nBest regards,\nPlacement Cell`;
}
export function statusUpdateHtml(name: string, status: string): string {
return `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Arial, sans-serif; color: #0f172a;">
<h2 style="margin: 0 0 16px; font-weight: 600;">Application Status Updated</h2>
<p style="margin: 0 0 12px;">Hi <strong>${escapeHtml(name)}</strong>,</p>
<p style="margin: 0 0 12px;">Your application status has been updated to <strong>${escapeHtml(status)}</strong>.</p>
<p style="margin: 0 0 12px;">If you have any questions, please reply to this email.</p>
<p style="margin-top: 24px; color: #475569;">Best regards,<br/>Placement Cell</p>
</div>
`;
}
function escapeHtml(input: string): string {
return input
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}

85
apps/admin/lib/mailer.ts Normal file
View File

@@ -0,0 +1,85 @@
import nodemailer, { Transporter } from 'nodemailer';
/**
* Singleton pooled transporter for server-side usage
*/
let cachedTransporter: Transporter | null = null;
function getEnv(name: string, fallbacks: string[] = []): string | undefined {
const keys = [name, ...fallbacks];
for (const key of keys) {
const val = process.env[key];
if (val && String(val).length > 0) return val;
}
return undefined;
}
function createTransporter(): Transporter {
// Support URL-style config first
const smtpUrl = getEnv('SMTP_URL', ['MAIL_URL']);
const host = getEnv('SMTP_HOST', ['MAIL_HOST']);
const portStr = getEnv('SMTP_PORT', ['MAIL_PORT']);
const user = getEnv('SMTP_USER', ['SMTP_USERNAME', 'MAIL_USER', 'MAIL_USERNAME']);
const pass = getEnv('SMTP_PASS', ['SMTP_PASSWORD', 'MAIL_PASS', 'MAIL_PASSWORD']);
const secureStr = getEnv('SMTP_SECURE');
if (smtpUrl) {
return nodemailer.createTransport({
pool: true,
url: smtpUrl,
maxConnections: Number(process.env.SMTP_MAX_CONNECTIONS || 5),
maxMessages: Number(process.env.SMTP_MAX_MESSAGES || 100),
} as any);
}
const port = Number(portStr || 587);
const isSecure = secureStr ? /^(1|true|yes)$/i.test(secureStr) : port === 465;
if (!host || !user || !pass) {
const missing: string[] = [];
if (!host) missing.push('SMTP_HOST');
if (!user) missing.push('SMTP_USER');
if (!pass) missing.push('SMTP_PASS');
throw new Error(
`SMTP configuration missing. Please set ${missing.join(', ')} (alternatives supported: SMTP_URL, SMTP_USERNAME/SMTP_PASSWORD, MAIL_*).`
);
}
return nodemailer.createTransport({
pool: true,
host,
port,
secure: isSecure,
auth: { user, pass },
maxConnections: Number(process.env.SMTP_MAX_CONNECTIONS || 5),
maxMessages: Number(process.env.SMTP_MAX_MESSAGES || 100),
});
}
export function getMailer(): Transporter {
if (!cachedTransporter) {
cachedTransporter = createTransporter();
}
return cachedTransporter;
}
export type SendEmailParams = {
to: string;
subject: string;
html?: string;
text?: string;
fromOverride?: string;
};
export async function sendEmail({ to, subject, html, text, fromOverride }: SendEmailParams): Promise<void> {
const transporter = getMailer();
const from = fromOverride || process.env.SMTP_FROM || process.env.SMTP_USER || 'no-reply@example.com';
await transporter.sendMail({
from,
to,
subject,
text,
html,
});
}