let nodemailer = null; try { nodemailer = require("nodemailer"); } catch { nodemailer = null; } async function createPlugin(ctx) { let transporter = null; let transportKey = ""; function readTransportConfig() { const relaySetting = typeof ctx.getPluginSetting === "function" ? (key, fallback = null) => ctx.getPluginSetting("rms.auth.smtp_relay", key, fallback) : (_key, fallback = null) => fallback; const host = String( relaySetting("host", ctx.getSetting("host", ctx.env.SMTP_HOST || "")) ).trim(); const portRaw = Number( relaySetting("port", ctx.getSetting("port", ctx.env.SMTP_PORT || 587)) ); const secure = String( relaySetting("secure", ctx.getSetting("secure", ctx.env.SMTP_SECURE || "false")) ) === "true"; const authUser = String( relaySetting("authUser", ctx.getSetting("authUser", ctx.env.SMTP_USER || "")) ).trim(); const authPass = String( relaySetting("authPass", ctx.getSetting("authPass", ctx.env.SMTP_PASS || "")) ).trim(); const allowInvalidCert = String( relaySetting("allowInvalidCert", ctx.getSetting("allowInvalidCert", ctx.env.SMTP_ALLOW_INVALID_CERT || "false")) ) === "true"; const from = String( relaySetting("from", ctx.getSetting("from", ctx.env.SMTP_FROM || "noreply@arcg.at")) ).trim() || "noreply@arcg.at"; const replyTo = String( relaySetting("replyTo", ctx.getSetting("replyTo", ctx.env.SMTP_REPLY_TO || "")) ).trim(); return { host, port: Number.isFinite(portRaw) && portRaw > 0 ? portRaw : 587, secure, authUser, authPass, allowInvalidCert, from, replyTo }; } async function deliverViaSmtp(entry) { const config = readTransportConfig(); if (!config.host || !nodemailer) { return false; } const key = JSON.stringify(config); if (!transporter || transportKey !== key) { transporter = nodemailer.createTransport({ host: config.host, port: config.port, secure: config.secure, auth: config.authUser ? { user: config.authUser, pass: config.authPass } : undefined, tls: config.allowInvalidCert ? { rejectUnauthorized: false } : undefined }); transportKey = key; } await transporter.sendMail({ from: entry.from, to: entry.to, replyTo: entry.replyTo || undefined, subject: entry.subject, text: entry.text, html: entry.html || undefined }); return true; } return { async execute(action, input) { if (action !== "send_challenge") { throw new Error(`Unknown action: ${action}`); } const payload = input && input.payload ? input.payload : {}; const recipient = input && input.recipient ? String(input.recipient) : ""; if (!recipient) { throw new Error("recipient missing"); } const transportConfig = readTransportConfig(); const entry = { at: new Date().toISOString(), via: "rms.auth.otp_email", to: recipient, from: transportConfig.from, replyTo: transportConfig.replyTo, subject: String(payload.subject || "ARCG OTP"), text: String(payload.text || ""), html: String(payload.html || "") }; let delivered = false; try { delivered = await deliverViaSmtp(entry); } catch (error) { entry.smtpError = String(error && error.message ? error.message : error); } entry.delivered = delivered; entry.transport = delivered ? "smtp" : "outbox-fallback"; await ctx.appendMailOutbox(entry); return { ok: true, delivered, transport: delivered ? "smtp" : "outbox-fallback" }; }, async health() { const config = readTransportConfig(); if (config.host && !nodemailer) { return { ok: false, message: "nodemailer nicht installiert" }; } return { ok: true }; } }; } module.exports = { createPlugin };