mirror of
https://codeberg.org/angestoepselt/homepage.git
synced 2026-03-21 22:32:17 +00:00
83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
package antispam
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"html/template"
|
|
"log"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type HCaptcha struct {
|
|
formField template.HTML
|
|
secretKey string
|
|
siteverifyEndpoint string
|
|
}
|
|
|
|
func (hcaptcha *HCaptcha) Script() template.HTML {
|
|
return `<script src="https://js.hcaptcha.com/1/api.js" async defer></script>`
|
|
}
|
|
|
|
func (hcaptcha *HCaptcha) FormField() template.HTML {
|
|
return hcaptcha.formField
|
|
}
|
|
|
|
func (hcaptcha *HCaptcha) CheckRequest(r *http.Request) (bool, error) {
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
return false, fmt.Errorf("error parsing form: %w", err)
|
|
}
|
|
|
|
token := r.PostForm.Get("x-captcha-response")
|
|
if token == "" {
|
|
slog.Debug("No hCaptcha response token in request")
|
|
return false, nil
|
|
}
|
|
|
|
form := url.Values{}
|
|
form.Set("secret", hcaptcha.secretKey)
|
|
form.Set("response", token)
|
|
res, err := http.PostForm(hcaptcha.siteverifyEndpoint, form)
|
|
if err != nil {
|
|
return false, fmt.Errorf("hCaptcha site verify error: %w", err)
|
|
}
|
|
|
|
var data struct {
|
|
Success bool `json:"success"`
|
|
Hostname string `json:"hostname"`
|
|
ErrorCodes []string `json:"error-codes"`
|
|
}
|
|
err = json.NewDecoder(res.Body).Decode(&data)
|
|
if err != nil {
|
|
return false, fmt.Errorf("hCaptcha site verify: JSON decode error: %w", err)
|
|
}
|
|
if res.StatusCode != http.StatusOK {
|
|
return false, fmt.Errorf("hCaptcha site verify: error codes %s, status %d", strings.Join(data.ErrorCodes, ", "), res.StatusCode)
|
|
}
|
|
|
|
result := data.Success && data.Hostname == r.URL.Hostname()
|
|
return result, nil
|
|
}
|
|
|
|
// NewHcaptcha returns an antispam implementation using the hCaptcha service.
|
|
func NewHcaptcha(siteKey string, secretKey string) *HCaptcha {
|
|
return newHcaptcha(siteKey, secretKey, "https://hcaptcha.com")
|
|
}
|
|
|
|
func newHcaptcha(siteKey string, secretKey string, endpoint string) *HCaptcha {
|
|
formField := `<div class="h-captcha" data-sitekey="` + template.HTMLEscapeString(siteKey) + `"></div>`
|
|
|
|
siteverifyEndpoint, err := url.JoinPath(endpoint, "siteverify")
|
|
if err != nil {
|
|
log.Panicf("error constructing siteverify endpoint: %v", err)
|
|
}
|
|
|
|
return &HCaptcha{
|
|
formField: template.HTML(formField),
|
|
secretKey: secretKey,
|
|
siteverifyEndpoint: siteverifyEndpoint,
|
|
}
|
|
}
|