[优化] 移除 Turnstile 相关功能和配置

This commit is contained in:
ryan
2026-05-31 15:22:30 +08:00
parent 57616626fd
commit 4cb8928e4e
12 changed files with 15 additions and 288 deletions
-3
View File
@@ -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
+2 -3
View File
@@ -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,
+3 -13
View File
@@ -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{
@@ -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()
}
}
-9
View File
@@ -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":
+3 -3
View File
@@ -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)
@@ -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<HTMLDivElement | null>(null);
const widgetIdRef = useRef<string | null>(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 <div id={elementId} ref={containerRef} className="min-h-16" />;
}
+4 -14
View File
@@ -21,31 +21,21 @@ export function logout() {
return apiRequest<void>('/user/logout');
}
export function register(payload: RegisterPayload, turnstileToken?: string) {
const query = turnstileToken
? `?turnstile=${encodeURIComponent(turnstileToken)}`
: '';
return apiRequest<void>(`/user/register${query}`, {
export function register(payload: RegisterPayload) {
return apiRequest<void>('/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<void>(`/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<void>(`/reset_password?${searchParams.toString()}`);
}
@@ -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<typeof resetRequestSchema>;
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}
</AuthFormField>
{statusQuery.data?.turnstile_check &&
statusQuery.data.turnstile_site_key ? (
<TurnstileWidget
siteKey={statusQuery.data.turnstile_site_key}
onVerify={(token) => setTurnstileToken(token)}
onExpire={() => setTurnstileToken('')}
onError={() => setTurnstileToken('')}
/>
) : null}
{message ? (
<InlineMessage tone={message.tone} message={message.text} />
) : null}
@@ -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="请输入邮箱验证码"
/>
</ResourceField>
{publicStatus.turnstile_check ? (
publicStatus.turnstile_site_key ? (
<TurnstileWidget
siteKey={publicStatus.turnstile_site_key}
onVerify={(token) => setEmailTurnstileToken(token)}
onExpire={() => setEmailTurnstileToken('')}
onError={() => setEmailTurnstileToken('')}
/>
) : (
<EmptyState
title="Turnstile 配置不完整"
description="当前系统已启用 Turnstile,但未配置 Site Key,邮箱绑定暂不可用。"
/>
)
) : null}
<div className="flex flex-wrap gap-2">
<SecondaryButton
type="button"
@@ -19,8 +19,6 @@ export interface PublicStatus {
wechat_qrcode: string;
wechat_login: boolean;
server_address: string;
turnstile_check: boolean;
turnstile_site_key: string;
register_enabled: boolean;
password_register_enabled: boolean;
auth_sources: PublicAuthSource[];