From 3c2a86f94dc35e5a7d161c0c97fbce0b2cb58c31 Mon Sep 17 00:00:00 2001 From: xifan Date: Mon, 31 Mar 2025 00:46:13 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Update=20option=20handling?= =?UTF-8?q?=20in=20SystemSetting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✅ Add backend validation for OIDC & Telegram OAuth config - ♻️ Refactor frontend option updates with batch processing --- controller/option.go | 13 +- web/src/components/SystemSetting.js | 1547 +++++++++++++-------------- 2 files changed, 754 insertions(+), 806 deletions(-) diff --git a/controller/option.go b/controller/option.go index f1e28e0c..81ef463c 100644 --- a/controller/option.go +++ b/controller/option.go @@ -53,11 +53,12 @@ func UpdateOption(c *gin.Context) { return } case "oidc.enabled": - if option.Value == "true" && system_setting.GetOIDCSettings().Enabled { + if option.Value == "true" && system_setting.GetOIDCSettings().ClientId == "" { c.JSON(http.StatusOK, gin.H{ "success": false, "message": "无法启用 OIDC 登录,请先填入 OIDC Client Id 以及 OIDC Client Secret!", }) + return } case "LinuxDOOAuthEnabled": if option.Value == "true" && common.LinuxDOClientId == "" { @@ -89,6 +90,15 @@ func UpdateOption(c *gin.Context) { "success": false, "message": "无法启用 Turnstile 校验,请先填入 Turnstile 校验相关配置信息!", }) + + return + } + case "TelegramOAuthEnabled": + if option.Value == "true" && common.TelegramBotToken == "" { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "无法启用 Telegram OAuth,请先填入 Telegram Bot Token!", + }) return } case "GroupRatio": @@ -100,6 +110,7 @@ func UpdateOption(c *gin.Context) { }) return } + } err = model.UpdateOption(option.Key, option.Value) if err != nil { diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index dde93d12..3c377a61 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -1,16 +1,23 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { Button, - Divider, Form, - Grid, - Header, - Message, + Row, + Col, + Typography, Modal, -} from 'semantic-ui-react'; -import { API, removeTrailingSlash, showError, showSuccess, verifyJSON } from '../helpers'; - -import { useTheme } from '../context/Theme'; + Banner, + TagInput, + Spin, +} from '@douyinfe/semi-ui'; +const { Text } = Typography; +import { + removeTrailingSlash, + showError, + showSuccess, + verifyJSON, +} from '../helpers/utils'; +import { API } from '../helpers/api'; const SystemSetting = () => { let [inputs, setInputs] = useState({ @@ -64,144 +71,138 @@ const SystemSetting = () => { LinuxDOClientId: '', LinuxDOClientSecret: '', }); - const [originInputs, setOriginInputs] = useState({}); - let [loading, setLoading] = useState(false); - const [EmailDomainWhitelist, setEmailDomainWhitelist] = useState([]); - const [restrictedDomainInput, setRestrictedDomainInput] = useState(''); - const [showPasswordWarningModal, setShowPasswordWarningModal] = - useState(false); - const theme = useTheme(); - const isDark = theme === 'dark'; + const [originInputs, setOriginInputs] = useState({}); + const [loading, setLoading] = useState(false); + const [isLoaded, setIsLoaded] = useState(false); + const formApiRef = useRef(null); + const [emailDomainWhitelist, setEmailDomainWhitelist] = useState([]); + const [showPasswordLoginConfirmModal, setShowPasswordLoginConfirmModal] = useState(false); + const [linuxDOOAuthEnabled, setLinuxDOOAuthEnabled] = useState(false); const getOptions = async () => { + setLoading(true); const res = await API.get('/api/option/'); const { success, message, data } = res.data; if (success) { let newInputs = {}; data.forEach((item) => { - if (item.key === 'TopupGroupRatio') { - item.value = JSON.stringify(JSON.parse(item.value), null, 2); + switch (item.key) { + case 'TopupGroupRatio': + item.value = JSON.stringify(JSON.parse(item.value), null, 2); + break; + case 'EmailDomainWhitelist': + setEmailDomainWhitelist(item.value ? item.value.split(',') : []); + break; + case 'PasswordLoginEnabled': + case 'PasswordRegisterEnabled': + case 'EmailVerificationEnabled': + case 'GitHubOAuthEnabled': + case 'WeChatAuthEnabled': + case 'TelegramOAuthEnabled': + case 'RegisterEnabled': + case 'TurnstileCheckEnabled': + case 'EmailDomainRestrictionEnabled': + case 'EmailAliasRestrictionEnabled': + case 'SMTPSSLEnabled': + case 'LinuxDOOAuthEnabled': + case 'oidc.enabled': + item.value = item.value === 'true'; + break; + case 'Price': + case 'MinTopUp': + item.value = parseFloat(item.value); + break; + default: + break; } newInputs[item.key] = item.value; }); - setInputs({ - ...newInputs, - EmailDomainWhitelist: newInputs.EmailDomainWhitelist.split(','), - }); + setInputs(newInputs); setOriginInputs(newInputs); - - setEmailDomainWhitelist( - newInputs.EmailDomainWhitelist.split(',').map((item) => { - return { key: item, text: item, value: item }; - }), - ); - } else { - showError(message); - } - }; - - useEffect(() => { - getOptions().then(); - }, []); - useEffect(() => {}, [inputs.EmailDomainWhitelist]); - - const updateOption = async (key, value) => { - setLoading(true); - switch (key) { - case 'PasswordLoginEnabled': - case 'PasswordRegisterEnabled': - case 'EmailVerificationEnabled': - case 'GitHubOAuthEnabled': - case 'oidc.enabled': - case 'LinuxDOOAuthEnabled': - case 'WeChatAuthEnabled': - case 'TelegramOAuthEnabled': - case 'TurnstileCheckEnabled': - case 'EmailDomainRestrictionEnabled': - case 'EmailAliasRestrictionEnabled': - case 'SMTPSSLEnabled': - case 'RegisterEnabled': - value = inputs[key] === 'true' ? 'false' : 'true'; - break; - default: - break; - } - const res = await API.put('/api/option/', { - key, - value, - }); - const { success, message } = res.data; - if (success) { - if (key === 'EmailDomainWhitelist') { - value = value.split(','); + if (formApiRef.current) { + formApiRef.current.setValues(newInputs); } - if (key === 'Price') { - value = parseFloat(value); - } - setInputs((inputs) => ({ - ...inputs, - [key]: value, - })); + setIsLoaded(true); } else { showError(message); } setLoading(false); }; - const handleInputChange = async (e, { name, value }) => { - if (name === 'PasswordLoginEnabled' && inputs[name] === 'true') { - // block disabling password login - setShowPasswordWarningModal(true); - return; - } - if ( - name === 'Notice' || - (name.startsWith('SMTP') && name !== 'SMTPSSLEnabled') || - name === 'ServerAddress' || - name === 'WorkerUrl' || - name === 'WorkerValidKey' || - name === 'EpayId' || - name === 'EpayKey' || - name === 'Price' || - name === 'PayAddress' || - name === 'GitHubClientId' || - name === 'GitHubClientSecret' || - name === 'oidc.well_known' || - name === 'oidc.client_id' || - name === 'oidc.client_secret' || - name === 'oidc.authorization_endpoint' || - name === 'oidc.token_endpoint' || - name === 'oidc.user_info_endpoint' || - name === 'WeChatServerAddress' || - name === 'WeChatServerToken' || - name === 'WeChatAccountQRCodeImageURL' || - name === 'TurnstileSiteKey' || - name === 'TurnstileSecretKey' || - name === 'EmailDomainWhitelist' || - name === 'TopupGroupRatio' || - name === 'TelegramBotToken' || - name === 'TelegramBotName' || - name === 'LinuxDOClientId' || - name === 'LinuxDOClientSecret' - ) { - setInputs((inputs) => ({ ...inputs, [name]: value })); - } else { - await updateOption(name, value); + useEffect(() => { + getOptions(); + }, []); + + const updateOptions = async (options) => { + setLoading(true); + try { + // 分离 checkbox 类型的选项和其他选项 + const checkboxOptions = options.filter(opt => + opt.key.toLowerCase().endsWith('enabled') + ); + const otherOptions = options.filter(opt => + !opt.key.toLowerCase().endsWith('enabled') + ); + + // 处理 checkbox 类型的选项 + for (const opt of checkboxOptions) { + const res = await API.put('/api/option/', { + key: opt.key, + value: opt.value.toString() + }); + if (!res.data.success) { + showError(res.data.message); + return; + } + } + + // 处理其他选项 + if (otherOptions.length > 0) { + const requestQueue = otherOptions.map(opt => + API.put('/api/option/', { + key: opt.key, + value: typeof opt.value === 'boolean' ? opt.value.toString() : opt.value + }) + ); + + const results = await Promise.all(requestQueue); + + // 检查所有请求是否成功 + const errorResults = results.filter(res => !res.data.success); + errorResults.forEach(res => { + showError(res.data.message); + }); + } + + showSuccess('更新成功'); + // 更新本地状态 + const newInputs = { ...inputs }; + options.forEach(opt => { + newInputs[opt.key] = opt.value; + }); + setInputs(newInputs); + } catch (error) { + showError('更新失败'); } + setLoading(false); + }; + + const handleFormChange = (values) => { + setInputs(values); }; const submitServerAddress = async () => { let ServerAddress = removeTrailingSlash(inputs.ServerAddress); - await updateOption('ServerAddress', ServerAddress); + await updateOptions([{ key: 'ServerAddress', value: ServerAddress }]); }; const submitWorker = async () => { let WorkerUrl = removeTrailingSlash(inputs.WorkerUrl); - await updateOption('WorkerUrl', WorkerUrl); - if (inputs.WorkerValidKey !== '') { - await updateOption('WorkerValidKey', inputs.WorkerValidKey); - } + await updateOptions([ + { key: 'WorkerUrl', value: WorkerUrl }, + { key: 'WorkerValidKey', value: inputs.WorkerValidKey } + ]); }; const submitPayAddress = async () => { @@ -214,89 +215,105 @@ const SystemSetting = () => { showError('充值分组倍率不是合法的 JSON 字符串'); return; } - await updateOption('TopupGroupRatio', inputs.TopupGroupRatio); } - let PayAddress = removeTrailingSlash(inputs.PayAddress); - await updateOption('PayAddress', PayAddress); + + const options = [ + { key: 'PayAddress', value: removeTrailingSlash(inputs.PayAddress) } + ]; + if (inputs.EpayId !== '') { - await updateOption('EpayId', inputs.EpayId); + options.push({ key: 'EpayId', value: inputs.EpayId }); } if (inputs.EpayKey !== undefined && inputs.EpayKey !== '') { - await updateOption('EpayKey', inputs.EpayKey); + options.push({ key: 'EpayKey', value: inputs.EpayKey }); } - await updateOption('Price', '' + inputs.Price); + if (inputs.Price !== '') { + options.push({ key: 'Price', value: inputs.Price.toString() }); + } + if (inputs.MinTopUp !== '') { + options.push({ key: 'MinTopUp', value: inputs.MinTopUp.toString() }); + } + if (inputs.CustomCallbackAddress !== '') { + options.push({ key: 'CustomCallbackAddress', value: inputs.CustomCallbackAddress }); + } + if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { + options.push({ key: 'TopupGroupRatio', value: inputs.TopupGroupRatio }); + } + + await updateOptions(options); }; const submitSMTP = async () => { + const options = []; + if (originInputs['SMTPServer'] !== inputs.SMTPServer) { - await updateOption('SMTPServer', inputs.SMTPServer); + options.push({ key: 'SMTPServer', value: inputs.SMTPServer }); } if (originInputs['SMTPAccount'] !== inputs.SMTPAccount) { - await updateOption('SMTPAccount', inputs.SMTPAccount); + options.push({ key: 'SMTPAccount', value: inputs.SMTPAccount }); } if (originInputs['SMTPFrom'] !== inputs.SMTPFrom) { - await updateOption('SMTPFrom', inputs.SMTPFrom); + options.push({ key: 'SMTPFrom', value: inputs.SMTPFrom }); } - if ( - originInputs['SMTPPort'] !== inputs.SMTPPort && - inputs.SMTPPort !== '' - ) { - await updateOption('SMTPPort', inputs.SMTPPort); + if (originInputs['SMTPPort'] !== inputs.SMTPPort && inputs.SMTPPort !== '') { + options.push({ key: 'SMTPPort', value: inputs.SMTPPort }); } - if ( - originInputs['SMTPToken'] !== inputs.SMTPToken && - inputs.SMTPToken !== '' - ) { - await updateOption('SMTPToken', inputs.SMTPToken); + if (originInputs['SMTPToken'] !== inputs.SMTPToken && inputs.SMTPToken !== '') { + options.push({ key: 'SMTPToken', value: inputs.SMTPToken }); + } + + if (options.length > 0) { + await updateOptions(options); } }; const submitEmailDomainWhitelist = async () => { - if ( - originInputs['EmailDomainWhitelist'] !== - inputs.EmailDomainWhitelist.join(',') && - inputs.SMTPToken !== '' - ) { - await updateOption( - 'EmailDomainWhitelist', - inputs.EmailDomainWhitelist.join(','), - ); + if (Array.isArray(emailDomainWhitelist)) { + await updateOptions([{ + key: 'EmailDomainWhitelist', + value: emailDomainWhitelist.join(',') + }]); + } else { + showError('邮箱域名白名单格式不正确'); } }; const submitWeChat = async () => { + const options = []; + if (originInputs['WeChatServerAddress'] !== inputs.WeChatServerAddress) { - await updateOption( - 'WeChatServerAddress', - removeTrailingSlash(inputs.WeChatServerAddress), - ); + options.push({ + key: 'WeChatServerAddress', + value: removeTrailingSlash(inputs.WeChatServerAddress) + }); } - if ( - originInputs['WeChatAccountQRCodeImageURL'] !== - inputs.WeChatAccountQRCodeImageURL - ) { - await updateOption( - 'WeChatAccountQRCodeImageURL', - inputs.WeChatAccountQRCodeImageURL, - ); + if (originInputs['WeChatAccountQRCodeImageURL'] !== inputs.WeChatAccountQRCodeImageURL) { + options.push({ + key: 'WeChatAccountQRCodeImageURL', + value: inputs.WeChatAccountQRCodeImageURL + }); } - if ( - originInputs['WeChatServerToken'] !== inputs.WeChatServerToken && - inputs.WeChatServerToken !== '' - ) { - await updateOption('WeChatServerToken', inputs.WeChatServerToken); + if (originInputs['WeChatServerToken'] !== inputs.WeChatServerToken && inputs.WeChatServerToken !== '') { + options.push({ key: 'WeChatServerToken', value: inputs.WeChatServerToken }); + } + + if (options.length > 0) { + await updateOptions(options); } }; const submitGitHubOAuth = async () => { + const options = []; + if (originInputs['GitHubClientId'] !== inputs.GitHubClientId) { - await updateOption('GitHubClientId', inputs.GitHubClientId); + options.push({ key: 'GitHubClientId', value: inputs.GitHubClientId }); } - if ( - originInputs['GitHubClientSecret'] !== inputs.GitHubClientSecret && - inputs.GitHubClientSecret !== '' - ) { - await updateOption('GitHubClientSecret', inputs.GitHubClientSecret); + if (originInputs['GitHubClientSecret'] !== inputs.GitHubClientSecret && inputs.GitHubClientSecret !== '') { + options.push({ key: 'GitHubClientSecret', value: inputs.GitHubClientSecret }); + } + + if (options.length > 0) { + await updateOptions(options); } }; @@ -315,679 +332,599 @@ const SystemSetting = () => { } catch (err) { console.error(err); showError("获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确"); + return; } } + const options = []; + if (originInputs['oidc.well_known'] !== inputs['oidc.well_known']) { - await updateOption('oidc.well_known', inputs['oidc.well_known']); + options.push({ key: 'oidc.well_known', value: inputs['oidc.well_known'] }); } if (originInputs['oidc.client_id'] !== inputs['oidc.client_id']) { - await updateOption('oidc.client_id', inputs['oidc.client_id']); + options.push({ key: 'oidc.client_id', value: inputs['oidc.client_id'] }); } if (originInputs['oidc.client_secret'] !== inputs['oidc.client_secret'] && inputs['oidc.client_secret'] !== '') { - await updateOption('oidc.client_secret', inputs['oidc.client_secret']); + options.push({ key: 'oidc.client_secret', value: inputs['oidc.client_secret'] }); } if (originInputs['oidc.authorization_endpoint'] !== inputs['oidc.authorization_endpoint']) { - await updateOption('oidc.authorization_endpoint', inputs['oidc.authorization_endpoint']); + options.push({ key: 'oidc.authorization_endpoint', value: inputs['oidc.authorization_endpoint'] }); } if (originInputs['oidc.token_endpoint'] !== inputs['oidc.token_endpoint']) { - await updateOption('oidc.token_endpoint', inputs['oidc.token_endpoint']); + options.push({ key: 'oidc.token_endpoint', value: inputs['oidc.token_endpoint'] }); } if (originInputs['oidc.user_info_endpoint'] !== inputs['oidc.user_info_endpoint']) { - await updateOption('oidc.user_info_endpoint', inputs['oidc.user_info_endpoint']); + options.push({ key: 'oidc.user_info_endpoint', value: inputs['oidc.user_info_endpoint'] }); } - } + + if (options.length > 0) { + await updateOptions(options); + } + }; const submitTelegramSettings = async () => { - // await updateOption('TelegramOAuthEnabled', inputs.TelegramOAuthEnabled); - await updateOption('TelegramBotToken', inputs.TelegramBotToken); - await updateOption('TelegramBotName', inputs.TelegramBotName); + const options = [ + { key: 'TelegramBotToken', value: inputs.TelegramBotToken }, + { key: 'TelegramBotName', value: inputs.TelegramBotName } + ]; + await updateOptions(options); }; const submitTurnstile = async () => { + const options = []; + if (originInputs['TurnstileSiteKey'] !== inputs.TurnstileSiteKey) { - await updateOption('TurnstileSiteKey', inputs.TurnstileSiteKey); + options.push({ key: 'TurnstileSiteKey', value: inputs.TurnstileSiteKey }); } - if ( - originInputs['TurnstileSecretKey'] !== inputs.TurnstileSecretKey && - inputs.TurnstileSecretKey !== '' - ) { - await updateOption('TurnstileSecretKey', inputs.TurnstileSecretKey); + if (originInputs['TurnstileSecretKey'] !== inputs.TurnstileSecretKey && inputs.TurnstileSecretKey !== '') { + options.push({ key: 'TurnstileSecretKey', value: inputs.TurnstileSecretKey }); } - }; - - const submitNewRestrictedDomain = () => { - const localDomainList = inputs.EmailDomainWhitelist; - if ( - restrictedDomainInput !== '' && - !localDomainList.includes(restrictedDomainInput) - ) { - setRestrictedDomainInput(''); - setInputs({ - ...inputs, - EmailDomainWhitelist: [...localDomainList, restrictedDomainInput], - }); - setEmailDomainWhitelist([ - ...EmailDomainWhitelist, - { - key: restrictedDomainInput, - text: restrictedDomainInput, - value: restrictedDomainInput, - }, - ]); + + if (options.length > 0) { + await updateOptions(options); } }; const submitLinuxDOOAuth = async () => { + const options = []; + if (originInputs['LinuxDOClientId'] !== inputs.LinuxDOClientId) { - await updateOption('LinuxDOClientId', inputs.LinuxDOClientId); + options.push({ key: 'LinuxDOClientId', value: inputs.LinuxDOClientId }); } - if ( - originInputs['LinuxDOClientSecret'] !== inputs.LinuxDOClientSecret && - inputs.LinuxDOClientSecret !== '' - ) { - await updateOption('LinuxDOClientSecret', inputs.LinuxDOClientSecret); + if (originInputs['LinuxDOClientSecret'] !== inputs.LinuxDOClientSecret && inputs.LinuxDOClientSecret !== '') { + options.push({ key: 'LinuxDOClientSecret', value: inputs.LinuxDOClientSecret }); + } + + if (options.length > 0) { + await updateOptions(options); } }; + const handleCheckboxChange = async (optionKey, event) => { + const value = event.target.checked; + + if (optionKey === 'PasswordLoginEnabled' && !value) { + setShowPasswordLoginConfirmModal(true); + } else { + await updateOptions([{ key: optionKey, value }]); + } + if (optionKey === 'LinuxDOOAuthEnabled') { + setLinuxDOOAuthEnabled(value); + } + }; + + const handlePasswordLoginConfirm = async () => { + await updateOptions([{ key: 'PasswordLoginEnabled', value: false }]); + setShowPasswordLoginConfirmModal(false); + }; + return ( - - -
-
- 通用设置 -
- - - - - 更新服务器地址 - -
- 代理设置(支持{' '} - - new-api-worker - - ) -
- - 注意:代理功能仅对图片请求和 Webhook 请求生效,不会影响其他 API 请求。如需配置 API 请求代理,请参考 - - {' '}API 代理设置文档 - - 。 - - - - - - 更新Worker设置 - -
- 支付设置(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!) -
- - - - - - - - - - - - - - 更新支付设置 - -
- 配置登录注册 -
- - - {showPasswordWarningModal && ( - setShowPasswordWarningModal(false)} - size={'tiny'} - style={{ maxWidth: '450px' }} - > - 警告 - -

- 取消密码登录将导致所有未绑定其他登录方式的用户(包括管理员)无法通过密码登录,确认取消? -

-
- - - + + + + + (支持{' '} + - 确定 - - -
- )} - - - - - - - -
- - - - - -
- 配置邮箱域名白名单 - - 用以防止恶意用户利用临时邮箱批量注册 - -
- - - - - - - - - { - submitNewRestrictedDomain(); - }} + new-api-worker +
+ ) + + + + + + + + + + + + + + + (当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!) + + + + + + + + + + + + + - 填入 + + + + + + + + + + + + + + + + + + + handleCheckboxChange('PasswordLoginEnabled', e) + } + > + 允许通过密码进行登录 + + + handleCheckboxChange('PasswordRegisterEnabled', e) + } + > + 允许通过密码进行注册 + + + handleCheckboxChange('EmailVerificationEnabled', e) + } + > + 通过密码注册时需要进行邮箱验证 + + handleCheckboxChange('RegisterEnabled', e)} + > + 允许新用户注册 + + + handleCheckboxChange('TurnstileCheckEnabled', e) + } + > + 启用 Turnstile 用户校验 + + + + + handleCheckboxChange('GitHubOAuthEnabled', e) + } + > + 允许通过 GitHub 账户登录 & 注册 + + + handleCheckboxChange('LinuxDOOAuthEnabled', e) + } + > + 允许通过 Linux DO 账户登录 & 注册 + + + handleCheckboxChange('WeChatAuthEnabled', e) + } + > + 允许通过微信登录 & 注册 + + + handleCheckboxChange('TelegramOAuthEnabled', e) + } + > + 允许通过 Telegram 进行登录 + + handleCheckboxChange('oidc.enabled', e)} + > + 允许通过 OIDC 进行登录 + + + + + + + 用以防止恶意用户利用临时邮箱批量注册 + + + + handleCheckboxChange('EmailDomainRestrictionEnabled', e) + } + > + 启用邮箱域名白名单 + + + + + handleCheckboxChange('EmailAliasRestrictionEnabled', e) + } + > + 启用邮箱别名限制 + + + + + - } - onKeyDown={(e) => { - if (e.key === 'Enter') { - submitNewRestrictedDomain(); - } - }} - autoComplete='new-password' - placeholder='输入新的允许的邮箱域名' - value={restrictedDomainInput} - onChange={(e, { value }) => { - setRestrictedDomainInput(value); - }} - /> - - - 保存邮箱域名白名单设置 - - -
- 配置 SMTP - 用以支持系统的邮件发送 -
- - - - - - - - - - - - - 保存 SMTP 设置 - -
- 配置 GitHub OAuth App - - 用以支持通过 GitHub 进行登录注册, - + + + 用以支持系统的邮件发送 + + + + + + + + + + + + + + + + + + + + handleCheckboxChange('SMTPSSLEnabled', e)} + > + 启用SMTP SSL + + + + + + + + 用以支持通过 OIDC 登录,例如 Okta、Auth0 等兼容 OIDC 协议的 IdP + + 若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 用以支持通过 GitHub 进行登录注册 + + + + + + + + + + + + + + 用以支持通过 Linux DO 进行登录注册 + + 点击此处 + + 管理你的 LinuxDO OAuth App + + + + + + + + + + + + + + 用以支持通过微信进行登录注册 + + + + + + + + + + + + + + + 用以支持通过 Telegram 进行登录注册 + + + + + + + + + + + + 用以支持用户校验 + + + + + + + + + + + + { + setShowPasswordLoginConfirmModal(false); + formApiRef.current.setValue('PasswordLoginEnabled', true); + }} + okText="确认" + cancelText="取消" > - 点击此处 - - 管理你的 GitHub OAuth App - -
- - Homepage URL 填 {inputs.ServerAddress} - ,Authorization callback URL 填{' '} - {`${inputs.ServerAddress}/oauth/github`} - - - - - - - 保存 GitHub OAuth 设置 - - -
- 配置 WeChat Server - - 用以支持通过微信进行登录注册, - - 点击此处 - - 了解 WeChat Server - -
- - - - - - - 保存 WeChat Server 设置 - - -
- 配置 Telegram 登录 -
- - - - - - 保存 Telegram 登录设置 - - -
- 配置 Turnstile - - 用以支持用户校验, - - 点击此处 - - 管理你的 Turnstile Sites,推荐选择 Invisible Widget Type - -
- - - - - - 保存 Turnstile 设置 - - -
- 配置 LinuxDO OAuth App - - 用以支持通过 LinuxDO 进行登录注册, - - 点击此处 - - 管理你的 LinuxDO OAuth App - -
- - Homepage URL 填 {inputs.ServerAddress} - ,Authorization callback URL 填{' '} - {`${inputs.ServerAddress}/oauth/linuxdo`} - - - - - - - 保存 LinuxDO OAuth 设置 - - -
- 配置 OIDC - - 用以支持通过 OIDC 登录,例如 Okta、Auth0 等兼容 OIDC 协议的 IdP - -
- - 主页链接填 { inputs.ServerAddress }, - 重定向 URL 填 { `${ inputs.ServerAddress }/oauth/oidc` } - - - 若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置 - - - - - - - - - - - 保存 OIDC 设置 - +

您确定要取消密码登录功能吗?这可能会影响用户的登录方式。

+ + + )} -
-
+ ) : ( +
+ +
+ )} + ); }; -export default SystemSetting; +export default SystemSetting; \ No newline at end of file