import React, { useEffect, useState } from 'react'; import { Button, Divider, Form, Grid, Header, Message, Modal, } from 'semantic-ui-react'; import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers'; const SystemSetting = () => { let [inputs, setInputs] = useState({ PasswordLoginEnabled: '', PasswordRegisterEnabled: '', EmailVerificationEnabled: '', GitHubOAuthEnabled: '', GitHubClientId: '', GitHubClientSecret: '', LinuxDoOAuthEnabled: '', LinuxDoClientId: '', LinuxDoClientSecret: '', LinuxDoMinLevel: 0, Notice: '', SMTPServer: '', SMTPPort: '', SMTPAccount: '', SMTPFrom: '', SMTPToken: '', ServerAddress: '', StripeApiSecret: '', StripeWebhookSecret: '', StripePriceId: '', PaymentEnabled: false, StripeUnitPrice: 8.0, MinTopUp: 5, TopupGroupRatio: '', Footer: '', WeChatAuthEnabled: '', WeChatServerAddress: '', WeChatServerToken: '', WeChatAccountQRCodeImageURL: '', TurnstileCheckEnabled: '', TurnstileSiteKey: '', TurnstileSecretKey: '', RegisterEnabled: '', UserSelfDeletionEnabled: false, EmailDomainRestrictionEnabled: '', EmailDomainWhitelist: [], // telegram login TelegramOAuthEnabled: '', TelegramBotToken: '', TelegramBotName: '', }); const [originInputs, setOriginInputs] = useState({}); let [loading, setLoading] = useState(false); const [EmailDomainWhitelist, setEmailDomainWhitelist] = useState([]); const [restrictedDomainInput, setRestrictedDomainInput] = useState(''); const [showPasswordWarningModal, setShowPasswordWarningModal] = useState(false); const getOptions = async () => { 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); } newInputs[item.key] = item.value; }); setInputs({ ...newInputs, EmailDomainWhitelist: newInputs.EmailDomainWhitelist.split(','), }); 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 'LinuxDoOAuthEnabled': case 'WeChatAuthEnabled': case 'TelegramOAuthEnabled': case 'TurnstileCheckEnabled': case 'EmailDomainRestrictionEnabled': case 'RegisterEnabled': case 'UserSelfDeletionEnabled': case 'PaymentEnabled': 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(','); } setInputs((inputs) => ({ ...inputs, [key]: value, })); } 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 === 'ServerAddress' || name === 'StripeApiSecret' || name === 'StripeWebhookSecret' || name === 'StripePriceId' || name === 'StripeUnitPrice' || name === 'MinTopUp' || name === 'GitHubClientId' || name === 'GitHubClientSecret' || name === 'LinuxDoClientId' || name === 'LinuxDoClientSecret' || name === 'LinuxDoMinLevel' || name === 'WeChatServerAddress' || name === 'WeChatServerToken' || name === 'WeChatAccountQRCodeImageURL' || name === 'TurnstileSiteKey' || name === 'TurnstileSecretKey' || name === 'EmailDomainWhitelist' || name === 'TopupGroupRatio' || name === 'TelegramBotToken' || name === 'TelegramBotName' ) { setInputs((inputs) => ({ ...inputs, [name]: value })); } else { await updateOption(name, value); } }; const submitServerAddress = async () => { let ServerAddress = removeTrailingSlash(inputs.ServerAddress); await updateOption('ServerAddress', ServerAddress); }; const submitPaymentConfig = async () => { if (inputs.ServerAddress === '') { showError('请先填写服务器地址'); return; } if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { if (!verifyJSON(inputs.TopupGroupRatio)) { showError('充值分组倍率不是合法的 JSON 字符串'); return; } await updateOption('TopupGroupRatio', inputs.TopupGroupRatio); } let stripeApiSecret = removeTrailingSlash(inputs.StripeApiSecret); if (stripeApiSecret && !stripeApiSecret.startsWith('sk_')) { showError('输入了无效的Stripe API密钥'); return; } stripeApiSecret && (await updateOption('StripeApiSecret', stripeApiSecret)); let stripeWebhookSecret = removeTrailingSlash(inputs.StripeWebhookSecret); if (stripeWebhookSecret && !stripeWebhookSecret.startsWith('whsec_')) { showError('输入了无效的Stripe Webhook签名密钥'); return; } stripeWebhookSecret && (await updateOption('StripeWebhookSecret', stripeWebhookSecret)); let stripePriceId = removeTrailingSlash(inputs.StripePriceId); if (stripePriceId && !stripePriceId.startsWith('price_')) { showError('输入了无效的Stripe 物品价格ID'); return; } await updateOption('StripePriceId', stripePriceId); await updateOption('PaymentEnable', inputs.PaymentEnabled); await updateOption('StripeUnitPrice', inputs.StripeUnitPrice); await updateOption('MinTopUp', inputs.MinTopUp); }; const submitSMTP = async () => { if (originInputs['SMTPServer'] !== inputs.SMTPServer) { await updateOption('SMTPServer', inputs.SMTPServer); } if (originInputs['SMTPAccount'] !== inputs.SMTPAccount) { await updateOption('SMTPAccount', inputs.SMTPAccount); } if (originInputs['SMTPFrom'] !== inputs.SMTPFrom) { await updateOption('SMTPFrom', inputs.SMTPFrom); } if ( originInputs['SMTPPort'] !== inputs.SMTPPort && inputs.SMTPPort !== '' ) { await updateOption('SMTPPort', inputs.SMTPPort); } if ( originInputs['SMTPToken'] !== inputs.SMTPToken && inputs.SMTPToken !== '' ) { await updateOption('SMTPToken', inputs.SMTPToken); } }; const submitEmailDomainWhitelist = async () => { if ( originInputs['EmailDomainWhitelist'] !== inputs.EmailDomainWhitelist.join(',') && inputs.SMTPToken !== '' ) { await updateOption( 'EmailDomainWhitelist', inputs.EmailDomainWhitelist.join(','), ); } }; const submitWeChat = async () => { if (originInputs['WeChatServerAddress'] !== inputs.WeChatServerAddress) { await updateOption( 'WeChatServerAddress', removeTrailingSlash(inputs.WeChatServerAddress), ); } if ( originInputs['WeChatAccountQRCodeImageURL'] !== inputs.WeChatAccountQRCodeImageURL ) { await updateOption( 'WeChatAccountQRCodeImageURL', inputs.WeChatAccountQRCodeImageURL, ); } if ( originInputs['WeChatServerToken'] !== inputs.WeChatServerToken && inputs.WeChatServerToken !== '' ) { await updateOption('WeChatServerToken', inputs.WeChatServerToken); } }; const submitGitHubOAuth = async () => { if (originInputs['GitHubClientId'] !== inputs.GitHubClientId) { await updateOption('GitHubClientId', inputs.GitHubClientId); } if ( originInputs['GitHubClientSecret'] !== inputs.GitHubClientSecret && inputs.GitHubClientSecret !== '' ) { await updateOption('GitHubClientSecret', inputs.GitHubClientSecret); } }; const submitLinuxDoOAuth = async () => { if (originInputs['LinuxDoClientId'] !== inputs.LinuxDoClientId) { await updateOption('LinuxDoClientId', inputs.LinuxDoClientId); } if ( originInputs['LinuxDoClientSecret'] !== inputs.LinuxDoClientSecret && inputs.LinuxDoClientSecret !== '' ) { await updateOption('LinuxDoClientSecret', inputs.LinuxDoClientSecret); } if (originInputs['LinuxDoMinLevel'] !== inputs.LinuxDoMinLevel) { await updateOption('LinuxDoMinLevel', inputs.LinuxDoMinLevel); } }; const submitTelegramSettings = async () => { // await updateOption('TelegramOAuthEnabled', inputs.TelegramOAuthEnabled); await updateOption('TelegramBotToken', inputs.TelegramBotToken); await updateOption('TelegramBotName', inputs.TelegramBotName); }; const submitTurnstile = async () => { if (originInputs['TurnstileSiteKey'] !== inputs.TurnstileSiteKey) { await updateOption('TurnstileSiteKey', inputs.TurnstileSiteKey); } if ( originInputs['TurnstileSecretKey'] !== inputs.TurnstileSecretKey && inputs.TurnstileSecretKey !== '' ) { await updateOption('TurnstileSecretKey', 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, }, ]); } }; return (
通用设置
更新服务器地址
支付设置(当前仅支持Stripe Checkout) 密钥、Webhook 等设置请 点击此处 进行设置,最好先在 测试环境 进行测试
Webhook 填: {`${inputs.ServerAddress}/api/stripe/webhook`} ,需要包含事件:checkout.session.completed 和{' '} checkout.session.expired 更新支付设置
配置登录注册
{showPasswordWarningModal && ( setShowPasswordWarningModal(false)} size={'tiny'} style={{ maxWidth: '450px' }} > 警告

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

)}
配置邮箱域名白名单 用以防止恶意用户利用临时邮箱批量注册
{ submitNewRestrictedDomain(); }} > 填入 } onKeyDown={(e) => { if (e.key === 'Enter') { submitNewRestrictedDomain(); } }} autoComplete='new-password' placeholder='输入新的允许的邮箱域名' value={restrictedDomainInput} onChange={(e, { value }) => { setRestrictedDomainInput(value); }} /> 保存邮箱域名白名单设置
配置 SMTP 用以支持系统的邮件发送
保存 SMTP 设置
配置 GitHub OAuth App 用以支持通过 GitHub 进行登录注册, 点击此处 管理你的 GitHub OAuth App
Homepage URL 填 {inputs.ServerAddress} ,Authorization callback URL 填{' '} {`${inputs.ServerAddress}/oauth/github`} 保存 GitHub OAuth 设置
配置 LINUX DO Oauth 用以支持通过 LINUX DO 进行登录注册, 点击此处 管理你的 LINUX DO OAuth
Homepage URL 填 {inputs.ServerAddress} ,Authorization callback URL 填{' '} {`${inputs.ServerAddress}/oauth/linuxdo`} 保存 LINUX DO OAuth 设置
配置 WeChat Server 用以支持通过微信进行登录注册, 点击此处 了解 WeChat Server
保存 WeChat Server 设置
配置 Telegram 登录
保存 Telegram 登录设置
配置 Turnstile 用以支持用户校验, 点击此处 管理你的 Turnstile Sites,推荐选择 Invisible Widget Type
保存 Turnstile 设置
); }; export default SystemSetting;