rda-v3/functions/api/contact.ts
2025-12-22 12:25:30 +00:00

116 lines
3 KiB
TypeScript

type Env = {
TURNSTILE_SECRET_KEY?: string;
RESEND_API_KEY?: string;
CONTACT_FROM_EMAIL?: string;
CONTACT_TO_EMAIL?: string;
};
const jsonResponse = (data: Record<string, unknown>, status = 200) =>
new Response(JSON.stringify(data), {
status,
headers: { "content-type": "application/json" },
});
const readFormField = (value: FormDataEntryValue | null) =>
typeof value === "string" ? value.trim() : "";
export const onRequestPost: PagesFunction<Env> = async ({
request,
env,
}) => {
const formData = await request.formData();
const name = readFormField(formData.get("name"));
const email = readFormField(formData.get("email"));
const message = readFormField(formData.get("message"));
const turnstileToken = readFormField(
formData.get("cf-turnstile-response")
);
if (!name || !email || !message) {
return jsonResponse(
{ ok: false, error: "Missing required fields." },
400
);
}
if (!env.TURNSTILE_SECRET_KEY) {
return jsonResponse(
{ ok: false, error: "Turnstile secret key not configured." },
500
);
}
if (!turnstileToken) {
return jsonResponse(
{ ok: false, error: "Turnstile verification failed." },
400
);
}
const verifyBody = new URLSearchParams({
secret: env.TURNSTILE_SECRET_KEY,
response: turnstileToken,
remoteip: request.headers.get("CF-Connecting-IP") ?? "",
});
const verifyResponse = await fetch(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
{
method: "POST",
body: verifyBody,
}
);
const verifyResult = (await verifyResponse.json()) as {
success?: boolean;
};
if (!verifyResult.success) {
return jsonResponse(
{ ok: false, error: "Turnstile verification failed." },
400
);
}
if (!env.RESEND_API_KEY) {
return jsonResponse(
{ ok: false, error: "Email provider not configured." },
500
);
}
const fromEmail = env.CONTACT_FROM_EMAIL;
if (!fromEmail) {
return jsonResponse(
{ ok: false, error: "Sender email not configured." },
500
);
}
const toEmail = env.CONTACT_TO_EMAIL ?? "calum@muir.in";
const emailPayload = {
from: fromEmail,
to: [toEmail],
reply_to: email,
subject: "New contact form submission",
text: `Name: ${name}\nEmail: ${email}\n\n${message}`,
};
const emailResponse = 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 (!emailResponse.ok) {
return jsonResponse(
{ ok: false, error: "Failed to send email." },
502
);
}
return jsonResponse({ ok: true });
};