From 7eefc5d0087fcb361e73c222ddede333405fedfc Mon Sep 17 00:00:00 2001 From: Austin Bennett Date: Wed, 22 Jul 2026 18:12:45 -0500 Subject: [PATCH] contact: fan out enquiries to n8n hub (fire-and-forget) 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. --- .env.example | 5 +++++ src/app/api/contact/route.ts | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/.env.example b/.env.example index 6e52e62..6a1387b 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,8 @@ SMTP_TOKEN= CONTACT_TO=morissa@itsthevine.com # Envelope sender. Many SMTP providers require this to match SMTP_USERNAME or a verified domain. 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= diff --git a/src/app/api/contact/route.ts b/src/app/api/contact/route.ts index 226ef92..62664ee 100644 --- a/src/app/api/contact/route.ts +++ b/src/app/api/contact/route.ts @@ -4,6 +4,26 @@ import nodemailer from 'nodemailer'; // 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; +// 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) { if (!SMTP_SERVER || !SMTP_PORT) { 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 }); } + // Email delivered — fan out to the automation hub (fire-and-forget). + notifyHub({ name, email, message }); + return NextResponse.json({ ok: true }); }