From 339fa6045591a7e03ddb718301fe62823c26f9c3 Mon Sep 17 00:00:00 2001 From: kitadmin Date: Thu, 30 Apr 2026 01:02:43 +0000 Subject: [PATCH] feat: mailer with magic link email (allegiance pattern, MorseQuest branding) --- server/mailer.js | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 server/mailer.js diff --git a/server/mailer.js b/server/mailer.js new file mode 100644 index 0000000..9bbe562 --- /dev/null +++ b/server/mailer.js @@ -0,0 +1,62 @@ +'use strict' +const nodemailer = require('nodemailer') + +const SMTP_HOST = process.env.SMTP_HOST || '' +const SMTP_PORT = parseInt(process.env.SMTP_PORT || '587', 10) +const SMTP_USER = process.env.SMTP_USER || '' +const SMTP_PASS = process.env.SMTP_PASS || '' +const APP_URL = (process.env.APP_URL || 'http://localhost:3001').replace(/\/$/, '') +const FROM_ADDR = SMTP_USER + ? `"MorseQuest" <${SMTP_USER}>` + : '"MorseQuest" ' + +let transport = null +if (SMTP_HOST && SMTP_USER && SMTP_PASS) { + transport = nodemailer.createTransport({ + host: SMTP_HOST, + port: SMTP_PORT, + secure: SMTP_PORT === 465, + auth: { user: SMTP_USER, pass: SMTP_PASS }, + }) + console.log(`[mailer] SMTP configured: ${SMTP_HOST}:${SMTP_PORT}`) +} else { + console.warn('[mailer] SMTP not configured — magic links will be logged only') +} + +async function send(to, subject, html) { + if (!transport) { + console.log(`[mailer] Would send to ${to}: ${subject}`) + return + } + await transport.sendMail({ from: FROM_ADDR, to, subject, html }) +} + +async function sendMagicLink(email, token) { + const link = `${APP_URL}/api/auth/verify?token=${token}` + const subject = 'Your MorseQuest Login Link' + const html = ` +
+

+ ⚡ MorseQuest Login +

+

+ Click the button below to log in to MorseQuest. This link is valid for 24 hours + and can only be used once. +

+
+ + Begin Your Quest › + +
+

+ If you didn't request this link, you can ignore this email safely. +

+
` + await send(email, subject, html) +} + +module.exports = { sendMagicLink }