From f41d7418a7b8098d3080257b88a9b46c45305a4f Mon Sep 17 00:00:00 2001 From: popsman01 <251607257+popsman01@users.noreply.github.com> Date: Sun, 18 Oct 2026 14:15:34 +0000 Subject: [PATCH] simple: batch fix 4 issues Co-authored-by: Simple Agent --- backend/src/notifications/channels/email.js | 73 ++------------------- backend/src/notifications/service.js | 3 +- backend/src/notifications/templates.js | 13 +--- 3 files changed, 10 insertions(+), 79 deletions(-) diff --git a/backend/src/notifications/channels/email.js b/backend/src/notifications/channels/email.js index ff5ecb21..ab2a7984 100644 --- a/backend/src/notifications/channels/email.js +++ b/backend/src/notifications/channels/email.js @@ -1,7 +1,3 @@ -'use strict'; - -const nodemailer = require('nodemailer'); - /** * Email notification channel. * Uses nodemailer in production; stubs in development/test. @@ -107,56 +103,11 @@ export async function sendEmail(to, { subject, body, html }) { logger.error('email.transport.failed', { error: err.message }); return { success: false, error: err.message }; } -let transporter = null; - -function getTransporter() { - if (transporter) { - return transporter; - } - - const { SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS } = process.env; - - if (!SMTP_HOST) { - return null; - } - - transporter = nodemailer.createTransport({ - host: SMTP_HOST, - port: SMTP_PORT ? Number(SMTP_PORT) : 587, - secure: Number(SMTP_PORT) === 465, - auth: SMTP_USER - ? { - user: SMTP_USER, - pass: SMTP_PASS, - } - : undefined, - }); - - return transporter; -} - -/** - * Send an email message. - * - * @param {Object} message - * @param {string} message.to Recipient address. - * @param {string} message.subject Subject line. - * @param {string} message.text Plain-text body. - * @param {string} [message.html] Optional HTML body. - * @returns {Promise<{sent: boolean, reason?: string}>} - */ -async function sendEmail({ to, subject, text, html }) { - if (!to || !subject) { - throw new Error('sendEmail requires "to" and "subject"'); - } - - const transport = getTransporter(); - if (!transport) { - // No SMTP configured: log and report as not sent so callers can decide. - // eslint-disable-next-line no-console - console.warn(`[email] SMTP not configured, skipping email to ${to}: ${subject}`); - return { sent: false, reason: 'smtp_not_configured' }; + if (!t) { + // No SMTP configured — stub in dev/test environments + logger.warn('email.stub', { to, subject }); + return { success: true, stub: true }; } try { @@ -172,15 +123,7 @@ async function sendEmail({ to, subject, text, html }) { } catch (err) { logger.error('email.send.failed', { to, subject, error: err.message }); return { success: false, error: err.message }; - await transport.sendMail({ - from: process.env.SMTP_FROM || process.env.SMTP_USER || 'no-reply@localhost', - to, - subject, - text, - html, - }); - - return { sent: true }; + } } /** @@ -213,7 +156,5 @@ async function sendVerificationEmail({ to, token, ttlMinutes = 15 }) { return sendEmail({ to, subject, text, html }); } -module.exports = { - sendEmail, - sendVerificationEmail, -}; +export { sendVerificationEmail }; +export default { sendEmail, sendVerificationEmail }; diff --git a/backend/src/notifications/service.js b/backend/src/notifications/service.js index 078526fc..d7822150 100644 --- a/backend/src/notifications/service.js +++ b/backend/src/notifications/service.js @@ -71,8 +71,7 @@ export async function cleanupStaleNotifications({ now = new Date() } = {}) { * @param {string} [params.locale] - Override locale; if omitted, read from user preferences * @returns {Promise} Results per channel */ -export async function sendNotification({ userId, type, data = {}, email, phone, phoneRegion, publicKey, actionUrl, actionRetryParams, channels = CHANNELS }) { -export async function sendNotification({ userId, type, data = {}, email, phone, publicKey, actionUrl, actionRetryParams, channels = CHANNELS, locale }) { +export async function sendNotification({ userId, type, data = {}, email, phone, phoneRegion, publicKey, actionUrl, actionRetryParams, channels = CHANNELS, locale }) { const results = {}; // Resolve locale: caller may supply it explicitly (e.g. from a webhook diff --git a/backend/src/notifications/templates.js b/backend/src/notifications/templates.js index a4583161..e9b2e36e 100644 --- a/backend/src/notifications/templates.js +++ b/backend/src/notifications/templates.js @@ -101,24 +101,15 @@ export function renderHtmlTemplate(template, data = {}) { } /** - * Get a rendered template for a given type and channel. + * Get a rendered template for a given type, channel, and locale. * Email templates are rendered as both plain text (`body`) and HTML (`html`). * The subject is a plain-text header and is never HTML-encoded. - * @param {string} type - Template key (e.g. 'transaction_received') - * @param {string} channel - 'email' | 'push' | 'sms' | 'inApp' - * @param {Record} data - * @returns {{ subject?: string, title?: string, body: string, html?: string } | null} - */ -export function getRenderedTemplate(type, channel, data = {}) { - if (!Object.prototype.hasOwnProperty.call(TEMPLATES, type)) return null; - const tmpl = Object.prototype.hasOwnProperty.call(TEMPLATES[type], channel) ? TEMPLATES[type][channel] : null; - * Get a rendered template for a given type, channel, and locale. * * @param {string} type - Template key (e.g. 'transaction_received') * @param {string} channel - 'email' | 'push' | 'sms' | 'inApp' * @param {Record} data - Interpolation variables * @param {string} [locale='en'] - Recipient's preferred locale - * @returns {{ subject?: string, title?: string, body: string } | null} + * @returns {{ subject?: string, title?: string, body: string, html?: string } | null} */ export function getRenderedTemplate(type, channel, data = {}, locale = 'en') { const tmpl = getRawTemplate(type, channel, locale);