Files
sub2api/backend/internal/service/turnstile_service.go

106 lines
3.5 KiB
Go
Raw Permalink Normal View History

2025-12-18 13:50:39 +08:00
package service
import (
"context"
"fmt"
2025-12-25 20:52:47 +08:00
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
2025-12-18 13:50:39 +08:00
)
var (
2025-12-25 20:52:47 +08:00
ErrTurnstileVerificationFailed = infraerrors.BadRequest("TURNSTILE_VERIFICATION_FAILED", "turnstile verification failed")
ErrTurnstileNotConfigured = infraerrors.ServiceUnavailable("TURNSTILE_NOT_CONFIGURED", "turnstile not configured")
ErrTurnstileInvalidSecretKey = infraerrors.BadRequest("TURNSTILE_INVALID_SECRET_KEY", "invalid turnstile secret key")
2025-12-18 13:50:39 +08:00
)
2025-12-20 11:56:11 +08:00
// TurnstileVerifier 验证 Turnstile token 的接口
type TurnstileVerifier interface {
VerifyToken(ctx context.Context, secretKey, token, remoteIP string) (*TurnstileVerifyResponse, error)
}
2025-12-18 13:50:39 +08:00
// TurnstileService Turnstile 验证服务
type TurnstileService struct {
settingService *SettingService
2025-12-20 11:56:11 +08:00
verifier TurnstileVerifier
2025-12-18 13:50:39 +08:00
}
// TurnstileVerifyResponse Cloudflare Turnstile 验证响应
type TurnstileVerifyResponse struct {
Success bool `json:"success"`
ChallengeTS string `json:"challenge_ts"`
Hostname string `json:"hostname"`
ErrorCodes []string `json:"error-codes"`
Action string `json:"action"`
CData string `json:"cdata"`
}
// NewTurnstileService 创建 Turnstile 服务实例
2025-12-20 11:56:11 +08:00
func NewTurnstileService(settingService *SettingService, verifier TurnstileVerifier) *TurnstileService {
2025-12-18 13:50:39 +08:00
return &TurnstileService{
settingService: settingService,
2025-12-20 11:56:11 +08:00
verifier: verifier,
2025-12-18 13:50:39 +08:00
}
}
// VerifyToken 验证 Turnstile token
func (s *TurnstileService) VerifyToken(ctx context.Context, token string, remoteIP string) error {
// 检查是否启用 Turnstile
if !s.settingService.IsTurnstileEnabled(ctx) {
logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Disabled, skipping verification")
2025-12-18 13:50:39 +08:00
return nil
}
// 获取 Secret Key
secretKey := s.settingService.GetTurnstileSecretKey(ctx)
if secretKey == "" {
logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Secret key not configured")
2025-12-18 13:50:39 +08:00
return ErrTurnstileNotConfigured
}
// 如果 token 为空,返回错误
if token == "" {
logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Token is empty")
2025-12-18 13:50:39 +08:00
return ErrTurnstileVerificationFailed
}
logger.LegacyPrintf("service.turnstile", "[Turnstile] Verifying token for IP: %s", remoteIP)
2025-12-20 11:56:11 +08:00
result, err := s.verifier.VerifyToken(ctx, secretKey, token, remoteIP)
2025-12-18 13:50:39 +08:00
if err != nil {
logger.LegacyPrintf("service.turnstile", "[Turnstile] Request failed: %v", err)
2025-12-18 13:50:39 +08:00
return fmt.Errorf("send request: %w", err)
}
if !result.Success {
logger.LegacyPrintf("service.turnstile", "[Turnstile] Verification failed, error codes: %v", result.ErrorCodes)
2025-12-18 13:50:39 +08:00
return ErrTurnstileVerificationFailed
}
logger.LegacyPrintf("service.turnstile", "%s", "[Turnstile] Verification successful")
2025-12-18 13:50:39 +08:00
return nil
}
// IsEnabled 检查 Turnstile 是否启用
func (s *TurnstileService) IsEnabled(ctx context.Context) bool {
return s.settingService.IsTurnstileEnabled(ctx)
}
// ValidateSecretKey 验证 Turnstile Secret Key 是否有效
func (s *TurnstileService) ValidateSecretKey(ctx context.Context, secretKey string) error {
// 发送一个测试token的验证请求来检查secret_key是否有效
result, err := s.verifier.VerifyToken(ctx, secretKey, "test-validation", "")
if err != nil {
return fmt.Errorf("validate secret key: %w", err)
}
// 检查是否有 invalid-input-secret 错误
for _, code := range result.ErrorCodes {
if code == "invalid-input-secret" {
return ErrTurnstileInvalidSecretKey
}
}
// 其他错误(如 invalid-input-response说明 secret key 是有效的
return nil
}