TinySkin/service/captcha/captcha.go
thehrz 0b880bece8
All checks were successful
CI / deploy (push) Successful in 57s
pref: rename project
2025-01-24 17:12:15 +08:00

86 lines
1.8 KiB
Go

package captcha
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"tinyskin/config"
)
type CaptchaService struct {
config config.Config
httpClient *http.Client
}
func NewCaptchaService(config config.Config, httpClient *http.Client) *CaptchaService {
return &CaptchaService{
config: config,
httpClient: httpClient,
}
}
func (c *CaptchaService) VerifyCaptcha(ctx context.Context, token, ip string) error {
if c.config.Captcha.Type != "turnstile" {
return nil
}
bw := &bytes.Buffer{}
err := json.NewEncoder(bw).Encode(turnstileResponse{
Secret: c.config.Captcha.Secret,
Response: token,
Remoteip: ip,
})
if err != nil {
return fmt.Errorf("verifyTurnstile: %w", err)
}
reqs, err := http.NewRequestWithContext(ctx, "POST", "https://challenges.cloudflare.com/turnstile/v0/siteverify", bw)
if err != nil {
return fmt.Errorf("verifyTurnstile: %w", err)
}
reqs.Header.Set("Accept", "*/*")
reqs.Header.Set("Content-Type", "application/json")
rep, err := c.httpClient.Do(reqs)
if err != nil {
return fmt.Errorf("verifyTurnstile: %w", err)
}
defer rep.Body.Close()
var t turnstileRet
err = json.NewDecoder(rep.Body).Decode(&t)
if err != nil {
return fmt.Errorf("verifyTurnstile: %w", err)
}
if !t.Success {
return fmt.Errorf("verifyTurnstile: %w", errors.Join(ErrTurnstile{
ErrorCodes: t.ErrorCodes,
}, ErrCaptcha))
}
return nil
}
type turnstileResponse struct {
Secret string `json:"secret"`
Response string `json:"response"`
Remoteip string `json:"remoteip"`
}
type turnstileRet struct {
Success bool `json:"success"`
ErrorCodes []string `json:"error-codes"`
}
var ErrCaptcha = errors.New("验证码错误")
type ErrTurnstile struct {
ErrorCodes []string
}
func (e ErrTurnstile) Error() string {
return strings.Join(e.ErrorCodes, " ")
}