67 lines
3.4 KiB
JavaScript
67 lines
3.4 KiB
JavaScript
import { connect } from '@tursodatabase/database';
|
|
import nodemailer from 'nodemailer';
|
|
import { randomUUID } from 'node:crypto';
|
|
import { resolve } from 'node:path';
|
|
|
|
const database = await connect(resolve(process.env.TDR_DATABASE_PATH || './data/shd-build.db'));
|
|
const now = new Date().toISOString();
|
|
const due = await (await database.prepare(`SELECT delivery.id AS delivery_id,delivery.user_id,message.*
|
|
FROM automated_system_message_deliveries delivery
|
|
JOIN automated_system_messages message ON message.id=delivery.message_id
|
|
WHERE delivery.delivered_at IS NULL AND delivery.scheduled_for<=? AND message.enabled=1
|
|
ORDER BY delivery.scheduled_for LIMIT 250`)).all(now);
|
|
const smtpConfigured = Boolean(process.env.SMTP_HOST && process.env.SMTP_FROM);
|
|
const transport = smtpConfigured ? nodemailer.createTransport({
|
|
host: process.env.SMTP_HOST,
|
|
port: Number(process.env.SMTP_PORT || 587),
|
|
secure: process.env.SMTP_SECURE === 'true',
|
|
auth: process.env.SMTP_USER ? { user: process.env.SMTP_USER, pass: process.env.SMTP_PASSWORD } : undefined,
|
|
requireTLS: process.env.SMTP_SECURE !== 'true',
|
|
disableFileAccess: true,
|
|
disableUrlAccess: true
|
|
}) : null;
|
|
const origin = (process.env.APP_ORIGIN || 'http://localhost:5173').replace(/\/+$/, '');
|
|
const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (character) => ({
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
})[character]);
|
|
let delivered = 0;
|
|
|
|
for (const message of due) {
|
|
const notificationId = randomUUID();
|
|
await (await database.prepare(`INSERT INTO notifications
|
|
(id,user_id,category,source,title,body,link,actions_json,mandatory,created_at)
|
|
VALUES(?,?,'isac',?,?,?,?,?,1,?)`)).run(
|
|
notificationId, message.user_id, message.source, message.title, message.body,
|
|
message.link ?? null, message.actions_json ?? null, now
|
|
);
|
|
await (await database.prepare(`UPDATE automated_system_message_deliveries
|
|
SET delivered_at=?,notification_id=? WHERE id=? AND delivered_at IS NULL`))
|
|
.run(now, notificationId, message.delivery_id);
|
|
const preference = await (await database.prepare(
|
|
"SELECT email_frequency FROM notification_preferences WHERE user_id=? AND category='isac'"
|
|
)).get(message.user_id);
|
|
if (transport && preference?.email_frequency === 'immediate') {
|
|
const identity = await (await database.prepare('SELECT email FROM user_emails WHERE user_id=?')).get(message.user_id);
|
|
if (typeof identity?.email === 'string') {
|
|
try {
|
|
await transport.sendMail({
|
|
from: process.env.SMTP_FROM,
|
|
to: identity.email,
|
|
subject: `SHD.build · ${message.title}`,
|
|
text: `${message.body}\n\n${message.link ? `${origin}${message.link}` : origin}`,
|
|
html: `<div style="background:#0a0c0d;color:#ecebe5;padding:28px;font-family:Arial,sans-serif"><p style="color:#f6a821;font-size:12px;letter-spacing:2px">${escapeHtml(message.source)}</p><h1>${escapeHtml(message.title)}</h1><p style="color:#a5aaab">${escapeHtml(message.body)}</p>${message.link ? `<a style="color:#f6a821" href="${escapeHtml(`${origin}${message.link}`)}">OPEN SHD.BUILD</a>` : ''}</div>`
|
|
});
|
|
await (await database.prepare('UPDATE notifications SET emailed_at=? WHERE id=?')).run(now, notificationId);
|
|
} catch (error) {
|
|
console.error(`Unable to email automated notification ${notificationId}`, error);
|
|
}
|
|
}
|
|
}
|
|
delivered += 1;
|
|
}
|
|
console.log(`Delivered ${delivered} automated ISAC system notification${delivered === 1 ? '' : 's'}.`);
|