The codebase that powers boop.cat
boop.cat
1// Copyright 2025 boop.cat
2// Licensed under the Apache License, Version 2.0
3// See LICENSE file for details.
4
5package lib
6
7import (
8 "encoding/json"
9 "net/http"
10 "net/url"
11 "os"
12 "strings"
13)
14
15type TurnstileResult struct {
16 OK bool
17 Error string
18}
19
20func VerifyTurnstile(token, remoteIP string) TurnstileResult {
21 secret := os.Getenv("TURNSTILE_SECRET_KEY")
22 if secret == "" {
23
24 return TurnstileResult{OK: true}
25 }
26
27 if token == "" {
28 return TurnstileResult{OK: false, Error: "missing-token"}
29 }
30
31 data := url.Values{}
32 data.Set("secret", secret)
33 data.Set("response", token)
34 if remoteIP != "" {
35 data.Set("remoteip", remoteIP)
36 }
37
38 resp, err := http.Post(
39 "https://challenges.cloudflare.com/turnstile/v0/siteverify",
40 "application/x-www-form-urlencoded",
41 strings.NewReader(data.Encode()),
42 )
43 if err != nil {
44 return TurnstileResult{OK: false, Error: "verification-failed"}
45 }
46 defer resp.Body.Close()
47
48 var result struct {
49 Success bool `json:"success"`
50 ErrorCodes []string `json:"error-codes"`
51 }
52 if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
53 return TurnstileResult{OK: false, Error: "parse-failed"}
54 }
55
56 if !result.Success {
57 errCode := "captcha-failed"
58 if len(result.ErrorCodes) > 0 {
59 errCode = result.ErrorCodes[0]
60 }
61 return TurnstileResult{OK: false, Error: errCode}
62 }
63
64 return TurnstileResult{OK: true}
65}