From 79d5ac76c0e0278d9242862f14e2bbd4e11b0807 Mon Sep 17 00:00:00 2001 From: Remi <73385395+itsrubberduck@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:44:41 +0200 Subject: [PATCH] Revise landing messaging and add news feed --- server/utils/notifications.ts | 103 ++++++++++++++++++++++++++++++++++ types/nodemailer.d.ts | 5 ++ 2 files changed, 108 insertions(+) create mode 100644 server/utils/notifications.ts create mode 100644 types/nodemailer.d.ts diff --git a/server/utils/notifications.ts b/server/utils/notifications.ts new file mode 100644 index 0000000..8eaf7d8 --- /dev/null +++ b/server/utils/notifications.ts @@ -0,0 +1,103 @@ +const ADMIN_EMAIL_FALLBACK = 'opensquawk@faktorxmensch.com' + +const RESEND_ENDPOINT = 'https://api.resend.com/emails' + +async function sendViaResend(subject: string, text: string) { + const apiKey = process.env.NOTIFY_RESEND_API_KEY + if (!apiKey) { + return false + } + + const to = process.env.NOTIFY_EMAIL_TO || ADMIN_EMAIL_FALLBACK + const from = process.env.NOTIFY_EMAIL_FROM || 'OpenSquawk ' + + try { + const response = await fetch(RESEND_ENDPOINT, { + method: 'POST', + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + from, + to, + subject, + text, + }), + }) + + if (!response.ok) { + const errorText = await response.text().catch(() => '') + console.error('Failed to send notification via Resend API', response.status, errorText) + return false + } + + return true + } catch (error) { + console.error('Error while sending notification via Resend API', error) + return false + } +} + +async function sendViaSmtp(subject: string, text: string) { + const host = process.env.NOTIFY_SMTP_HOST + if (!host) { + return false + } + + const user = process.env.NOTIFY_SMTP_USER + const pass = process.env.NOTIFY_SMTP_PASS + if (!user || !pass) { + console.warn('SMTP notification is configured without credentials – skipping send.') + return false + } + + const port = Number.parseInt(process.env.NOTIFY_SMTP_PORT || '', 10) + const secure = process.env.NOTIFY_SMTP_SECURE === 'true' + + let nodemailer: any = null + try { + const module = await import('nodemailer') + nodemailer = module.default ?? module + } catch (error) { + console.warn('nodemailer is not available. Skipping SMTP notification.', error) + return false + } + + const to = process.env.NOTIFY_EMAIL_TO || ADMIN_EMAIL_FALLBACK + const from = process.env.NOTIFY_EMAIL_FROM || 'OpenSquawk ' + + try { + const transporter = nodemailer.createTransport({ + host, + port: Number.isNaN(port) ? secure ? 465 : 587 : port, + secure, + auth: { + user, + pass, + }, + }) + + await transporter.sendMail({ from, to, subject, text }) + return true + } catch (error) { + console.error('Failed to send notification via SMTP', error) + return false + } +} + +export async function sendAdminNotification(subject: string, text: string) { + const sentViaResend = await sendViaResend(subject, text) + if (sentViaResend) { + return true + } + + const sentViaSmtp = await sendViaSmtp(subject, text) + if (sentViaSmtp) { + return true + } + + const to = process.env.NOTIFY_EMAIL_TO || ADMIN_EMAIL_FALLBACK + console.info(`[notify:fallback] ${subject}\nEmpfänger: ${to}\n${text}`) + return false +} diff --git a/types/nodemailer.d.ts b/types/nodemailer.d.ts new file mode 100644 index 0000000..88cd8b0 --- /dev/null +++ b/types/nodemailer.d.ts @@ -0,0 +1,5 @@ +declare module 'nodemailer' { + const nodemailer: any + export function createTransport(options: any): any + export default nodemailer +}