1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-10-26 01:05:49 -04:00

refactor .js files to ES modules where possible

This commit is contained in:
2024-03-12 14:58:00 -04:00
parent e66dc37c41
commit e1a26d5257
28 changed files with 460 additions and 414 deletions

View File

@@ -1,40 +1,47 @@
import nodemailer from "nodemailer";
import queryString from "query-string";
import fetcher from "../../lib/helpers/fetcher";
import { siteDomain, authorEmail, hcaptchaSiteKey } from "../../lib/config";
import config from "../../lib/config";
import type { NextApiHandler } from "next";
const handler: NextApiHandler = async (req, res) => {
// redirect GET requests to this endpoint to the contact form itself
if (req.method === "GET") {
return res.redirect(`${process.env.NEXT_PUBLIC_BASE_URL || `https://${siteDomain}`}/contact/`);
// only allow POST requests, otherwise return a 405 Method Not Allowed
if (req.method !== "POST") {
return res.status(405).send(null);
}
// possible weirdness? https://github.com/orgs/vercel/discussions/78#discussioncomment-5089059
const data = req.body;
try {
// possible weirdness? https://github.com/orgs/vercel/discussions/78#discussioncomment-5089059
const data = req.body;
// 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");
// 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");
}
// throw an internal error, not user's fault
if (!(await sendMessage(data))) {
throw new Error("nodemailer_error");
}
// disable caching on both ends. see:
// https://vercel.com/docs/concepts/functions/edge-functions/edge-caching
res.setHeader("Cache-Control", "private, no-cache, no-store, must-revalidate");
// success! let the client know
return res.status(201).json({ success: true });
} catch (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: any
) {
return res.status(400).json({ error: error.message ?? "Bad request." });
}
if (!data["h-captcha-response"] || !(await validateCaptcha(data["h-captcha-response"]))) {
// either the captcha is wrong or completely missing
throw new Error("INVALID_CAPTCHA");
}
// throw an internal error, not user's fault
if (!(await sendMessage(data))) {
throw new Error("NODEMAILER_ERROR");
}
// disable caching on both ends. see:
// https://vercel.com/docs/concepts/functions/edge-functions/edge-caching
res.setHeader("Cache-Control", "private, no-cache, no-store, must-revalidate");
// success! let the client know
return res.status(201).json({ success: true });
};
const validateCaptcha = async (formResponse: unknown): Promise<unknown> => {
@@ -46,7 +53,7 @@ const validateCaptcha = async (formResponse: unknown): Promise<unknown> => {
body: queryString.stringify({
response: formResponse,
// fallback to dummy secret for testing: https://docs.hcaptcha.com/#integration-testing-test-keys
sitekey: hcaptchaSiteKey || "10000000-ffff-ffff-ffff-000000000001",
sitekey: process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY || "10000000-ffff-ffff-ffff-000000000001",
secret: process.env.HCAPTCHA_SECRET_KEY || "0x0000000000000000000000000000000000000000",
}),
});
@@ -57,19 +64,22 @@ const validateCaptcha = async (formResponse: unknown): Promise<unknown> => {
const sendMessage = async (data: Record<string, unknown>): Promise<boolean> => {
try {
const transporter = nodemailer.createTransport({
service: "mailgun",
// https://resend.com/docs/send-with-nodemailer-smtp
host: "smtp.resend.com",
secure: true,
port: 465,
auth: {
user: process.env.MAILGUN_SMTP_USER,
pass: process.env.MAILGUN_SMTP_PASS,
user: "resend",
pass: process.env.RESEND_API_KEY,
},
});
await transporter.sendMail({
from: `${data.name} <${process.env.MAILGUN_SMTP_USER}>`,
sender: `nodemailer <${process.env.MAILGUN_SMTP_USER}>`,
from: `${data.name} <${process.env.RESEND_DOMAIN ? `noreply@${process.env.RESEND_DOMAIN}` : "onboarding@resend.dev"}>`,
sender: `nodemailer <${process.env.RESEND_DOMAIN ? `noreply@${process.env.RESEND_DOMAIN}` : "onboarding@resend.dev"}>`,
replyTo: `${data.name} <${data.email}>`,
to: `<${authorEmail}>`,
subject: `[${siteDomain}] Contact Form Submission`,
to: `<${config.authorEmail}>`,
subject: `[${config.siteDomain}] Contact Form Submission`,
// TODO: add markdown parsing as promised on the form.
text: `${data.message}`,
});