Use Resend for contact form emails
This commit is contained in:
parent
29da1ccec7
commit
3df163b656
1 changed files with 28 additions and 51 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
type Env = {
|
type Env = {
|
||||||
TURNSTILE_SECRET_KEY?: string;
|
TURNSTILE_SECRET_KEY?: string;
|
||||||
AZURE_COMMUNICATION_CONNECTION_STRING?: string;
|
RESEND_API_KEY?: string;
|
||||||
CONTACT_FROM_EMAIL?: string;
|
CONTACT_FROM_EMAIL?: string;
|
||||||
CONTACT_TO_EMAIL?: string;
|
CONTACT_TO_EMAIL?: string;
|
||||||
};
|
};
|
||||||
|
|
@ -14,23 +14,6 @@ const jsonResponse = (data: Record<string, unknown>, status = 200) =>
|
||||||
const readFormField = (value: FormDataEntryValue | null) =>
|
const readFormField = (value: FormDataEntryValue | null) =>
|
||||||
typeof value === "string" ? value.trim() : "";
|
typeof value === "string" ? value.trim() : "";
|
||||||
|
|
||||||
const parseConnectionString = (connectionString: string) => {
|
|
||||||
const parts = connectionString.split(";").filter(Boolean);
|
|
||||||
const map = new Map<string, string>();
|
|
||||||
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<Env> = async ({ request, env }) => {
|
const handlePost: PagesFunction<Env> = async ({ request, env }) => {
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
const name = readFormField(formData.get("name"));
|
const name = readFormField(formData.get("name"));
|
||||||
|
|
@ -83,7 +66,7 @@ const handlePost: PagesFunction<Env> = async ({ request, env }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!env.AZURE_COMMUNICATION_CONNECTION_STRING) {
|
if (!env.RESEND_API_KEY) {
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
{ ok: false, error: "Email provider not configured." },
|
{ ok: false, error: "Email provider not configured." },
|
||||||
500,
|
500,
|
||||||
|
|
@ -100,47 +83,41 @@ const handlePost: PagesFunction<Env> = async ({ request, env }) => {
|
||||||
|
|
||||||
const toEmail = env.CONTACT_TO_EMAIL ?? "calum@muir.in";
|
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 = {
|
const emailPayload = {
|
||||||
senderAddress: fromEmail,
|
from: fromEmail,
|
||||||
content: {
|
to: [toEmail],
|
||||||
subject: "New contact form submission",
|
reply_to: email,
|
||||||
plainText: `Name: ${name}\nEmail: ${email}\n\n${message}`,
|
subject: "New contact form submission",
|
||||||
},
|
text: `Name: ${name}\nEmail: ${email}\n\n${message}`,
|
||||||
recipients: {
|
|
||||||
to: [{ address: toEmail }],
|
|
||||||
},
|
|
||||||
replyTo: [{ address: email }],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiBase = endpoint.replace(/\/$/, "");
|
const response = await fetch("https://api.resend.com/emails", {
|
||||||
const response = await fetch(
|
method: "POST",
|
||||||
apiBase + "/emails:send?api-version=2023-03-31",
|
headers: {
|
||||||
{
|
Authorization: `Bearer ${env.RESEND_API_KEY}`,
|
||||||
method: "POST",
|
"Content-Type": "application/json",
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"api-key": accessKey,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(emailPayload),
|
|
||||||
},
|
},
|
||||||
);
|
body: JSON.stringify(emailPayload),
|
||||||
|
});
|
||||||
if (!response.ok) {
|
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(
|
return jsonResponse(
|
||||||
{
|
{
|
||||||
ok: false,
|
ok: false,
|
||||||
error: "Failed to send email.",
|
error: "Failed to send email.",
|
||||||
details: errorText.slice(0, 500),
|
details,
|
||||||
},
|
},
|
||||||
502,
|
502,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue