import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import express from 'express'; import nodemailer from 'nodemailer'; const DEFAULT_APP_PORT = 8080; const DEFAULT_API_ONLY_PORT = 3013; const DEFAULT_CONTACT_EMAIL = 'info@bayareaaffiliates.com'; const SMTP_HOST = 'email-smtp.us-east-2.amazonaws.com'; const SMTP_PORT = 587; const REQUIRED_FIELDS = ['name', 'email', 'message']; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const distDir = path.join(__dirname, 'dist'); function getFlag(name) { return process.argv.includes(name); } function getOption(name) { const entry = process.argv.find((arg) => arg.startsWith(`${name}=`)); return entry ? entry.slice(name.length + 1) : undefined; } function getPort(apiOnly) { const portValue = getOption('--port') || process.env.PORT || String(apiOnly ? DEFAULT_API_ONLY_PORT : DEFAULT_APP_PORT); const port = Number.parseInt(portValue, 10); if (Number.isNaN(port)) { throw new Error(`Invalid port value: ${portValue}`); } return port; } function trimField(value) { return typeof value === 'string' ? value.trim() : ''; } function sanitizeHeaderValue(value) { return value.replace(/[\r\n]+/g, ' ').trim(); } function validatePayload(payload) { if (!payload || typeof payload !== 'object') { return { ok: false, error: 'Invalid request payload.' }; } const data = { name: trimField(payload.name).slice(0, 120), email: trimField(payload.email).slice(0, 160), phone: trimField(payload.phone).slice(0, 80), company: trimField(payload.company).slice(0, 120), message: trimField(payload.message).slice(0, 4000), website: trimField(payload.website).slice(0, 160), }; for (const field of REQUIRED_FIELDS) { if (!data[field]) { return { ok: false, error: 'Please fill out all required fields.' }; } } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) { return { ok: false, error: 'Please provide a valid email address.' }; } return { ok: true, data }; } function createTransporter() { const user = process.env.AMAZON_USER; const pass = process.env.AMAZON_PASSWORD; if (!user || !pass) { return null; } return nodemailer.createTransport({ host: SMTP_HOST, secure: false, port: SMTP_PORT, auth: { user, pass, }, }); } function buildMessageBody(data) { return [ 'New website inquiry', '', `Name: ${data.name}`, `Email: ${data.email}`, `Phone: ${data.phone || 'Not provided'}`, `Company: ${data.company || 'Not provided'}`, '', 'Message:', data.message, ].join('\n'); } function resolveRouteIndex(requestPath) { const relativePath = requestPath.replace(/^\/+/, ''); if (!relativePath) { return path.join(distDir, 'index.html'); } return path.join(distDir, relativePath, 'index.html'); } const apiOnly = getFlag('--api-only'); const port = getPort(apiOnly); const contactToEmail = process.env.CONTACT_TO_EMAIL || DEFAULT_CONTACT_EMAIL; const contactFromEmail = process.env.CONTACT_FROM_EMAIL || contactToEmail; const app = express(); app.disable('x-powered-by'); app.use(express.json({ limit: '32kb' })); app.post('/api/contact', async (req, res) => { const validation = validatePayload(req.body); if (!validation.ok) { return res.status(400).json({ ok: false, error: validation.error }); } const { data } = validation; if (data.website) { return res.status(400).json({ ok: false, error: 'Invalid submission.' }); } const transporter = createTransporter(); if (!transporter) { return res.status(503).json({ ok: false, error: 'Email service is not configured right now. Please email us directly.', }); } try { await transporter.sendMail({ from: contactFromEmail, to: contactToEmail, replyTo: data.email, subject: sanitizeHeaderValue(`Website inquiry from ${data.name}`), text: buildMessageBody(data), }); return res.json({ ok: true }); } catch (error) { console.error('Contact form delivery failed.', error); return res.status(500).json({ ok: false, error: 'Unable to send your message right now. Please call or email us directly.', }); } }); if (!apiOnly) { app.use(express.static(distDir, { index: 'index.html', redirect: false })); app.get(/^(?!\/api\/).*/, (req, res) => { const routeIndex = resolveRouteIndex(req.path); const fallbackIndex = path.join(distDir, 'index.html'); if (fs.existsSync(routeIndex)) { return res.sendFile(routeIndex); } return res.sendFile(fallbackIndex); }); } app.listen(port, () => { if (!process.env.AMAZON_USER || !process.env.AMAZON_PASSWORD) { console.warn('Amazon SES SMTP credentials are missing. /api/contact will return 503 until configured.'); } console.log(`Bay Area IT server listening on port ${port}${apiOnly ? ' (API only)' : ''}.`); });