Files
jcoffey-dev 85bbf5f828 Forms, the endpoint, and search from it
Forms are YAML in forms/, rendered by {{ form }} with a small script and stylesheet the build publishes. cl-cms endpoint serves /_cl/ for any number of sites by host: it checks Origin, a per-IP rate limit, text-only bodies, a trap field, a signed 3 s to 24 h token and optional Turnstile, then the declared fields (types, lengths, options, no line breaks in single-line fields), and delivers by SMTP over TLS, optionally appending to a 0600 JSONL file. It also answers search from a collection's search.json, so a site can search in the browser or from the endpoint. cl-cms serve can pass /_cl/ to an endpoint, and -reload=false leaves out the live-reload script for headless browsers.
2026-10-10 14:10:17 -07:00

40 lines
994 B
Go

package endpoint
import (
"context"
"encoding/json"
"net/http"
"net/url"
"strings"
"time"
)
var turnstileURL = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
// turnstileOK asks Cloudflare whether a Turnstile response is genuine.
func turnstileOK(ctx context.Context, secret, response, ip string) bool {
if secret == "" || response == "" {
return false
}
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
form := url.Values{"secret": {secret}, "response": {response}}
if ip != "" {
form.Set("remoteip", ip)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, turnstileURL, strings.NewReader(form.Encode()))
if err != nil {
return false
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
res, err := http.DefaultClient.Do(req)
if err != nil {
return false
}
defer res.Body.Close()
var d struct {
Success bool `json:"success"`
}
return json.NewDecoder(res.Body).Decode(&d) == nil && d.Success
}