mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-06-05 19:15:30 -04:00
Remove Cloudflare Turnstile integration and replace it with Vercel's BotID for spam protection in the contact form. Update environment variables and dependencies accordingly.
This commit is contained in:
-16
@@ -60,13 +60,6 @@ export const env = createEnv({
|
||||
|
||||
/** Required. The destination email for contact form submissions. */
|
||||
RESEND_TO_EMAIL: z.string().email(),
|
||||
|
||||
/**
|
||||
* Required. Secret for Cloudflare `siteverify` API to validate a form's turnstile result on the backend.
|
||||
*
|
||||
* @see https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
|
||||
*/
|
||||
TURNSTILE_SECRET_KEY: z.string().default("1x0000000000000000000000000000000AA"),
|
||||
},
|
||||
client: {
|
||||
/**
|
||||
@@ -146,14 +139,6 @@ export const env = createEnv({
|
||||
* @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||
*/
|
||||
NEXT_PUBLIC_SITE_TZ: z.string().default("America/New_York"),
|
||||
|
||||
/**
|
||||
* Required. Site key must be prefixed with NEXT_PUBLIC_ since it is used to embed the captcha widget. Falls back to
|
||||
* testing keys if not set or in dev environment.
|
||||
*
|
||||
* @see https://developers.cloudflare.com/turnstile/troubleshooting/testing/
|
||||
*/
|
||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY: z.string().default("1x00000000000000000000AA"),
|
||||
},
|
||||
experimental__runtimeEnv: {
|
||||
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
|
||||
@@ -163,7 +148,6 @@ export const env = createEnv({
|
||||
NEXT_PUBLIC_ONION_DOMAIN: process.env.NEXT_PUBLIC_ONION_DOMAIN,
|
||||
NEXT_PUBLIC_SITE_LOCALE: process.env.NEXT_PUBLIC_SITE_LOCALE,
|
||||
NEXT_PUBLIC_SITE_TZ: process.env.NEXT_PUBLIC_SITE_TZ,
|
||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY,
|
||||
},
|
||||
emptyStringAsUndefined: true,
|
||||
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
||||
|
||||
+7
-33
@@ -1,17 +1,16 @@
|
||||
"use server";
|
||||
|
||||
import { env } from "@/lib/env";
|
||||
import { headers } from "next/headers";
|
||||
import { Resend } from "resend";
|
||||
import { z } from "zod";
|
||||
import siteConfig from "@/lib/config/site";
|
||||
import { checkBotId } from "botid/server";
|
||||
|
||||
const ContactSchema = z
|
||||
.object({
|
||||
name: z.string().trim().min(1, { message: "Your name is required." }),
|
||||
email: z.string().email({ message: "Your email address is required." }),
|
||||
message: z.string().trim().min(15, { message: "Your message must be at least 15 characters." }),
|
||||
"cf-turnstile-response": z.string().min(1, { message: "Are you sure you're not a robot...? 🤖" }),
|
||||
})
|
||||
.readonly();
|
||||
|
||||
@@ -28,6 +27,12 @@ export const send = async (state: ContactState, payload: FormData): Promise<Cont
|
||||
console.debug("[server/resend] received payload:", payload);
|
||||
|
||||
try {
|
||||
// BotID server-side verification
|
||||
const verification = await checkBotId();
|
||||
if (verification.isBot) {
|
||||
return { success: false, message: "Bot detection failed. 🤖" };
|
||||
}
|
||||
|
||||
const data = ContactSchema.safeParse(Object.fromEntries(payload));
|
||||
|
||||
if (!data.success) {
|
||||
@@ -38,37 +43,6 @@ export const send = async (state: ContactState, payload: FormData): Promise<Cont
|
||||
};
|
||||
}
|
||||
|
||||
// try to get the client IP (for turnstile accuracy, not logging!) but no biggie if we can't
|
||||
let remoteip;
|
||||
try {
|
||||
remoteip = (await headers()).get("x-forwarded-for");
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
|
||||
// validate captcha
|
||||
const turnstileResponse = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
secret: env.TURNSTILE_SECRET_KEY,
|
||||
response: data.data["cf-turnstile-response"],
|
||||
remoteip,
|
||||
}),
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
if (!turnstileResponse || !turnstileResponse.ok) {
|
||||
throw new Error(`[server/resend] turnstile validation failed: ${turnstileResponse.status}`);
|
||||
}
|
||||
|
||||
const turnstileData = (await turnstileResponse.json()) as { success: boolean };
|
||||
|
||||
if (!turnstileData.success) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Did you complete the CAPTCHA? (If you're human, that is...)",
|
||||
};
|
||||
}
|
||||
|
||||
if (env.RESEND_FROM_EMAIL === "onboarding@resend.dev") {
|
||||
// https://resend.com/docs/api-reference/emails/send-email
|
||||
console.warn("[server/resend] 'RESEND_FROM_EMAIL' is not set, falling back to onboarding@resend.dev.");
|
||||
|
||||
Reference in New Issue
Block a user