Skip to content
37 linesCodeBlameRaw
1import { env } from "cloudflare:workers";
2
3import { TURNSTILE_FIELD, TURNSTILE_VERIFY, type TurnstileSettings, turnstilePassed, turnstileSettings } from "./turnstile";
4
5/** Turnstile's settings from the Worker's: null when it is off (lib/turnstile.ts). */
6export function turnstile(): TurnstileSettings {
7 const vars = env as unknown as { TURNSTILE_SITE_KEY?: string; TURNSTILE_SECRET?: string };
8 return turnstileSettings(vars.TURNSTILE_SITE_KEY, vars.TURNSTILE_SECRET);
9}
10
11/**
12 * Whether the form's Turnstile answer is good. True when Turnstile is off.
13 * A missing or refused answer is false. When Cloudflare cannot be reached
14 * the sign-up goes ahead, with the rate limit and email confirmation still
15 * in its way: a check that cannot be made is no reason to stop everyone.
16 */
17export async function turnstileOk(form: FormData, client: string | null): Promise<boolean> {
18 const settings = turnstile();
19 if (!settings) return true;
20 const answer = String(form.get(TURNSTILE_FIELD) ?? "").trim();
21 if (!answer) return false;
22 const body = new FormData();
23 body.set("secret", settings.secret);
24 body.set("response", answer);
25 if (client) body.set("remoteip", client);
26 try {
27 const response = await fetch(TURNSTILE_VERIFY, { method: "POST", body });
28 if (!response.ok) {
29 console.error(`turnstile: siteverify answered ${response.status}`);
30 return true;
31 }
32 return turnstilePassed(await response.json());
33 } catch (error) {
34 console.error("turnstile: siteverify could not be reached", error);
35 return true;
36 }
37}