const nodemailer = require('nodemailer'); const { all, audit, now, run } = require('../db'); function readSettings() { const rows = all('SELECT key, value FROM app_settings'); return Object.fromEntries(rows.map((row) => [row.key, row.value])); } // Raw config (includes password) — for sending mail only. function getConfig() { const s = readSettings(); return { host: s.smtp_host || '', port: Number(s.smtp_port || 587), secure: s.smtp_secure === 'true', user: s.smtp_user || '', password: s.smtp_password || '', from: s.smtp_from || s.smtp_user || '', enabled: s.alerts_enabled === 'true', leadDays: Number(s.alerts_lead_days || 30), recipients: String(s.alerts_recipients || '').split(',').map((x) => x.trim()).filter(Boolean), webhookEnabled: s.webhook_enabled === 'true', webhookUrl: s.webhook_url || '', webhookSecret: s.webhook_secret || '' }; } // Safe config for the API/UI — password is never returned, only whether it is set. function publicConfig() { const c = getConfig(); return { smtp_host: c.host, smtp_port: c.port, smtp_secure: c.secure, smtp_user: c.user, smtp_from: c.from, smtp_password_set: Boolean(c.password), alerts_enabled: c.enabled, alerts_lead_days: c.leadDays, alerts_recipients: c.recipients.join(', '), configured: Boolean(c.host), webhook_enabled: c.webhookEnabled, webhook_url: c.webhookUrl, webhook_secret_set: Boolean(c.webhookSecret), webhook_configured: Boolean(c.webhookUrl) }; } function setValue(key, value) { run( 'INSERT INTO app_settings (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at', [key, String(value), now()] ); } function saveConfig(body, userId) { if (body.smtp_host !== undefined) setValue('smtp_host', body.smtp_host || ''); if (body.smtp_port !== undefined) setValue('smtp_port', Number(body.smtp_port) || 587); if (body.smtp_secure !== undefined) setValue('smtp_secure', body.smtp_secure ? 'true' : 'false'); if (body.smtp_user !== undefined) setValue('smtp_user', body.smtp_user || ''); if (body.smtp_from !== undefined) setValue('smtp_from', body.smtp_from || ''); if (body.smtp_password) setValue('smtp_password', body.smtp_password); // only overwrite when a new value is supplied if (body.alerts_enabled !== undefined) setValue('alerts_enabled', body.alerts_enabled ? 'true' : 'false'); if (body.alerts_lead_days !== undefined) setValue('alerts_lead_days', Number(body.alerts_lead_days) || 30); if (body.alerts_recipients !== undefined) setValue('alerts_recipients', body.alerts_recipients || ''); if (body.webhook_enabled !== undefined) setValue('webhook_enabled', body.webhook_enabled ? 'true' : 'false'); if (body.webhook_url !== undefined) setValue('webhook_url', body.webhook_url || ''); if (body.webhook_secret_clear) setValue('webhook_secret', ''); else if (body.webhook_secret) setValue('webhook_secret', body.webhook_secret); // only overwrite when a new value is supplied audit(userId, 'update', 'notification_settings', 'smtp', null, { ...body, smtp_password: body.smtp_password ? '[set]' : undefined, webhook_secret: body.webhook_secret ? '[set]' : undefined }); return publicConfig(); } function getTransport() { const c = getConfig(); if (!c.host) { throw Object.assign(new Error('SMTP host is not configured'), { status: 400 }); } return nodemailer.createTransport({ host: c.host, port: c.port, secure: c.secure, auth: c.user ? { user: c.user, pass: c.password } : undefined }); } async function sendMail({ to, subject, html, text }) { const c = getConfig(); const recipients = to ? (Array.isArray(to) ? to : [to]) : c.recipients; if (!recipients.length) { throw Object.assign(new Error('No recipients are configured'), { status: 400 }); } const transport = getTransport(); return transport.sendMail({ from: c.from || c.user, to: recipients.join(', '), subject, html, text }); } async function sendTestEmail(to, userId) { const info = await sendMail({ to: to ? [to] : null, subject: 'MixedAssets test email', html: '
Your MixedAssets SMTP settings are working. This is a test message.
', text: 'Your MixedAssets SMTP settings are working. This is a test message.' }); audit(userId, 'send', 'test_email', null, null, { to }); return { messageId: info.messageId, accepted: info.accepted }; } module.exports = { getConfig, publicConfig, saveConfig, sendMail, sendTestEmail };