auth/src/email.js
jtricerolph f08d53d702 Add internal registry endpoint, self-registration, and workforce sync
- Register internalRoutes at /api/auth/internal (used by management)
- Add registerRoutes for staff self-registration flow
- Add workforce sync job (runs on schedule, fails gracefully if unconfigured)
- Add email.js for registration/invite emails

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 20:35:44 +00:00

61 lines
2.4 KiB
JavaScript

import nodemailer from 'nodemailer'
const SETTINGS_URL = process.env.SETTINGS_URL || 'http://10.10.10.106:3080'
const SETTINGS_SECRET = process.env.SETTINGS_SECRET || ''
let _smtpCache = null // { config, expires_at }
let _transporter = null
async function getSmtpConfig() {
if (_smtpCache && Date.now() < _smtpCache.expires_at) return _smtpCache.config
const res = await fetch(`${SETTINGS_URL}/settings/api/internal/integration/smtp`, {
headers: { Authorization: `Bearer ${SETTINGS_SECRET}` },
signal: AbortSignal.timeout(5000),
})
if (!res.ok) throw new Error(`Failed to fetch SMTP config from settings: ${res.status}`)
const config = await res.json()
if (!config.host) throw new Error('SMTP not configured in settings')
_smtpCache = { config, expires_at: Date.now() + 5 * 60_000 }
_transporter = null // force transporter rebuild on next send
return config
}
async function getTransporter() {
if (_transporter) return _transporter
const config = await getSmtpConfig()
const port = parseInt(config.port || '587')
_transporter = nodemailer.createTransport({
host: config.host,
port,
secure: port === 465,
auth: config.user ? { user: config.user, pass: config.pass } : undefined,
})
return _transporter
}
export async function sendPinEmail(to, name, pin) {
const config = await getSmtpConfig()
const transport = await getTransporter()
const from = config.from || '"Hotel Number Four" <noreply@hotelnumberfour.com>'
await transport.sendMail({
from,
to,
subject: 'Your Hotel Number Four verification code',
text: `Hi ${name},\n\nYour staff portal verification code is: ${pin}\n\nThis code expires in 15 minutes.\n\nIf you did not request this, please ignore this email.`,
html: `
<div style="font-family:sans-serif;max-width:480px;margin:0 auto;padding:2rem">
<h2 style="color:#1e3a5f;margin:0 0 1rem">Hotel Number Four</h2>
<p>Hi ${name},</p>
<p>Your staff portal verification code is:</p>
<div style="font-size:2.5rem;font-weight:700;letter-spacing:0.3em;color:#1e3a5f;
background:#f3f4f6;padding:1rem 2rem;border-radius:8px;
text-align:center;margin:1.5rem 0">${pin}</div>
<p style="color:#6b7280;font-size:0.875rem">
This code expires in 15 minutes.<br>
If you did not request access to the staff portal, please ignore this email.
</p>
</div>
`,
})
}