45f1e0e7f6
- mailer.js: add requireTLS to force STARTTLS before AUTH on internal relay
- docker-compose.yml: use network_mode:bridge to reach 172.17.0.1:2587 SMTP relay
and switch to ${SMTP_HOST:-172.17.0.1}/${SMTP_PORT:-2587} defaults
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
68 lines
2.5 KiB
JavaScript
68 lines
2.5 KiB
JavaScript
'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" <noreply@morsequest.keylinkit.net>'
|
|
|
|
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 },
|
|
requireTLS: true, // force STARTTLS before AUTH
|
|
connectionTimeout: 10_000,
|
|
greetingTimeout: 10_000,
|
|
socketTimeout: 15_000,
|
|
tls: { rejectUnauthorized: false }, // relay cert is for mail.keylinkit.net, not 172.17.0.1
|
|
})
|
|
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 = `
|
|
<div style="font-family:'Open Sans',sans-serif;max-width:480px;margin:0 auto;
|
|
padding:2rem;background:#F5E6D3;border:2px solid #D4AF37">
|
|
<h1 style="font-family:'Cinzel',serif;color:#3C2415;font-size:1.4rem;margin-bottom:1rem">
|
|
⚡ MorseQuest Login
|
|
</h1>
|
|
<p style="color:#3C2415;line-height:1.6">
|
|
Click the button below to log in to MorseQuest. This link is valid for 24 hours
|
|
and can only be used once.
|
|
</p>
|
|
<div style="text-align:center;margin:2rem 0">
|
|
<a href="${link}"
|
|
style="display:inline-block;padding:0.8rem 2rem;background:#D4AF37;
|
|
color:#3C2415;text-decoration:none;font-family:'Cinzel',serif;
|
|
font-size:1rem;font-weight:600;border:2px solid #3C2415">
|
|
Begin Your Quest ›
|
|
</a>
|
|
</div>
|
|
<p style="color:#654321;font-size:0.85rem;line-height:1.5">
|
|
If you didn't request this link, you can ignore this email safely.
|
|
</p>
|
|
</div>`
|
|
await send(email, subject, html)
|
|
}
|
|
|
|
module.exports = { sendMagicLink }
|