contact: fan out enquiries to n8n hub (fire-and-forget)
build-and-publish / build (push) Successful in 8s

Keeps the direct SMTP email as the reliable delivery path; when CONTACT_HUB_URL
is set, also POSTs the enquiry to the n8n webhook so the hub sends the customer
auto-reply and (later) creates CRM/task records. Best-effort with a 4s timeout,
so a slow or down hub never blocks or fails the form.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
2026-07-22 18:12:45 -05:00
co-authored by Claude Opus 4.8
parent c88bd3bc98
commit 69e67182a1
2 changed files with 28 additions and 0 deletions
+5
View File
@@ -9,3 +9,8 @@ SMTP_TOKEN=
CONTACT_TO=[email protected] CONTACT_TO=[email protected]
# Envelope sender. Many SMTP providers require this to match SMTP_USERNAME or a verified domain. # Envelope sender. Many SMTP providers require this to match SMTP_USERNAME or a verified domain.
CONTACT_FROM= CONTACT_FROM=
# Optional: automation hub (n8n) webhook. When set, each enquiry is also POSTed here
# (fire-and-forget) so the hub can send the customer auto-reply and create CRM/task
# records. The direct email above stays the reliable path; leave blank to disable.
CONTACT_HUB_URL=
+23
View File
@@ -4,6 +4,26 @@ import nodemailer from 'nodemailer';
// Server-side only, so the SMTP credentials never reach the browser. // Server-side only, so the SMTP credentials never reach the browser.
const { SMTP_SERVER, SMTP_PORT, SMTP_USERNAME, SMTP_TOKEN, CONTACT_TO, CONTACT_FROM } = process.env; const { SMTP_SERVER, SMTP_PORT, SMTP_USERNAME, SMTP_TOKEN, CONTACT_TO, CONTACT_FROM } = process.env;
// Optional automation hub (n8n). When set, we also POST the enquiry here so it can
// send the customer auto-reply and (later) create a CRM lead / task. Best-effort:
// the direct email above is the reliable path, so a slow or down hub never blocks
// — or fails — the contact form.
const CONTACT_HUB_URL = process.env.CONTACT_HUB_URL;
function notifyHub(payload: { name: string; email: string; message: string }) {
if (!CONTACT_HUB_URL) return;
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 4000);
fetch(CONTACT_HUB_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
signal: controller.signal,
})
.catch((err) => console.error('contact: hub notify failed (non-fatal)', err))
.finally(() => clearTimeout(timeout));
}
export async function POST(request: Request) { export async function POST(request: Request) {
if (!SMTP_SERVER || !SMTP_PORT) { if (!SMTP_SERVER || !SMTP_PORT) {
console.error('contact: SMTP env vars missing'); console.error('contact: SMTP env vars missing');
@@ -55,5 +75,8 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Could not send the message.' }, { status: 502 }); return NextResponse.json({ error: 'Could not send the message.' }, { status: 502 });
} }
// Email delivered — fan out to the automation hub (fire-and-forget).
notifyHub({ name, email, message });
return NextResponse.json({ ok: true }); return NextResponse.json({ ok: true });
} }