feat(web): add Cloudflare Turnstile to the public forms
Render the widget on the contact, event registration and both application forms, and verify the token with Siteverify in each action before anything is written to the CMS. Verification fails closed. The check is off unless both TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY are set. Allow challenges.cloudflare.com in script-src and frame-src. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
|
||||
Client-side JS is an enhancement only - the page works as a plain HTML form
|
||||
post, and `useNavigation()` merely swaps the button into its loading state.
|
||||
The one exception is the Cloudflare Turnstile check, which needs scripting
|
||||
whenever it is configured (see ~/components/ui/Turnstile).
|
||||
|
||||
Every message the reader can see is localised: field labels and the generic
|
||||
errors come from the i18n catalogue, the page prose from the approved draft.
|
||||
@@ -41,9 +43,11 @@ import { data, Form, Link, useLocation, useNavigation, useRouteLoaderData } from
|
||||
import { ArrowIcon, Button } from "~/components/ui/Button";
|
||||
import { EuBadge } from "~/components/ui/EuBadge";
|
||||
import { Input, Textarea } from "~/components/ui/Input";
|
||||
import { Turnstile } from "~/components/ui/Turnstile";
|
||||
import { createTranslator, sectionPath, type Locale } from "~/i18n";
|
||||
import { getServerEnv } from "~/lib/env.server";
|
||||
import { buildBreadcrumbJsonLd, buildMeta } from "~/lib/seo";
|
||||
import { verifyTurnstile } from "~/lib/turnstile.server";
|
||||
|
||||
import type { Route } from "./+types/contact";
|
||||
import type { CmsContactPage } from "./_page-kit/cms-types";
|
||||
@@ -274,6 +278,20 @@ export async function action({ params, request }: Route.ActionArgs) {
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await verifyTurnstile(form))) {
|
||||
const t = createTranslator(locale);
|
||||
|
||||
return data(
|
||||
{
|
||||
status: "failed" as const,
|
||||
errors: {} as ContactErrors,
|
||||
values,
|
||||
formError: t("form.errorVerification"),
|
||||
},
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await createContactSubmission(
|
||||
values,
|
||||
@@ -548,6 +566,8 @@ export default function Contact({
|
||||
{...(errors.message ? { error: errors.message } : {})}
|
||||
/>
|
||||
|
||||
<Turnstile action="contact" locale={locale} resetKey={actionData} />
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
size="lg"
|
||||
|
||||
Reference in New Issue
Block a user