1
0
mirror of https://github.com/XFox111/my-website.git synced 2026-04-22 07:28:01 +03:00

!feat: added Cloudflare Turnstile captcha to the contact form

feat: added contact form disclaimer
This commit is contained in:
2025-01-22 15:57:02 +00:00
parent 5d059fb7c4
commit 9d369ad4d2
8 changed files with 116 additions and 3 deletions
+27
View File
@@ -3,6 +3,7 @@
import { canonicalName } from "@/_data/metadata";
import nodemailer from "nodemailer";
import { z } from "zod";
import { verifyTurnstile } from "./turnstile";
const schema = z.object({
email: z.string().email().max(60),
@@ -24,6 +25,32 @@ const mailClient = nodemailer.createTransport({
export default async function sendInquiry(_: FormStatus, formData: FormData): Promise<FormStatus>
{
const cfToken = formData.get("cf-turnstile-response")?.toString();
if (!cfToken)
return {
status: "error",
message: "You must complete the challenge"
};
const [isValid, error] = await verifyTurnstile(cfToken);
if (!isValid)
{
if (error === "timeout-or-duplicate")
return {
status: "error",
message: "Challenge has expired. Try again"
};
console.error(error);
return {
status: "error",
message: "Something went wrong"
};
}
const { success, data } = schema.safeParse({
email: formData.get("email"),
subject: formData.get("subject"),
+51
View File
@@ -0,0 +1,51 @@
"use server";
import { headers } from "next/headers";
export async function getSitekey(): Promise<string | undefined>
{
return process.env.CF_SITEKEY;
}
export async function verifyTurnstile(token: string): Promise<[false, TurnstileErrorType] | [true]>
{
if (!process.env.CF_SECRET)
return [true];
const formData = new FormData();
console.log(headers().get("CF-Connecting-IP"));
formData.append("secret", process.env.CF_SECRET);
formData.append("response", token);
formData.append("remoteip", headers().get("CF-Connecting-IP") ?? "");
const response = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify",
{
body: formData,
method: "POST"
}
);
const result: TurnstileValidationResponse = await response.json();
if (result.success)
return [result.success];
else
return [result.success, result["error-codes"][0]];
}
export type TurnstileValidationResponse =
{
success: boolean;
challenge_ts: string;
hostname: string;
"error-codes": TurnstileErrorType[];
action: string;
cdata: `sessionid-${string}`;
metadata: Record<string, string>;
};
export type TurnstileErrorType =
"missing-input-secret" | "invalid-input-secret" | "missing-input-response" |
"invalid-input-response" | "bad-request" | "timeout-or-duplicate" | "internal-error";