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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user