From 4cb8928e4eaf8ebf4b0fe11ec33f2d23b7bc481d Mon Sep 17 00:00:00 2001 From: ryan Date: Sun, 31 May 2026 15:22:30 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E7=A7=BB=E9=99=A4=20Tur?= =?UTF-8?q?nstile=20=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD=E5=92=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 1 - openflare_server/common/constants.go | 3 - openflare_server/controller/misc.go | 5 +- openflare_server/controller/option.go | 16 +-- .../middleware/turnstile-check.go | 81 -------------- openflare_server/model/option.go | 9 -- openflare_server/router/api-router.go | 6 +- .../web/components/forms/turnstile-widget.tsx | 101 ------------------ .../web/features/auth/api/auth.ts | 18 +--- .../password-reset-request-form.tsx | 26 +---- .../settings/components/settings-page.tsx | 35 +----- openflare_server/web/types/public-status.ts | 2 - 12 files changed, 15 insertions(+), 288 deletions(-) delete mode 100644 VERSION delete mode 100644 openflare_server/middleware/turnstile-check.go delete mode 100644 openflare_server/web/components/forms/turnstile-widget.tsx diff --git a/VERSION b/VERSION deleted file mode 100644 index 1bb9786c..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.0.x diff --git a/openflare_server/common/constants.go b/openflare_server/common/constants.go index b8d77591..fa1a39ca 100644 --- a/openflare_server/common/constants.go +++ b/openflare_server/common/constants.go @@ -30,7 +30,6 @@ var PasswordRegisterEnabled = true var EmailVerificationEnabled = false var GitHubOAuthEnabled = false var WeChatAuthEnabled = false -var TurnstileCheckEnabled = false var RegisterEnabled = false var SMTPServer = "" @@ -45,8 +44,6 @@ var WeChatServerAddress = "" var WeChatServerToken = "" var WeChatAccountQRCodeImageURL = "" -var TurnstileSiteKey = "" -var TurnstileSecretKey = "" var AgentToken = "" var AgentDiscoveryToken = "" var NodeOfflineThreshold = 2 * time.Minute diff --git a/openflare_server/controller/misc.go b/openflare_server/controller/misc.go index 7df06376..8f506343 100644 --- a/openflare_server/controller/misc.go +++ b/openflare_server/controller/misc.go @@ -3,7 +3,6 @@ package controller import ( "encoding/json" "fmt" - "github.com/gin-gonic/gin" "net/http" "openflare/common" "openflare/model" @@ -11,6 +10,8 @@ import ( "openflare/utils/mail" "openflare/utils/security" "openflare/utils/validation" + + "github.com/gin-gonic/gin" ) // GetStatus godoc @@ -39,8 +40,6 @@ func GetStatus(c *gin.Context) { "wechat_qrcode": common.WeChatAccountQRCodeImageURL, "wechat_login": common.WeChatAuthEnabled, "server_address": common.ServerAddress, - "turnstile_check": common.TurnstileCheckEnabled, - "turnstile_site_key": common.TurnstileSiteKey, "register_enabled": common.RegisterEnabled, "password_register_enabled": common.PasswordRegisterEnabled, "auth_sources": authSources, diff --git a/openflare_server/controller/option.go b/openflare_server/controller/option.go index 1e649e0c..3a498aeb 100644 --- a/openflare_server/controller/option.go +++ b/openflare_server/controller/option.go @@ -3,7 +3,6 @@ package controller import ( "encoding/json" "fmt" - "github.com/gin-gonic/gin" "net/http" "openflare/common" "openflare/model" @@ -13,6 +12,8 @@ import ( "regexp" "strconv" "strings" + + "github.com/gin-gonic/gin" ) var ( @@ -236,10 +237,7 @@ func validateOptionWithState(option model.Option, state map[string]string) error if option.Value == "true" && strings.TrimSpace(state["WeChatServerAddress"]) == "" { return fmt.Errorf("鏃犳硶鍚敤寰俊鐧诲綍锛岃鍏堝~鍏ュ井淇$櫥褰曠浉鍏抽厤缃俊鎭紒") } - case "TurnstileCheckEnabled": - if option.Value == "true" && strings.TrimSpace(state["TurnstileSiteKey"]) == "" { - return fmt.Errorf("鏃犳硶鍚敤 Turnstile 鏍¢獙锛岃鍏堝~鍏?Turnstile 鏍¢獙鐩稿叧閰嶇疆淇℃伅锛?") - } + } if err := validateRateLimitOption(option.Key, option.Value); err != nil { @@ -341,14 +339,6 @@ func UpdateOption(c *gin.Context) { }) return } - case "TurnstileCheckEnabled": - if option.Value == "true" && common.TurnstileSiteKey == "" { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "无法启用 Turnstile 校验,请先填入 Turnstile 校验相关配置信息!", - }) - return - } } if err = validateRateLimitOption(option.Key, option.Value); err != nil { c.JSON(http.StatusOK, gin.H{ diff --git a/openflare_server/middleware/turnstile-check.go b/openflare_server/middleware/turnstile-check.go deleted file mode 100644 index f96e1baa..00000000 --- a/openflare_server/middleware/turnstile-check.go +++ /dev/null @@ -1,81 +0,0 @@ -package middleware - -import ( - "encoding/json" - "github.com/gin-contrib/sessions" - "github.com/gin-gonic/gin" - "log/slog" - "net/http" - "net/url" - "openflare/common" -) - -type turnstileCheckResponse struct { - Success bool `json:"success"` -} - -func TurnstileCheck() gin.HandlerFunc { - return func(c *gin.Context) { - if common.TurnstileCheckEnabled { - session := sessions.Default(c) - turnstileChecked := session.Get("turnstile") - if turnstileChecked != nil { - c.Next() - return - } - response := c.Query("turnstile") - if response == "" { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "Turnstile token 为空", - }) - c.Abort() - return - } - rawRes, err := http.PostForm("https://challenges.cloudflare.com/turnstile/v0/siteverify", url.Values{ - "secret": {common.TurnstileSecretKey}, - "response": {response}, - "remoteip": {c.ClientIP()}, - }) - if err != nil { - slog.Error("turnstile verification request failed", "error", err) - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": err.Error(), - }) - c.Abort() - return - } - defer rawRes.Body.Close() - var res turnstileCheckResponse - err = json.NewDecoder(rawRes.Body).Decode(&res) - if err != nil { - slog.Error("decode turnstile verification response failed", "error", err) - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": err.Error(), - }) - c.Abort() - return - } - if !res.Success { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "Turnstile 校验失败,请刷新重试!", - }) - c.Abort() - return - } - session.Set("turnstile", true) - err = session.Save() - if err != nil { - c.JSON(http.StatusOK, gin.H{ - "message": "无法保存会话信息,请重试", - "success": false, - }) - return - } - } - c.Next() - } -} diff --git a/openflare_server/model/option.go b/openflare_server/model/option.go index 25760f55..9fa59608 100644 --- a/openflare_server/model/option.go +++ b/openflare_server/model/option.go @@ -34,7 +34,6 @@ func InitOptionMap() { common.OptionMap["EmailVerificationEnabled"] = strconv.FormatBool(common.EmailVerificationEnabled) common.OptionMap["GitHubOAuthEnabled"] = strconv.FormatBool(common.GitHubOAuthEnabled) common.OptionMap["WeChatAuthEnabled"] = strconv.FormatBool(common.WeChatAuthEnabled) - common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled) common.OptionMap["SMTPServer"] = "" common.OptionMap["SMTPPort"] = strconv.Itoa(common.SMTPPort) common.OptionMap["SMTPAccount"] = "" @@ -50,8 +49,6 @@ func InitOptionMap() { common.OptionMap["WeChatServerAddress"] = "" common.OptionMap["WeChatServerToken"] = "" common.OptionMap["WeChatAccountQRCodeImageURL"] = "" - common.OptionMap["TurnstileSiteKey"] = "" - common.OptionMap["TurnstileSecretKey"] = "" common.OptionMap["AgentDiscoveryToken"] = "" common.OptionMap["AgentHeartbeatInterval"] = strconv.Itoa(common.AgentHeartbeatInterval) common.OptionMap["AgentWebsocketUpgradeEnabled"] = strconv.FormatBool(common.AgentWebsocketUpgradeEnabled) @@ -180,8 +177,6 @@ func updateOptionMap(key string, value string) { common.GitHubOAuthEnabled = boolValue case "WeChatAuthEnabled": common.WeChatAuthEnabled = boolValue - case "TurnstileCheckEnabled": - common.TurnstileCheckEnabled = boolValue } } switch key { @@ -212,10 +207,6 @@ func updateOptionMap(key string, value string) { common.WeChatServerToken = value case "WeChatAccountQRCodeImageURL": common.WeChatAccountQRCodeImageURL = value - case "TurnstileSiteKey": - common.TurnstileSiteKey = value - case "TurnstileSecretKey": - common.TurnstileSecretKey = value case "AgentDiscoveryToken": common.AgentDiscoveryToken = value case "AgentHeartbeatInterval": diff --git a/openflare_server/router/api-router.go b/openflare_server/router/api-router.go index bba7c664..d1e53d4c 100644 --- a/openflare_server/router/api-router.go +++ b/openflare_server/router/api-router.go @@ -14,8 +14,8 @@ func SetApiRouter(router *gin.Engine) { apiRouter.GET("/status", controller.GetStatus) apiRouter.GET("/notice", controller.GetNotice) apiRouter.GET("/about", controller.GetAbout) - apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification) - apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail) + apiRouter.GET("/verification", middleware.CriticalRateLimit(), controller.SendEmailVerification) + apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), controller.SendPasswordResetEmail) apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword) apiRouter.GET("/oauth/github", middleware.CriticalRateLimit(), controller.GitHubOAuth) apiRouter.GET("/oauth/wechat", middleware.CriticalRateLimit(), controller.WeChatAuth) @@ -33,7 +33,7 @@ func SetApiRouter(router *gin.Engine) { userRoute := apiRouter.Group("/user") { - userRoute.POST("/register", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.Register) + userRoute.POST("/register", middleware.CriticalRateLimit(), controller.Register) userRoute.POST("/login", middleware.CriticalRateLimit(), controller.Login) userRoute.GET("/logout", controller.Logout) diff --git a/openflare_server/web/components/forms/turnstile-widget.tsx b/openflare_server/web/components/forms/turnstile-widget.tsx deleted file mode 100644 index 2da15fcd..00000000 --- a/openflare_server/web/components/forms/turnstile-widget.tsx +++ /dev/null @@ -1,101 +0,0 @@ -'use client'; - -import { useEffect, useId, useRef } from 'react'; - -declare global { - interface Window { - turnstile?: { - render: ( - container: string | HTMLElement, - options: { - sitekey: string; - callback: (token: string) => void; - 'expired-callback'?: () => void; - 'error-callback'?: () => void; - theme?: 'auto' | 'light' | 'dark'; - }, - ) => string; - remove: (widgetId: string) => void; - reset: (widgetId?: string) => void; - }; - } -} - -const TURNSTILE_SCRIPT_ID = 'cloudflare-turnstile-script'; -const TURNSTILE_SCRIPT_SRC = - 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; - -interface TurnstileWidgetProps { - siteKey: string; - onVerify: (token: string) => void; - onExpire?: () => void; - onError?: () => void; -} - -export function TurnstileWidget({ - siteKey, - onVerify, - onExpire, - onError, -}: TurnstileWidgetProps) { - const containerRef = useRef(null); - const widgetIdRef = useRef(null); - const elementId = useId().replace(/:/g, '-'); - - useEffect(() => { - let cancelled = false; - - const mountWidget = () => { - if (cancelled || !containerRef.current || !window.turnstile) { - return; - } - - if (widgetIdRef.current) { - window.turnstile.remove(widgetIdRef.current); - widgetIdRef.current = null; - } - - widgetIdRef.current = window.turnstile.render(containerRef.current, { - sitekey: siteKey, - callback: onVerify, - 'expired-callback': onExpire, - 'error-callback': onError, - theme: 'auto', - }); - }; - - const existingScript = document.getElementById( - TURNSTILE_SCRIPT_ID, - ) as HTMLScriptElement | null; - - if (window.turnstile) { - mountWidget(); - } else if (existingScript) { - existingScript.addEventListener('load', mountWidget); - } else { - const script = document.createElement('script'); - script.id = TURNSTILE_SCRIPT_ID; - script.src = TURNSTILE_SCRIPT_SRC; - script.async = true; - script.defer = true; - script.addEventListener('load', mountWidget); - document.head.appendChild(script); - } - - return () => { - cancelled = true; - const script = document.getElementById( - TURNSTILE_SCRIPT_ID, - ) as HTMLScriptElement | null; - if (script) { - script.removeEventListener('load', mountWidget); - } - if (widgetIdRef.current && window.turnstile) { - window.turnstile.remove(widgetIdRef.current); - widgetIdRef.current = null; - } - }; - }, [onError, onExpire, onVerify, siteKey]); - - return
; -} diff --git a/openflare_server/web/features/auth/api/auth.ts b/openflare_server/web/features/auth/api/auth.ts index d8df5d50..4cd37c5a 100644 --- a/openflare_server/web/features/auth/api/auth.ts +++ b/openflare_server/web/features/auth/api/auth.ts @@ -21,31 +21,21 @@ export function logout() { return apiRequest('/user/logout'); } -export function register(payload: RegisterPayload, turnstileToken?: string) { - const query = turnstileToken - ? `?turnstile=${encodeURIComponent(turnstileToken)}` - : ''; - - return apiRequest(`/user/register${query}`, { +export function register(payload: RegisterPayload) { + return apiRequest('/user/register', { method: 'POST', body: JSON.stringify(payload), }); } -export function sendEmailVerification(email: string, turnstileToken?: string) { +export function sendEmailVerification(email: string) { const searchParams = new URLSearchParams({ email }); - if (turnstileToken) { - searchParams.set('turnstile', turnstileToken); - } return apiRequest(`/verification?${searchParams.toString()}`); } -export function sendPasswordResetEmail(email: string, turnstileToken?: string) { +export function sendPasswordResetEmail(email: string) { const searchParams = new URLSearchParams({ email }); - if (turnstileToken) { - searchParams.set('turnstile', turnstileToken); - } return apiRequest(`/reset_password?${searchParams.toString()}`); } diff --git a/openflare_server/web/features/auth/components/password-reset-request-form.tsx b/openflare_server/web/features/auth/components/password-reset-request-form.tsx index 3349882b..f16cbaf4 100644 --- a/openflare_server/web/features/auth/components/password-reset-request-form.tsx +++ b/openflare_server/web/features/auth/components/password-reset-request-form.tsx @@ -1,17 +1,15 @@ 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import Link from 'next/link'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { InlineMessage } from '@/components/feedback/inline-message'; -import { TurnstileWidget } from '@/components/forms/turnstile-widget'; import { AppCard } from '@/components/ui/app-card'; import { sendPasswordResetEmail } from '@/features/auth/api/auth'; -import { getPublicStatus } from '@/features/auth/api/public'; import { AuthButton, AuthFormField, @@ -26,7 +24,6 @@ const resetRequestSchema = z.object({ type ResetRequestFormValues = z.infer; export function PasswordResetRequestForm() { - const [turnstileToken, setTurnstileToken] = useState(''); const [message, setMessage] = useState<{ tone: 'success' | 'danger' | 'info'; text: string; @@ -37,14 +34,9 @@ export function PasswordResetRequestForm() { defaultValues: { email: '' }, }); - const statusQuery = useQuery({ - queryKey: ['public-status'], - queryFn: getPublicStatus, - }); - const mutation = useMutation({ mutationFn: (values: ResetRequestFormValues) => - sendPasswordResetEmail(values.email, turnstileToken || undefined), + sendPasswordResetEmail(values.email), onSuccess: () => { setMessage({ tone: 'success', text: '重置邮件发送成功,请检查邮箱。' }); form.reset(); @@ -59,10 +51,6 @@ export function PasswordResetRequestForm() { const handleSubmit = form.handleSubmit((values) => { setMessage(null); - if (statusQuery.data?.turnstile_check && !turnstileToken) { - setMessage({ tone: 'info', text: '请先完成人机验证。' }); - return; - } mutation.mutate(values); }); @@ -86,16 +74,6 @@ export function PasswordResetRequestForm() { ) : null} - {statusQuery.data?.turnstile_check && - statusQuery.data.turnstile_site_key ? ( - setTurnstileToken(token)} - onExpire={() => setTurnstileToken('')} - onError={() => setTurnstileToken('')} - /> - ) : null} - {message ? ( ) : null} diff --git a/openflare_server/web/features/settings/components/settings-page.tsx b/openflare_server/web/features/settings/components/settings-page.tsx index 54e81685..b1f6979e 100644 --- a/openflare_server/web/features/settings/components/settings-page.tsx +++ b/openflare_server/web/features/settings/components/settings-page.tsx @@ -8,7 +8,6 @@ import { ErrorState } from '@/components/feedback/error-state'; import { InlineMessage } from '@/components/feedback/inline-message'; import { LoadingState } from '@/components/feedback/loading-state'; import { AppModal } from '@/components/ui/app-modal'; -import { TurnstileWidget } from '@/components/forms/turnstile-widget'; import { useAuth } from '@/components/providers/auth-provider'; import { PageHeader } from '@/components/layout/page-header'; import { AppCard } from '@/components/ui/app-card'; @@ -71,7 +70,6 @@ const defaultSystemFields = { EmailVerificationEnabled: false, GitHubOAuthEnabled: false, WeChatAuthEnabled: false, - TurnstileCheckEnabled: false, SMTPServer: '', SMTPPort: '587', SMTPAccount: '', @@ -81,8 +79,6 @@ const defaultSystemFields = { WeChatServerAddress: '', WeChatServerToken: '', WeChatAccountQRCodeImageURL: '', - TurnstileSiteKey: '', - TurnstileSecretKey: '', }; const defaultOperationFields = { @@ -257,7 +253,6 @@ export function SettingsPage() { const [accessToken, setAccessToken] = useState(''); const [emailAddress, setEmailAddress] = useState(''); const [emailCode, setEmailCode] = useState(''); - const [emailTurnstileToken, setEmailTurnstileToken] = useState(''); const [authSourceModalOpen, setAuthSourceModalOpen] = useState(false); const [geoIPTestIP, setGeoIPTestIP] = useState('8.8.8.8'); const [cleanupModalState, setCleanupModalState] = @@ -325,8 +320,6 @@ export function SettingsPage() { GitHubClientId: publicStatus.github_client_id || previous.GitHubClientId, WeChatAccountQRCodeImageURL: publicStatus.wechat_qrcode || previous.WeChatAccountQRCodeImageURL, - TurnstileSiteKey: - publicStatus.turnstile_site_key || previous.TurnstileSiteKey, })); setOtherFields((previous) => ({ ...previous, @@ -364,7 +357,6 @@ export function SettingsPage() { ), GitHubOAuthEnabled: toBoolean(optionMap.GitHubOAuthEnabled, false), WeChatAuthEnabled: toBoolean(optionMap.WeChatAuthEnabled, false), - TurnstileCheckEnabled: toBoolean(optionMap.TurnstileCheckEnabled, false), SMTPServer: optionMap.SMTPServer ?? '', SMTPPort: optionMap.SMTPPort ?? '587', SMTPAccount: optionMap.SMTPAccount ?? '', @@ -374,8 +366,6 @@ export function SettingsPage() { WeChatServerAddress: optionMap.WeChatServerAddress ?? '', WeChatServerToken: '', WeChatAccountQRCodeImageURL: optionMap.WeChatAccountQRCodeImageURL ?? '', - TurnstileSiteKey: optionMap.TurnstileSiteKey ?? '', - TurnstileSecretKey: '', }); setOperationFields({ @@ -618,16 +608,8 @@ export function SettingsPage() { return; } - if (publicStatusQuery.data?.turnstile_check && !emailTurnstileToken) { - setFeedback({ tone: 'info', message: '请先完成人机验证。' }); - return; - } - void runBusyAction('email-send', async () => { - await sendEmailVerification( - emailAddress.trim(), - emailTurnstileToken || undefined, - ); + await sendEmailVerification(emailAddress.trim()); setFeedback({ tone: 'success', message: '验证码已发送,请检查邮箱。' }); }); }; @@ -974,21 +956,6 @@ export function SettingsPage() { placeholder="请输入邮箱验证码" /> - {publicStatus.turnstile_check ? ( - publicStatus.turnstile_site_key ? ( - setEmailTurnstileToken(token)} - onExpire={() => setEmailTurnstileToken('')} - onError={() => setEmailTurnstileToken('')} - /> - ) : ( - - ) - ) : null}