127 lines
4.9 KiB
JavaScript
127 lines
4.9 KiB
JavaScript
const nodemailer = require('nodemailer');
|
|
const { all, audit, now, run } = require('../db');
|
|
const { logEvent } = require('../logger');
|
|
|
|
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();
|
|
try {
|
|
const info = await transport.sendMail({
|
|
from: c.from || c.user,
|
|
to: recipients.join(', '),
|
|
subject,
|
|
html,
|
|
text
|
|
});
|
|
logEvent('smtp', `Email sent: "${subject}" to ${recipients.length} recipient(s)`, { subject, recipients: recipients.length, host: c.host, messageId: info.messageId });
|
|
return info;
|
|
} catch (error) {
|
|
logEvent('smtp', `Email send failed: "${subject}" — ${error.message}`, { subject, recipients: recipients.length, host: c.host }, 'error');
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function sendTestEmail(to, userId) {
|
|
const info = await sendMail({
|
|
to: to ? [to] : null,
|
|
subject: 'DepreCore test email',
|
|
html: '<p>Your DepreCore SMTP settings are working. This is a test message.</p>',
|
|
text: 'Your DepreCore 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 };
|