Update SproutGate

This commit is contained in:
2026-05-13 12:19:36 +08:00
parent f7db8aa053
commit a37b92e144
51 changed files with 12034 additions and 254 deletions

View File

@@ -0,0 +1,95 @@
package handlers
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/gin-gonic/gin"
"sproutgate-backend/internal/storage"
)
const turnstileVerifyURL = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
type turnstileVerifyResp struct {
Success bool `json:"success"`
}
// verifyTurnstileToken 向 Cloudflare 验证 Turnstile token失败返回用户可见错误。
func verifyTurnstileToken(ctx context.Context, secretKey, token, remoteIP string) error {
if strings.TrimSpace(token) == "" {
return fmt.Errorf("请完成人机验证")
}
form := url.Values{}
form.Set("secret", secretKey)
form.Set("response", token)
if remoteIP != "" {
form.Set("remoteip", remoteIP)
}
client := &http.Client{Timeout: 8 * time.Second}
resp, err := client.PostForm(turnstileVerifyURL, form)
if err != nil {
return fmt.Errorf("人机验证服务暂时不可用,请稍后重试")
}
defer resp.Body.Close()
var result turnstileVerifyResp
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return fmt.Errorf("人机验证响应解析失败")
}
if !result.Success {
return fmt.Errorf("人机验证未通过,请重试")
}
return nil
}
// GetAdminTurnstile
// @Summary Turnstile 配置(脱敏)
// @Tags admin
// @Produce json
// @Security AdminToken
// @Success 200 {object} map[string]interface{}
// @Failure 401 {object} map[string]interface{}
// @Router /api/admin/turnstile [get]
func (h *Handler) GetAdminTurnstile(c *gin.Context) {
cfg := h.store.GetTurnstileConfig()
c.JSON(http.StatusOK, gin.H{
"enabled": cfg.Enabled,
"siteKey": strings.TrimSpace(cfg.SiteKey),
"secretKeySet": strings.TrimSpace(cfg.SecretKey) != "",
})
}
// PutAdminTurnstile
// @Summary 更新 Turnstile 配置
// @Tags admin
// @Accept json
// @Produce json
// @Security AdminToken
// @Param body body PutAdminTurnstileRequest true "siteKey、secretKey"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Failure 401 {object} map[string]interface{}
// @Failure 500 {object} map[string]interface{}
// @Router /api/admin/turnstile [put]
func (h *Handler) PutAdminTurnstile(c *gin.Context) {
var req PutAdminTurnstileRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
return
}
in := storage.TurnstileConfig{
Enabled: req.Enabled,
SiteKey: strings.TrimSpace(req.SiteKey),
SecretKey: strings.TrimSpace(req.SecretKey),
}
if err := h.store.UpdateTurnstileConfig(in); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save turnstile config"})
return
}
h.GetAdminTurnstile(c)
}