1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-10-28 23:55:50 -04:00

convert APIs to edge functions (#1648)

This commit is contained in:
2023-06-25 11:26:29 -04:00
committed by GitHub
parent 62419f1380
commit aa64279dd8
10 changed files with 305 additions and 429 deletions

View File

@@ -1,6 +1,7 @@
import { NextResponse } from "next/server";
import queryString from "query-string";
import { logServerError } from "../../lib/helpers/sentry";
import type { NextApiRequest, NextApiResponse } from "next";
import { baseUrl } from "../../lib/config";
import type { NextRequest } from "next/server";
// fallback to dummy secret for testing: https://docs.hcaptcha.com/#integration-testing-test-keys
const HCAPTCHA_SITE_KEY =
@@ -11,56 +12,55 @@ const HCAPTCHA_API_ENDPOINT = "https://hcaptcha.com/siteverify";
const { AIRTABLE_API_KEY, AIRTABLE_BASE } = process.env;
const AIRTABLE_API_ENDPOINT = "https://api.airtable.com/v0/";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
// disable caching on both ends
res.setHeader("Cache-Control", "private, no-cache, no-store, must-revalidate");
res.setHeader("Pragma", "no-cache");
export const config = {
runtime: "edge",
};
// redirect GET requests to this endpoint to the contact form itself
if (req.method === "GET") {
return res.redirect(302, "/contact/");
}
const { body } = req;
// these are both backups to client-side validations just in case someone squeezes through without them. the codes
// are identical so they're caught in the same fashion.
if (!body.name || !body.email || !body.message) {
// all fields are required
throw new Error("USER_MISSING_DATA");
}
if (!body["h-captcha-response"] || !(await validateCaptcha(body["h-captcha-response"]))) {
// either the captcha is wrong or completely missing
throw new Error("USER_INVALID_CAPTCHA");
}
// sent directly to airtable
const airtableResult = await sendToAirtable({
Name: body.name,
Email: body.email,
Message: body.message,
});
// throw an internal error, not user's fault
if (airtableResult !== true) {
throw new Error("AIRTABLE_API_ERROR");
}
// success! let the client know
return res.status(200).json({ success: true });
} catch (error) {
// extract just the error message to send back to client
const message = error instanceof Error ? error.message : "UNKNOWN_EXCEPTION";
// log errors (except PEBCAK) to console and sentry
if (!message.startsWith("USER_")) {
await logServerError(error);
}
// 500 Internal Server Error
return res.status(500).json({ success: false, message });
// eslint-disable-next-line import/no-anonymous-default-export
export default async (req: NextRequest) => {
// redirect GET requests to this endpoint to the contact form itself
if (req.method === "GET") {
return NextResponse.redirect(`${baseUrl}/contact/`);
}
// possible weirdness? https://github.com/orgs/vercel/discussions/78#discussioncomment-5089059
const data = await req.json();
// these are both backups to client-side validations just in case someone squeezes through without them. the codes
// are identical so they're caught in the same fashion.
if (!data.name || !data.email || !data.message) {
// all fields are required
throw new Error("MISSING_DATA");
}
if (!data["h-captcha-response"] || !(await validateCaptcha(data["h-captcha-response"]))) {
// either the captcha is wrong or completely missing
throw new Error("INVALID_CAPTCHA");
}
// sent directly to airtable
const airtableResult = await sendToAirtable({
Name: data.name,
Email: data.email,
Message: data.message,
});
// throw an internal error, not user's fault
if (airtableResult !== true) {
throw new Error("AIRTABLE_API_ERROR");
}
// success! let the client know
return NextResponse.json(
{ success: true },
{
status: 201,
headers: {
// disable caching on both ends. see:
// https://vercel.com/docs/concepts/functions/edge-functions/edge-caching
"Cache-Control": "private, no-cache, no-store, must-revalidate",
},
}
);
};
const validateCaptcha = async (formResponse: unknown): Promise<unknown> => {
@@ -95,5 +95,3 @@ const sendToAirtable = async (data: unknown): Promise<boolean> => {
return response.ok;
};
export default handler;