feat: i18n support

This commit is contained in:
JustSong 2025-02-02 00:05:40 +08:00
parent e183e3b9b0
commit b1fe81a84f
5 changed files with 558 additions and 241 deletions

View File

@ -429,5 +429,173 @@
"value": "Value"
}
}
},
"setting": {
"title": "System Settings",
"tabs": {
"personal": "Personal Settings",
"operation": "Operation Settings",
"system": "System Settings",
"other": "Other Settings"
},
"personal": {
"general": {
"title": "General Settings",
"system_token_notice": "Note: The token generated here is for system management, not for requesting OpenAI related services.",
"buttons": {
"update_profile": "Update Profile",
"generate_token": "Generate System Token",
"copy_invite": "Copy Invite Link",
"delete_account": "Delete Account"
}
},
"binding": {
"title": "Account Binding",
"buttons": {
"bind_wechat": "Bind WeChat Account",
"bind_github": "Bind GitHub Account",
"bind_email": "Bind Email Address",
"bind_lark": "Bind Lark Account"
},
"wechat": {
"title": "WeChat Binding",
"description": "Scan QR code to follow the official account, enter 'verification code' to get the code (valid for 3 minutes)",
"verification_code": "Verification Code",
"bind": "Bind"
},
"email": {
"title": "Bind Email Address",
"email_placeholder": "Enter email address",
"code_placeholder": "Verification code",
"get_code": "Get Code",
"get_code_retry": "Resend({{countdown}})",
"bind": "Confirm Binding",
"cancel": "Cancel"
}
},
"delete_account": {
"title": "Dangerous Operation",
"warning": "You are deleting your account. All data will be cleared and cannot be recovered",
"confirm_placeholder": "Enter your username {{username}} to confirm deletion",
"buttons": {
"confirm": "Confirm Delete",
"cancel": "Cancel"
}
}
},
"system": {
"general": {
"title": "General Settings",
"server_address": "Server Address",
"server_address_placeholder": "e.g.: https://yourdomain.com",
"buttons": {
"update": "Update Server Address"
}
},
"login": {
"title": "Login & Registration Settings",
"password_login": "Allow Password Login",
"password_register": "Allow Password Registration",
"email_verification": "Require Email Verification for Password Registration",
"github_oauth": "Allow GitHub OAuth Login & Registration",
"wechat_login": "Allow WeChat Login & Registration",
"registration": "Allow New User Registration (When disabled, new users cannot register by any means)",
"turnstile": "Enable Turnstile User Verification"
},
"email_restriction": {
"title": "Email Domain Whitelist",
"subtitle": "Used to prevent malicious users from batch registering using temporary emails",
"enable": "Enable Email Domain Whitelist",
"allowed_domains": "Allowed Email Domains",
"add_domain": "Add New Allowed Email Domain",
"add_domain_placeholder": "Enter new allowed email domain",
"buttons": {
"fill": "Fill",
"save": "Save Email Domain Whitelist Settings"
}
},
"smtp": {
"title": "SMTP Configuration",
"subtitle": "Used to support system email sending",
"server": "SMTP Server Address",
"server_placeholder": "e.g.: smtp.gmail.com",
"port": "SMTP Port",
"port_placeholder": "Default: 587",
"account": "SMTP Account",
"account_placeholder": "Usually your email address",
"from": "SMTP Sender Email",
"from_placeholder": "Usually same as email address",
"token": "SMTP Access Token",
"token_placeholder": "Sensitive information will not be sent to frontend",
"buttons": {
"save": "Save SMTP Settings"
}
},
"github": {
"title": "GitHub OAuth App Configuration",
"subtitle": "Used to support GitHub login and registration",
"manage_link": "Click here",
"manage_text": "to manage your GitHub OAuth Apps",
"url_notice": "Set Homepage URL to {{server_url}}, and Authorization callback URL to {{callback_url}}",
"client_id": "GitHub Client ID",
"client_id_placeholder": "Enter your registered GitHub OAuth APP ID",
"client_secret": "GitHub Client Secret",
"client_secret_placeholder": "Sensitive information will not be sent to frontend",
"buttons": {
"save": "Save GitHub OAuth Settings"
}
},
"lark": {
"title": "Lark OAuth Configuration",
"subtitle": "Used to support Lark login and registration",
"manage_link": "Click here",
"manage_text": "to manage your Lark applications",
"url_notice": "Set Homepage URL to {{server_url}}, and Redirect URL to {{callback_url}}",
"client_id": "App ID",
"client_id_placeholder": "Enter App ID",
"client_secret": "App Secret",
"client_secret_placeholder": "Sensitive information will not be sent to frontend",
"buttons": {
"save": "Save Lark OAuth Settings"
}
},
"wechat": {
"title": "WeChat Server Configuration",
"subtitle": "Used to support WeChat login and registration",
"learn_more": "Learn about WeChat Server",
"server_address": "WeChat Server Address",
"server_address_placeholder": "e.g.: https://yourdomain.com",
"token": "WeChat Server Access Token",
"token_placeholder": "Sensitive information will not be sent to frontend",
"qrcode": "WeChat Official Account QR Code Image URL",
"qrcode_placeholder": "Enter an image URL",
"buttons": {
"save": "Save WeChat Server Settings"
}
},
"turnstile": {
"title": "Turnstile Configuration",
"subtitle": "Used to support user verification",
"manage_link": "Click here",
"manage_text": "to manage your Turnstile Sites, Invisible Widget Type recommended",
"site_key": "Turnstile Site Key",
"site_key_placeholder": "Enter your registered Turnstile Site Key",
"secret_key": "Turnstile Secret Key",
"secret_key_placeholder": "Sensitive information will not be sent to frontend",
"buttons": {
"save": "Save Turnstile Settings"
}
},
"password_login": {
"warning": {
"title": "Warning",
"content": "Disabling password login will prevent all users (including administrators) who haven't bound other login methods from logging in via password. Confirm disable?",
"buttons": {
"confirm": "Confirm",
"cancel": "Cancel"
}
}
}
}
}
}

View File

@ -429,5 +429,173 @@
"value": "数值"
}
}
},
"setting": {
"title": "系统设置",
"tabs": {
"personal": "个人设置",
"operation": "运营设置",
"system": "系统设置",
"other": "其他设置"
},
"personal": {
"general": {
"title": "通用设置",
"system_token_notice": "注意,此处生成的令牌用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。",
"buttons": {
"update_profile": "更新个人信息",
"generate_token": "生成系统访问令牌",
"copy_invite": "复制邀请链接",
"delete_account": "删除个人账户"
}
},
"binding": {
"title": "账号绑定",
"buttons": {
"bind_wechat": "绑定微信账号",
"bind_github": "绑定 GitHub 账号",
"bind_email": "绑定邮箱地址",
"bind_lark": "绑定飞书账号"
},
"wechat": {
"title": "微信绑定",
"description": "微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)",
"verification_code": "验证码",
"bind": "绑定"
},
"email": {
"title": "绑定邮箱地址",
"email_placeholder": "输入邮箱地址",
"code_placeholder": "验证码",
"get_code": "获取验证码",
"get_code_retry": "重新发送({{countdown}})",
"bind": "确认绑定",
"cancel": "取消"
}
},
"delete_account": {
"title": "危险操作",
"warning": "您正在删除自己的帐户,将清空所有数据且不可恢复",
"confirm_placeholder": "输入你的账户名 {{username}} 以确认删除",
"buttons": {
"confirm": "确认删除",
"cancel": "取消"
}
}
},
"system": {
"general": {
"title": "通用设置",
"server_address": "服务器地址",
"server_address_placeholder": "例如https://yourdomain.com",
"buttons": {
"update": "更新服务器地址"
}
},
"login": {
"title": "配置登录注册",
"password_login": "允许通过密码进行登录",
"password_register": "允许通过密码进行注册",
"email_verification": "通过密码注册时需要进行邮箱验证",
"github_oauth": "允许通过 GitHub 账户登录 & 注册",
"wechat_login": "允许通过微信登录 & 注册",
"registration": "允许新用户注册(此项为否时,新用户将无法以任何方式进行注册)",
"turnstile": "启用 Turnstile 用户校验"
},
"email_restriction": {
"title": "配置邮箱域名白名单",
"subtitle": "用以防止恶意用户利用临时邮箱批量注册",
"enable": "启用邮箱域名白名单",
"allowed_domains": "允许的邮箱域名",
"add_domain": "添加新的允许的邮箱域名",
"add_domain_placeholder": "输入新的允许的邮箱域名",
"buttons": {
"fill": "填入",
"save": "保存邮箱域名白名单设置"
}
},
"smtp": {
"title": "配置 SMTP",
"subtitle": "用以支持系统的邮件发送",
"server": "SMTP 服务器地址",
"server_placeholder": "例如smtp.qq.com",
"port": "SMTP 端口",
"port_placeholder": "默认: 587",
"account": "SMTP 账户",
"account_placeholder": "通常是邮箱地址",
"from": "SMTP 发送者邮箱",
"from_placeholder": "通常和邮箱地址保持一致",
"token": "SMTP 访问凭证",
"token_placeholder": "敏感信息不会发送到前端显示",
"buttons": {
"save": "保存 SMTP 设置"
}
},
"github": {
"title": "配置 GitHub OAuth App",
"subtitle": "用以支持通过 GitHub 进行登录注册",
"manage_link": "点击此处",
"manage_text": "管理你的 GitHub OAuth App",
"url_notice": "Homepage URL 填 {{server_url}}Authorization callback URL 填 {{callback_url}}",
"client_id": "GitHub Client ID",
"client_id_placeholder": "输入你注册的 GitHub OAuth APP 的 ID",
"client_secret": "GitHub Client Secret",
"client_secret_placeholder": "敏感信息不会发送到前端显示",
"buttons": {
"save": "保存 GitHub OAuth 设置"
}
},
"lark": {
"title": "配置飞书授权登录",
"subtitle": "用以支持通过飞书进行登录注册",
"manage_link": "点击此处",
"manage_text": "管理你的飞书应用",
"url_notice": "主页链接填 {{server_url}},重定向 URL 填 {{callback_url}}",
"client_id": "App ID",
"client_id_placeholder": "输入 App ID",
"client_secret": "App Secret",
"client_secret_placeholder": "敏感信息不会发送到前端显示",
"buttons": {
"save": "保存飞书 OAuth 设置"
}
},
"wechat": {
"title": "配置 WeChat Server",
"subtitle": "用以支持通过微信进行登录注册",
"learn_more": "了解 WeChat Server",
"server_address": "WeChat Server 服务器地址",
"server_address_placeholder": "例如https://yourdomain.com",
"token": "WeChat Server 访问凭证",
"token_placeholder": "敏感信息不会发送到前端显示",
"qrcode": "微信公众号二维码图片链接",
"qrcode_placeholder": "输入一个图片链接",
"buttons": {
"save": "保存 WeChat Server 设置"
}
},
"turnstile": {
"title": "配置 Turnstile",
"subtitle": "用以支持用户校验",
"manage_link": "点击此处",
"manage_text": "管理你的 Turnstile Sites推荐选择 Invisible Widget Type",
"site_key": "Turnstile Site Key",
"site_key_placeholder": "输入你注册的 Turnstile Site Key",
"secret_key": "Turnstile Secret Key",
"secret_key_placeholder": "敏感信息不会发送到前端显示",
"buttons": {
"save": "保存 Turnstile 设置"
}
},
"password_login": {
"warning": {
"title": "警告",
"content": "取消密码登录将导致所有未绑定其他登录方式的用户(包括管理员)无法通过密码登录,确认取消?",
"buttons": {
"confirm": "确定",
"cancel": "取消"
}
}
}
}
}
}

View File

@ -1,4 +1,5 @@
import React, { useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Divider, Form, Header, Image, Message, Modal } from 'semantic-ui-react';
import { Link, useNavigate } from 'react-router-dom';
import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers';
@ -7,6 +8,7 @@ import { UserContext } from '../context/User';
import { onGitHubOAuthClicked, onLarkOAuthClicked } from './utils';
const PersonalSetting = () => {
const { t } = useTranslation();
const [userState, userDispatch] = useContext(UserContext);
let navigate = useNavigate();
@ -169,18 +171,24 @@ const PersonalSetting = () => {
return (
<div style={{ lineHeight: '40px' }}>
<Header as='h3'>通用设置</Header>
<Header as='h3'>{t('setting.personal.general.title')}</Header>
<Message>
注意此处生成的令牌用于系统管理而非用于请求 OpenAI 相关的服务请知悉
{t('setting.personal.general.system_token_notice')}
</Message>
<Button as={Link} to={`/user/edit/`}>
更新个人信息
{t('setting.personal.general.buttons.update_profile')}
</Button>
<Button onClick={generateAccessToken}>
{t('setting.personal.general.buttons.generate_token')}
</Button>
<Button onClick={getAffLink}>
{t('setting.personal.general.buttons.copy_invite')}
</Button>
<Button onClick={generateAccessToken}>生成系统访问令牌</Button>
<Button onClick={getAffLink}>复制邀请链接</Button>
<Button onClick={() => {
setShowAccountDeleteModal(true);
}}>删除个人账户</Button>
}}>
{t('setting.personal.general.buttons.delete_account')}
</Button>
{systemToken && (
<Form.Input
@ -201,18 +209,12 @@ const PersonalSetting = () => {
/>
)}
<Divider />
<Header as='h3'>账号绑定</Header>
{
status.wechat_login && (
<Button
onClick={() => {
setShowWeChatBindModal(true);
}}
>
绑定微信账号
</Button>
)
}
<Header as='h3'>{t('setting.personal.binding.title')}</Header>
{status.wechat_login && (
<Button onClick={() => setShowWeChatBindModal(true)}>
{t('setting.personal.binding.buttons.bind_wechat')}
</Button>
)}
<Modal
onClose={() => setShowWeChatBindModal(false)}
onOpen={() => setShowWeChatBindModal(true)}
@ -223,41 +225,35 @@ const PersonalSetting = () => {
<Modal.Description>
<Image src={status.wechat_qrcode} fluid />
<div style={{ textAlign: 'center' }}>
<p>
微信扫码关注公众号输入验证码获取验证码三分钟内有效
</p>
<p>{t('setting.personal.binding.wechat.description')}</p>
</div>
<Form size='large'>
<Form.Input
fluid
placeholder='验证码'
placeholder={t('setting.personal.binding.wechat.verification_code')}
name='wechat_verification_code'
value={inputs.wechat_verification_code}
onChange={handleInputChange}
/>
<Button color='' fluid size='large' onClick={bindWeChat}>
绑定
{t('setting.personal.binding.wechat.bind')}
</Button>
</Form>
</Modal.Description>
</Modal.Content>
</Modal>
{
status.github_oauth && (
<Button onClick={()=>{onGitHubOAuthClicked(status.github_client_id)}}>绑定 GitHub 账号</Button>
)
}
{
status.lark_client_id && (
<Button onClick={()=>{onLarkOAuthClicked(status.lark_client_id)}}>绑定飞书账号</Button>
)
}
<Button
onClick={() => {
setShowEmailBindModal(true);
}}
>
绑定邮箱地址
{status.github_oauth && (
<Button onClick={() => onGitHubOAuthClicked(status.github_client_id)}>
{t('setting.personal.binding.buttons.bind_github')}
</Button>
)}
{status.lark_client_id && (
<Button onClick={() => onLarkOAuthClicked(status.lark_client_id)}>
{t('setting.personal.binding.buttons.bind_lark')}
</Button>
)}
<Button onClick={() => setShowEmailBindModal(true)}>
{t('setting.personal.binding.buttons.bind_email')}
</Button>
<Modal
onClose={() => setShowEmailBindModal(false)}
@ -266,57 +262,57 @@ const PersonalSetting = () => {
size={'tiny'}
style={{ maxWidth: '450px' }}
>
<Modal.Header>绑定邮箱地址</Modal.Header>
<Modal.Header>{t('setting.personal.binding.email.title')}</Modal.Header>
<Modal.Content>
<Modal.Description>
<Form size='large'>
<Form.Input
fluid
placeholder='输入邮箱地址'
placeholder={t('setting.personal.binding.email.email_placeholder')}
onChange={handleInputChange}
name='email'
type='email'
action={
<Button onClick={sendVerificationCode} disabled={disableButton || loading}>
{disableButton ? `重新发送(${countdown})` : '获取验证码'}
{disableButton
? t('setting.personal.binding.email.get_code_retry', { countdown })
: t('setting.personal.binding.email.get_code')}
</Button>
}
/>
<Form.Input
fluid
placeholder='验证码'
placeholder={t('setting.personal.binding.email.code_placeholder')}
name='email_verification_code'
value={inputs.email_verification_code}
onChange={handleInputChange}
/>
{turnstileEnabled ? (
{turnstileEnabled && (
<Turnstile
sitekey={turnstileSiteKey}
onVerify={(token) => {
setTurnstileToken(token);
}}
/>
) : (
<></>
)}
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
<Button
color=''
fluid
size='large'
onClick={bindEmail}
loading={loading}
>
确认绑定
</Button>
<div style={{ width: '1rem' }}></div>
<Button
fluid
size='large'
onClick={() => setShowEmailBindModal(false)}
>
取消
</Button>
<Button
color=''
fluid
size='large'
onClick={bindEmail}
loading={loading}
>
{t('setting.personal.binding.email.bind')}
</Button>
<div style={{ width: '1rem' }}></div>
<Button
fluid
size='large'
onClick={() => setShowEmailBindModal(false)}
>
{t('setting.personal.binding.email.cancel')}
</Button>
</div>
</Form>
</Modal.Description>
@ -329,27 +325,27 @@ const PersonalSetting = () => {
size={'tiny'}
style={{ maxWidth: '450px' }}
>
<Modal.Header>危险操作</Modal.Header>
<Modal.Header>{t('setting.personal.delete_account.title')}</Modal.Header>
<Modal.Content>
<Message>您正在删除自己的帐户将清空所有数据且不可恢复</Message>
<Message>{t('setting.personal.delete_account.warning')}</Message>
<Modal.Description>
<Form size='large'>
<Form.Input
fluid
placeholder={`输入你的账户名 ${userState?.user?.username} 以确认删除`}
placeholder={t('setting.personal.delete_account.confirm_placeholder', {
username: userState?.user?.username
})}
name='self_account_deletion_confirmation'
value={inputs.self_account_deletion_confirmation}
onChange={handleInputChange}
/>
{turnstileEnabled ? (
{turnstileEnabled && (
<Turnstile
sitekey={turnstileSiteKey}
onVerify={(token) => {
setTurnstileToken(token);
}}
/>
) : (
<></>
)}
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
<Button
@ -359,7 +355,7 @@ const PersonalSetting = () => {
onClick={deleteAccount}
loading={loading}
>
确认删除
{t('setting.personal.delete_account.buttons.confirm')}
</Button>
<div style={{ width: '1rem' }}></div>
<Button
@ -367,7 +363,7 @@ const PersonalSetting = () => {
size='large'
onClick={() => setShowAccountDeleteModal(false)}
>
取消
{t('setting.personal.delete_account.buttons.cancel')}
</Button>
</div>
</Form>

View File

@ -1,8 +1,10 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Divider, Form, Grid, Header, Modal, Message } from 'semantic-ui-react';
import { API, removeTrailingSlash, showError } from '../helpers';
const SystemSetting = () => {
const { t } = useTranslation();
let [inputs, setInputs] = useState({
PasswordLoginEnabled: '',
PasswordRegisterEnabled: '',
@ -260,42 +262,43 @@ const SystemSetting = () => {
<Grid columns={1}>
<Grid.Column>
<Form loading={loading}>
<Header as='h3'>通用设置</Header>
<Header as='h3'>{t('setting.system.general.title')}</Header>
<Form.Group widths='equal'>
<Form.Input
label='服务器地址'
placeholder='例如https://yourdomain.com'
label={t('setting.system.general.server_address')}
placeholder={t('setting.system.general.server_address_placeholder')}
value={inputs.ServerAddress}
name='ServerAddress'
onChange={handleInputChange}
/>
</Form.Group>
<Form.Button onClick={submitServerAddress}>
更新服务器地址
{t('setting.system.general.buttons.update')}
</Form.Button>
<Divider />
<Header as='h3'>配置登录注册</Header>
<Header as='h3'>{t('setting.system.login.title')}</Header>
<Form.Group inline>
<Form.Checkbox
checked={inputs.PasswordLoginEnabled === 'true'}
label='允许通过密码进行登录'
label={t('setting.system.login.password_login')}
name='PasswordLoginEnabled'
onChange={handleInputChange}
/>
{
showPasswordWarningModal &&
{showPasswordWarningModal && (
<Modal
open={showPasswordWarningModal}
onClose={() => setShowPasswordWarningModal(false)}
size={'tiny'}
style={{ maxWidth: '450px' }}
>
<Modal.Header>警告</Modal.Header>
<Modal.Header>{t('setting.system.password_login.warning.title')}</Modal.Header>
<Modal.Content>
<p>取消密码登录将导致所有未绑定其他登录方式的用户包括管理员无法通过密码登录确认取消</p>
<p>{t('setting.system.password_login.warning.content')}</p>
</Modal.Content>
<Modal.Actions>
<Button onClick={() => setShowPasswordWarningModal(false)}>取消</Button>
<Button onClick={() => setShowPasswordWarningModal(false)}>
{t('setting.system.password_login.warning.buttons.cancel')}
</Button>
<Button
color='yellow'
onClick={async () => {
@ -303,32 +306,32 @@ const SystemSetting = () => {
await updateOption('PasswordLoginEnabled', 'false');
}}
>
确定
{t('setting.system.password_login.warning.buttons.confirm')}
</Button>
</Modal.Actions>
</Modal>
}
)}
<Form.Checkbox
checked={inputs.PasswordRegisterEnabled === 'true'}
label='允许通过密码进行注册'
label={t('setting.system.login.password_register')}
name='PasswordRegisterEnabled'
onChange={handleInputChange}
/>
<Form.Checkbox
checked={inputs.EmailVerificationEnabled === 'true'}
label='通过密码注册时需要进行邮箱验证'
label={t('setting.system.login.email_verification')}
name='EmailVerificationEnabled'
onChange={handleInputChange}
/>
<Form.Checkbox
checked={inputs.GitHubOAuthEnabled === 'true'}
label='允许通过 GitHub 账户登录 & 注册'
label={t('setting.system.login.github_oauth')}
name='GitHubOAuthEnabled'
onChange={handleInputChange}
/>
<Form.Checkbox
checked={inputs.WeChatAuthEnabled === 'true'}
label='允许通过微信登录 & 注册'
label={t('setting.system.login.wechat_login')}
name='WeChatAuthEnabled'
onChange={handleInputChange}
/>
@ -336,304 +339,283 @@ const SystemSetting = () => {
<Form.Group inline>
<Form.Checkbox
checked={inputs.RegisterEnabled === 'true'}
label='允许新用户注册(此项为否时,新用户将无法以任何方式进行注册)'
label={t('setting.system.login.registration')}
name='RegisterEnabled'
onChange={handleInputChange}
/>
<Form.Checkbox
checked={inputs.TurnstileCheckEnabled === 'true'}
label='启用 Turnstile 用户校验'
label={t('setting.system.login.turnstile')}
name='TurnstileCheckEnabled'
onChange={handleInputChange}
/>
</Form.Group>
<Divider />
<Header as='h3'>
配置邮箱域名白名单
<Header.Subheader>用以防止恶意用户利用临时邮箱批量注册</Header.Subheader>
</Header>
<Form.Group widths={3}>
<Header as='h3'>{t('setting.system.email_restriction.title')}</Header>
<Message>{t('setting.system.email_restriction.subtitle')}</Message>
<Form.Group inline>
<Form.Checkbox
label='启用邮箱域名白名单'
checked={inputs.EmailDomainRestrictionEnabled === 'true'}
label={t('setting.system.email_restriction.enable')}
name='EmailDomainRestrictionEnabled'
onChange={handleInputChange}
checked={inputs.EmailDomainRestrictionEnabled === 'true'}
/>
</Form.Group>
<Form.Group widths={2}>
<Form.Dropdown
label='允许的邮箱域名'
placeholder='允许的邮箱域名'
name='EmailDomainWhitelist'
required
fluid
multiple
selection
onChange={handleInputChange}
value={inputs.EmailDomainWhitelist}
autoComplete='new-password'
options={EmailDomainWhitelist}
/>
<Form.Group widths={3}>
<Form.Input
label='添加新的允许的邮箱域名'
action={
<Button type='button' onClick={() => {
submitNewRestrictedDomain();
}}>填入</Button>
}
onKeyDown={(e) => {
if (e.key === 'Enter') {
submitNewRestrictedDomain();
}
}}
autoComplete='new-password'
placeholder='输入新的允许的邮箱域名'
label={t('setting.system.email_restriction.add_domain')}
placeholder={t('setting.system.email_restriction.add_domain_placeholder')}
value={restrictedDomainInput}
onChange={(e, { value }) => {
setRestrictedDomainInput(value);
}}
action={
<Button onClick={() => {
if (restrictedDomainInput === '') return;
setEmailDomainWhitelist([...EmailDomainWhitelist, {
key: restrictedDomainInput,
text: restrictedDomainInput,
value: restrictedDomainInput
}]);
setRestrictedDomainInput('');
}}>
{t('setting.system.email_restriction.buttons.fill')}
</Button>
}
/>
</Form.Group>
<Form.Button onClick={submitEmailDomainWhitelist}>保存邮箱域名白名单设置</Form.Button>
<Form.Dropdown
label={t('setting.system.email_restriction.allowed_domains')}
placeholder={t('setting.system.email_restriction.allowed_domains')}
fluid
multiple
search
selection
allowAdditions
value={EmailDomainWhitelist.map(item => item.value)}
options={EmailDomainWhitelist}
onAddItem={(e, { value }) => {
setEmailDomainWhitelist([...EmailDomainWhitelist, {
key: value,
text: value,
value: value
}]);
}}
onChange={(e, { value }) => {
let newEmailDomainWhitelist = [];
value.forEach((item) => {
newEmailDomainWhitelist.push({
key: item,
text: item,
value: item
});
});
setEmailDomainWhitelist(newEmailDomainWhitelist);
}}
/>
<Form.Button onClick={submitEmailDomainWhitelist}>
{t('setting.system.email_restriction.buttons.save')}
</Form.Button>
<Divider />
<Header as='h3'>
配置 SMTP
<Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
</Header>
<Header as='h3'>{t('setting.system.smtp.title')}</Header>
<Message>{t('setting.system.smtp.subtitle')}</Message>
<Form.Group widths={3}>
<Form.Input
label='SMTP 服务器地址'
label={t('setting.system.smtp.server')}
placeholder={t('setting.system.smtp.server_placeholder')}
name='SMTPServer'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.SMTPServer}
placeholder='例如smtp.qq.com'
/>
<Form.Input
label='SMTP 端口'
label={t('setting.system.smtp.port')}
placeholder={t('setting.system.smtp.port_placeholder')}
name='SMTPPort'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.SMTPPort}
placeholder='默认: 587'
/>
<Form.Input
label='SMTP 账户'
label={t('setting.system.smtp.account')}
placeholder={t('setting.system.smtp.account_placeholder')}
name='SMTPAccount'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.SMTPAccount}
placeholder='通常是邮箱地址'
/>
</Form.Group>
<Form.Group widths={3}>
<Form.Input
label='SMTP 发送者邮箱'
label={t('setting.system.smtp.from')}
placeholder={t('setting.system.smtp.from_placeholder')}
name='SMTPFrom'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.SMTPFrom}
placeholder='通常和邮箱地址保持一致'
/>
<Form.Input
label='SMTP 访问凭证'
label={t('setting.system.smtp.token')}
placeholder={t('setting.system.smtp.token_placeholder')}
name='SMTPToken'
onChange={handleInputChange}
type='password'
autoComplete='new-password'
checked={inputs.RegisterEnabled === 'true'}
placeholder='敏感信息不会发送到前端显示'
value={inputs.SMTPToken}
/>
</Form.Group>
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
<Form.Button onClick={submitSMTP}>
{t('setting.system.smtp.buttons.save')}
</Form.Button>
<Divider />
<Header as='h3'>
配置 GitHub OAuth App
<Header.Subheader>
用以支持通过 GitHub 进行登录注册
<a href='https://github.com/settings/developers' target='_blank'>
点击此处
</a>
管理你的 GitHub OAuth App
</Header.Subheader>
</Header>
<Header as='h3'>{t('setting.system.github.title')}</Header>
<Message>
Homepage URL <code>{inputs.ServerAddress}</code>
Authorization callback URL {' '}
<code>{`${inputs.ServerAddress}/oauth/github`}</code>
{t('setting.system.github.subtitle')}
<a href='https://github.com/settings/developers' target='_blank'>
{t('setting.system.github.manage_link')}
</a>
{t('setting.system.github.manage_text')}
</Message>
<Message>
{t('setting.system.github.url_notice', {
server_url: originInputs.ServerAddress,
callback_url: `${originInputs.ServerAddress}/oauth/github`
})}
</Message>
<Form.Group widths={3}>
<Form.Input
label='GitHub Client ID'
label={t('setting.system.github.client_id')}
placeholder={t('setting.system.github.client_id_placeholder')}
name='GitHubClientId'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.GitHubClientId}
placeholder='输入你注册的 GitHub OAuth APP 的 ID'
/>
<Form.Input
label='GitHub Client Secret'
label={t('setting.system.github.client_secret')}
placeholder={t('setting.system.github.client_secret_placeholder')}
name='GitHubClientSecret'
onChange={handleInputChange}
type='password'
autoComplete='new-password'
value={inputs.GitHubClientSecret}
placeholder='敏感信息不会发送到前端显示'
/>
</Form.Group>
<Form.Button onClick={submitGitHubOAuth}>
保存 GitHub OAuth 设置
{t('setting.system.github.buttons.save')}
</Form.Button>
<Divider />
<Header as='h3'>
配置飞书授权登录
{t('setting.system.lark.title')}
<Header.Subheader>
用以支持通过飞书进行登录注册
{t('setting.system.lark.subtitle')}
<a href='https://open.feishu.cn/app' target='_blank'>
点击此处
{t('setting.system.lark.manage_link')}
</a>
管理你的飞书应用
{t('setting.system.lark.manage_text')}
</Header.Subheader>
</Header>
<Message>
主页链接填 <code>{inputs.ServerAddress}</code>
重定向 URL {' '}
<code>{`${inputs.ServerAddress}/oauth/lark`}</code>
{t('setting.system.lark.url_notice', {
server_url: inputs.ServerAddress,
callback_url: `${inputs.ServerAddress}/oauth/lark`
})}
</Message>
<Form.Group widths={3}>
<Form.Input
label='App ID'
label={t('setting.system.lark.client_id')}
name='LarkClientId'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.LarkClientId}
placeholder='输入 App ID'
placeholder={t('setting.system.lark.client_id_placeholder')}
/>
<Form.Input
label='App Secret'
label={t('setting.system.lark.client_secret')}
name='LarkClientSecret'
onChange={handleInputChange}
type='password'
autoComplete='new-password'
value={inputs.LarkClientSecret}
placeholder='敏感信息不会发送到前端显示'
placeholder={t('setting.system.lark.client_secret_placeholder')}
/>
</Form.Group>
<Form.Button onClick={submitLarkOAuth}>
保存飞书 OAuth 设置
{t('setting.system.lark.buttons.save')}
</Form.Button>
<Divider />
<Header as='h3'>
配置 WeChat Server
{t('setting.system.wechat.title')}
<Header.Subheader>
用以支持通过微信进行登录注册
{t('setting.system.wechat.subtitle')}
<a
href='https://github.com/songquanpeng/wechat-server'
target='_blank'
>
点击此处
{t('setting.system.wechat.learn_more')}
</a>
了解 WeChat Server
</Header.Subheader>
</Header>
<Form.Group widths={3}>
<Form.Input
label='WeChat Server 服务器地址'
label={t('setting.system.wechat.server_address')}
name='WeChatServerAddress'
placeholder='例如https://yourdomain.com'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.WeChatServerAddress}
placeholder={t('setting.system.wechat.server_address_placeholder')}
/>
<Form.Input
label='WeChat Server 访问凭证'
label={t('setting.system.wechat.token')}
name='WeChatServerToken'
type='password'
onChange={handleInputChange}
type='password'
autoComplete='new-password'
value={inputs.WeChatServerToken}
placeholder='敏感信息不会发送到前端显示'
placeholder={t('setting.system.wechat.token_placeholder')}
/>
<Form.Input
label='微信公众号二维码图片链接'
label={t('setting.system.wechat.qrcode')}
name='WeChatAccountQRCodeImageURL'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.WeChatAccountQRCodeImageURL}
placeholder='输入一个图片链接'
placeholder={t('setting.system.wechat.qrcode_placeholder')}
/>
</Form.Group>
<Form.Button onClick={submitWeChat}>
保存 WeChat Server 设置
{t('setting.system.wechat.buttons.save')}
</Form.Button>
<Divider />
<Header as='h3'>
配置 Message Pusher
{t('setting.system.turnstile.title')}
<Header.Subheader>
用以推送报警信息
<a
href='https://github.com/songquanpeng/message-pusher'
target='_blank'
>
点击此处
</a>
了解 Message Pusher
</Header.Subheader>
</Header>
<Form.Group widths={3}>
<Form.Input
label='Message Pusher 推送地址'
name='MessagePusherAddress'
placeholder='例如https://msgpusher.com/push/your_username'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.MessagePusherAddress}
/>
<Form.Input
label='Message Pusher 访问凭证'
name='MessagePusherToken'
type='password'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.MessagePusherToken}
placeholder='敏感信息不会发送到前端显示'
/>
</Form.Group>
<Form.Button onClick={submitMessagePusher}>
保存 Message Pusher 设置
</Form.Button>
<Divider />
<Header as='h3'>
配置 Turnstile
<Header.Subheader>
用以支持用户校验
{t('setting.system.turnstile.subtitle')}
<a href='https://dash.cloudflare.com/' target='_blank'>
点击此处
{t('setting.system.turnstile.manage_link')}
</a>
管理你的 Turnstile Sites推荐选择 Invisible Widget Type
{t('setting.system.turnstile.manage_text')}
</Header.Subheader>
</Header>
<Form.Group widths={3}>
<Form.Input
label='Turnstile Site Key'
label={t('setting.system.turnstile.site_key')}
name='TurnstileSiteKey'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.TurnstileSiteKey}
placeholder='输入你注册的 Turnstile Site Key'
placeholder={t('setting.system.turnstile.site_key_placeholder')}
/>
<Form.Input
label='Turnstile Secret Key'
label={t('setting.system.turnstile.secret_key')}
name='TurnstileSecretKey'
onChange={handleInputChange}
type='password'
autoComplete='new-password'
value={inputs.TurnstileSecretKey}
placeholder='敏感信息不会发送到前端显示'
placeholder={t('setting.system.turnstile.secret_key_placeholder')}
/>
</Form.Group>
<Form.Button onClick={submitTurnstile}>
保存 Turnstile 设置
{t('setting.system.turnstile.buttons.save')}
</Form.Button>
</Form>
</Grid.Column>

View File

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Card, Tab } from 'semantic-ui-react';
import SystemSetting from '../../components/SystemSetting';
import { isRoot } from '../../helpers';
@ -7,9 +8,11 @@ import PersonalSetting from '../../components/PersonalSetting';
import OperationSetting from '../../components/OperationSetting';
const Setting = () => {
const { t } = useTranslation();
let panes = [
{
menuItem: '个人设置',
menuItem: t('setting.tabs.personal'),
render: () => (
<Tab.Pane attached={false}>
<PersonalSetting />
@ -20,7 +23,7 @@ const Setting = () => {
if (isRoot()) {
panes.push({
menuItem: '运营设置',
menuItem: t('setting.tabs.operation'),
render: () => (
<Tab.Pane attached={false}>
<OperationSetting />
@ -28,7 +31,7 @@ const Setting = () => {
),
});
panes.push({
menuItem: '系统设置',
menuItem: t('setting.tabs.system'),
render: () => (
<Tab.Pane attached={false}>
<SystemSetting />
@ -36,7 +39,7 @@ const Setting = () => {
),
});
panes.push({
menuItem: '其他设置',
menuItem: t('setting.tabs.other'),
render: () => (
<Tab.Pane attached={false}>
<OtherSetting />
@ -49,12 +52,12 @@ const Setting = () => {
<div className='dashboard-container'>
<Card fluid className='chart-card'>
<Card.Content>
<Card.Header className='header'>系统设置</Card.Header>
<Card.Header className='header'>{t('setting.title')}</Card.Header>
<Tab
menu={{
secondary: true,
pointing: true,
className: 'settings-tab', // 添加自定义类名以便样式化
className: 'settings-tab',
}}
panes={panes}
/>