From b8e50c10f6ca419d562e8671674856d98c5d25a5 Mon Sep 17 00:00:00 2001 From: HouYunFei <1844025705@qq.com> Date: Mon, 25 May 2026 16:03:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B3=A8=E5=86=8C=E5=BC=80=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在系统设置中新增 allowRegister 配置项控制用户注册 - 实现注册接口的开关检查机制 - 登录页面根据注册开关动态显示或隐藏注册选项 - 首次 Linux.do 登录创建用户时也受注册开关限制 - 更新系统配置文档说明新的认证配置项 --- docs/system-settings.md | 14 ++++++++++++++ model/setting.go | 3 ++- service/auth.go | 11 +++++++++++ service/settings.go | 4 ++++ web/src/app/(admin)/admin/settings/page.tsx | 8 +++++++- web/src/app/(user)/login/page.tsx | 16 +++++++++++----- web/src/services/api/admin.ts | 1 + 7 files changed, 50 insertions(+), 7 deletions(-) diff --git a/docs/system-settings.md b/docs/system-settings.md index aa992f7..74a267b 100644 --- a/docs/system-settings.md +++ b/docs/system-settings.md @@ -22,6 +22,12 @@ "defaultTextModel": "gpt-5.5", "systemPrompt": "", "allowCustomChannel": true + }, + "auth": { + "allowRegister": true, + "linuxDo": { + "enabled": false + } } } ``` @@ -29,6 +35,7 @@ | 字段 | 类型 | 说明 | | --- | --- | --- | | `modelChannel` | object | 模型渠道公开配置组 | +| `auth` | object | 认证相关公开配置 | `modelChannel` 字段: @@ -56,6 +63,13 @@ | 云端渠道 | 使用后端 `/api/v1/*` 代理接口,请求会按模型名匹配 `private.value.channels` 中的可用渠道 | | 本地直连 | 默认可选;`allowCustomChannel` 关闭后不可选,用户在浏览器本地配置 `baseUrl`、`apiKey` 和模型列表后直接请求模型接口 | +`auth` 字段: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `allowRegister` | boolean | 是否允许用户注册,默认允许;关闭后注册入口隐藏,注册接口拒绝新用户创建 | +| `linuxDo.enabled` | boolean | 是否开启 Linux.do 登录 | + ## private.value ```json diff --git a/model/setting.go b/model/setting.go index 60dc301..d6d4a1f 100644 --- a/model/setting.go +++ b/model/setting.go @@ -46,7 +46,8 @@ type PublicSetting struct { } type PublicAuthSetting struct { - LinuxDo PublicLinuxDoAuthSetting `json:"linuxDo"` + AllowRegister *bool `json:"allowRegister"` + LinuxDo PublicLinuxDoAuthSetting `json:"linuxDo"` } type PublicLinuxDoAuthSetting struct { diff --git a/service/auth.go b/service/auth.go index 9a26619..ad32df7 100644 --- a/service/auth.go +++ b/service/auth.go @@ -59,6 +59,14 @@ func EnsureDefaultAdmin() error { } func Register(username string, password string) (model.AuthSession, error) { + settings, err := repository.GetSettings() + if err != nil { + return model.AuthSession{}, err + } + normalizedSettings := normalizeSettings(settings) + if normalizedSettings.Public.Auth.AllowRegister != nil && !*normalizedSettings.Public.Auth.AllowRegister { + return model.AuthSession{}, safeMessageError{message: "当前未开放注册"} + } username = strings.TrimSpace(username) if strings.ContainsAny(username, " \t\r\n") { return model.AuthSession{}, safeMessageError{message: "用户名不能包含空格"} @@ -163,6 +171,9 @@ func LoginWithLinuxDo(r *http.Request, code string, state string) (model.AuthSes return model.AuthSession{}, redirect, err } if !ok { + if settings.Public.Auth.AllowRegister != nil && !*settings.Public.Auth.AllowRegister { + return model.AuthSession{}, redirect, safeMessageError{message: "当前未开放注册"} + } user = model.User{ ID: newID("user"), Username: linuxDoUsername(profile.Username, linuxDoID), diff --git a/service/settings.go b/service/settings.go index 0f30d6a..bbf088f 100644 --- a/service/settings.go +++ b/service/settings.go @@ -78,6 +78,10 @@ func normalizePublicSetting(setting model.PublicSetting) model.PublicSetting { enabled := true setting.ModelChannel.AllowCustomChannel = &enabled } + if setting.Auth.AllowRegister == nil { + enabled := true + setting.Auth.AllowRegister = &enabled + } return setting } diff --git a/web/src/app/(admin)/admin/settings/page.tsx b/web/src/app/(admin)/admin/settings/page.tsx index 867d4dc..2493467 100644 --- a/web/src/app/(admin)/admin/settings/page.tsx +++ b/web/src/app/(admin)/admin/settings/page.tsx @@ -36,7 +36,7 @@ const emptySettings: AdminSettings = { systemPrompt: "", allowCustomChannel: true, }, - auth: { linuxDo: { enabled: false } }, + auth: { allowRegister: true, linuxDo: { enabled: false } }, }, private: { channels: [], promptSync: { enabled: true, cron: "*/5 * * * *" }, auth: { linuxDo: { clientId: "", clientSecret: "" } } }, }; @@ -371,6 +371,11 @@ export default function AdminSettingsPage() { + + + + + 模型算力点 = {}): modelCosts: normalizeModelCosts(setting.modelChannel?.modelCosts || []), }, auth: { + allowRegister: setting.auth?.allowRegister !== false, linuxDo: { enabled: setting.auth?.linuxDo?.enabled === true, }, diff --git a/web/src/app/(user)/login/page.tsx b/web/src/app/(user)/login/page.tsx index 9ca4cdf..010a12d 100644 --- a/web/src/app/(user)/login/page.tsx +++ b/web/src/app/(user)/login/page.tsx @@ -32,6 +32,7 @@ function LoginContent() { const setSession = useUserStore((state) => state.setSession); const isLoading = useUserStore((state) => state.isLoading); const linuxDoEnabled = useConfigStore((state) => state.publicSettings?.auth?.linuxDo?.enabled === true); + const allowRegister = useConfigStore((state) => state.publicSettings?.auth?.allowRegister !== false); const [mode, setMode] = useState<"login" | "register">("login"); const redirect = searchParams.get("redirect") || "/"; @@ -48,8 +49,16 @@ function LoginContent() { }); }, [message, redirect, router, searchParams, setSession]); + useEffect(() => { + if (!allowRegister && mode === "register") setMode("login"); + }, [allowRegister, mode]); + const submit = async (values: LoginFormValues) => { try { + if (mode === "register" && !allowRegister) { + message.error("当前未开放注册"); + return; + } if (mode === "register" && values.password !== values.confirmPassword) { message.error("两次输入的密码不一致"); return; @@ -87,10 +96,7 @@ function LoginContent() { block value={mode} onChange={(value) => setMode(value as "login" | "register")} - options={[ - { label: "登录", value: "login" }, - { label: "注册", value: "register" }, - ]} + options={allowRegister ? [{ label: "登录", value: "login" }, { label: "注册", value: "register" }] : [{ label: "登录", value: "login" }]} /> 用户名} rules={[{ required: true, message: "请输入用户名" }]}> @@ -104,7 +110,7 @@ function LoginContent() { } autoComplete="new-password" /> ) : null} - + diff --git a/web/src/services/api/admin.ts b/web/src/services/api/admin.ts index 1a024db..d4971ab 100644 --- a/web/src/services/api/admin.ts +++ b/web/src/services/api/admin.ts @@ -186,6 +186,7 @@ export type AdminModelCost = { export type AdminPublicSettings = { modelChannel: AdminPublicModelChannelSettings; auth: { + allowRegister: boolean; linuxDo: { enabled: boolean; };