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: `

${escapeHtml(message.source)}

${escapeHtml(message.title)}

${escapeHtml(message.body)}

${message.link ? `OPEN SHD.BUILD` : ''}
` }); 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'}.`);