Verstuur uitnodigingen voor experts via SendGrid, zoals het ZRM-platform

- lib/mail.ts gebruikt SendGrid met dezelfde variabelen als het Rikx
  ZRM-platform (SENDGRID_API_KEY, SENDGRID_FROM_EMAIL, SENDGRID_FROM_NAME);
  SMTP blijft beschikbaar als alternatief
- Uitnodigingsmail krijgt ook een HTML-versie met knop
- Zonder SendGrid of SMTP blijft de pagina Experts de link tonen
- .env.example en CLAUDE.md bijgewerkt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 15:05:45 +02:00
parent 53bf667f54
commit f9e4a1aa5d
4 changed files with 56 additions and 10 deletions
+33 -5
View File
@@ -1,12 +1,39 @@
import nodemailer from "nodemailer";
// E-mail via SMTP (zie SMTP_* in CLAUDE.md). Zonder SMTP_HOST wordt er niets verstuurd;
// de aanroeper toont dan bijvoorbeeld de uitnodigingslink in beeld.
// E-mail versturen. Voorkeur: SendGrid, met dezelfde instellingen als het Rikx ZRM-platform
// (SENDGRID_API_KEY, SENDGRID_FROM_EMAIL, SENDGRID_FROM_NAME). Anders SMTP (SMTP_*).
// Zonder beide wordt er niets verstuurd; de aanroeper toont dan bijvoorbeeld de uitnodigingslink in beeld.
export function isMailConfigured(): boolean {
return Boolean(process.env.SMTP_HOST);
return Boolean(process.env.SENDGRID_API_KEY || process.env.SMTP_HOST);
}
export async function sendMail(message: { to: string; subject: string; text: string; html?: string }): Promise<void> {
type Message = { to: string; toName?: string; subject: string; text: string; html?: string };
export async function sendMail(message: Message): Promise<void> {
if (process.env.SENDGRID_API_KEY) return sendViaSendGrid(message, process.env.SENDGRID_API_KEY);
return sendViaSmtp(message);
}
async function sendViaSendGrid(message: Message, apiKey: string): Promise<void> {
const res = await fetch("https://api.sendgrid.com/v3/mail/send", {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({
personalizations: [{ to: [{ email: message.to, name: message.toName }], subject: message.subject }],
from: {
email: process.env.SENDGRID_FROM_EMAIL || "no-reply@rikxplatform.nl",
name: process.env.SENDGRID_FROM_NAME || "Rikx",
},
content: [
{ type: "text/plain", value: message.text },
...(message.html ? [{ type: "text/html", value: message.html }] : []),
],
}),
});
if (!res.ok) throw new Error(`SendGrid ${res.status}: ${await res.text()}`);
}
async function sendViaSmtp(message: Message): Promise<void> {
const port = Number(process.env.SMTP_PORT || 587);
const transport = nodemailer.createTransport({
host: process.env.SMTP_HOST,
@@ -14,5 +41,6 @@ export async function sendMail(message: { to: string; subject: string; text: str
secure: port === 465,
auth: process.env.SMTP_USER ? { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS } : undefined,
});
await transport.sendMail({ from: process.env.SMTP_FROM || "noreply@rikx.nl", ...message });
const { toName: _toName, ...rest } = message;
await transport.sendMail({ from: process.env.SMTP_FROM || "noreply@rikx.nl", ...rest });
}