From 3df163b65659d70a9381e84deec93a2c15e0c0fb Mon Sep 17 00:00:00 2001 From: Calum Muir Date: Sun, 28 Dec 2025 12:29:46 +0000 Subject: [PATCH] Use Resend for contact form emails --- functions/api/contact.ts | 79 ++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 51 deletions(-) diff --git a/functions/api/contact.ts b/functions/api/contact.ts index 66626e2..bd6e5f7 100644 --- a/functions/api/contact.ts +++ b/functions/api/contact.ts @@ -1,6 +1,6 @@ type Env = { TURNSTILE_SECRET_KEY?: string; - AZURE_COMMUNICATION_CONNECTION_STRING?: string; + RESEND_API_KEY?: string; CONTACT_FROM_EMAIL?: string; CONTACT_TO_EMAIL?: string; }; @@ -14,23 +14,6 @@ const jsonResponse = (data: Record, status = 200) => const readFormField = (value: FormDataEntryValue | null) => typeof value === "string" ? value.trim() : ""; -const parseConnectionString = (connectionString: string) => { - const parts = connectionString.split(";").filter(Boolean); - const map = new Map(); - for (const part of parts) { - const [rawKey, ...rest] = part.split("="); - const key = rawKey?.trim().toLowerCase(); - const value = rest.join("=").trim(); - if (key && value) { - map.set(key, value); - } - } - return { - endpoint: map.get("endpoint") ?? "", - accessKey: map.get("accesskey") ?? map.get("access_key") ?? "", - }; -}; - const handlePost: PagesFunction = async ({ request, env }) => { const formData = await request.formData(); const name = readFormField(formData.get("name")); @@ -83,7 +66,7 @@ const handlePost: PagesFunction = async ({ request, env }) => { ); } - if (!env.AZURE_COMMUNICATION_CONNECTION_STRING) { + if (!env.RESEND_API_KEY) { return jsonResponse( { ok: false, error: "Email provider not configured." }, 500, @@ -100,47 +83,41 @@ const handlePost: PagesFunction = async ({ request, env }) => { const toEmail = env.CONTACT_TO_EMAIL ?? "calum@muir.in"; - const { endpoint, accessKey } = parseConnectionString( - env.AZURE_COMMUNICATION_CONNECTION_STRING, - ); - if (!endpoint || !accessKey) { - return jsonResponse( - { ok: false, error: "Email provider not configured." }, - 500, - ); - } - const emailPayload = { - senderAddress: fromEmail, - content: { - subject: "New contact form submission", - plainText: `Name: ${name}\nEmail: ${email}\n\n${message}`, - }, - recipients: { - to: [{ address: toEmail }], - }, - replyTo: [{ address: email }], + from: fromEmail, + to: [toEmail], + reply_to: email, + subject: "New contact form submission", + text: `Name: ${name}\nEmail: ${email}\n\n${message}`, }; - const apiBase = endpoint.replace(/\/$/, ""); - const response = await fetch( - apiBase + "/emails:send?api-version=2023-03-31", - { - method: "POST", - headers: { - "Content-Type": "application/json", - "api-key": accessKey, - }, - body: JSON.stringify(emailPayload), + const response = await fetch("https://api.resend.com/emails", { + method: "POST", + headers: { + Authorization: `Bearer ${env.RESEND_API_KEY}`, + "Content-Type": "application/json", }, - ); + body: JSON.stringify(emailPayload), + }); if (!response.ok) { - const errorText = await response.text(); + const contentType = response.headers.get("content-type") ?? ""; + const errorText = contentType.includes("application/json") + ? JSON.stringify(await response.json().catch(() => ({}))) + : await response.text(); + const errorCode = response.headers.get("x-ms-error-code"); + const details = [ + `${response.status} ${response.statusText}`.trim(), + errorCode ? `code=${errorCode}` : "", + errorText.trim(), + ] + .filter(Boolean) + .join(" | ") + .slice(0, 500); return jsonResponse( { ok: false, error: "Failed to send email.", - details: errorText.slice(0, 500), + details, }, 502, );