SystemSetting.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. import React, { useEffect, useState } from 'react';
  2. import {
  3. Button,
  4. Divider,
  5. Form,
  6. Grid,
  7. Header,
  8. Message,
  9. Modal,
  10. } from 'semantic-ui-react';
  11. import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers';
  12. import { useTheme } from '../context/Theme';
  13. const SystemSetting = () => {
  14. let [inputs, setInputs] = useState({
  15. PasswordLoginEnabled: '',
  16. PasswordRegisterEnabled: '',
  17. EmailVerificationEnabled: '',
  18. GitHubOAuthEnabled: '',
  19. GitHubClientId: '',
  20. GitHubClientSecret: '',
  21. Notice: '',
  22. SMTPServer: '',
  23. SMTPPort: '',
  24. SMTPAccount: '',
  25. SMTPFrom: '',
  26. SMTPToken: '',
  27. ServerAddress: '',
  28. EpayId: '',
  29. EpayKey: '',
  30. Price: 7.3,
  31. MinTopUp: 1,
  32. TopupGroupRatio: '',
  33. PayAddress: '',
  34. CustomCallbackAddress: '',
  35. Footer: '',
  36. WeChatAuthEnabled: '',
  37. WeChatServerAddress: '',
  38. WeChatServerToken: '',
  39. WeChatAccountQRCodeImageURL: '',
  40. TurnstileCheckEnabled: '',
  41. TurnstileSiteKey: '',
  42. TurnstileSecretKey: '',
  43. RegisterEnabled: '',
  44. EmailDomainRestrictionEnabled: '',
  45. EmailAliasRestrictionEnabled: '',
  46. SMTPSSLEnabled: '',
  47. EmailDomainWhitelist: [],
  48. // telegram login
  49. TelegramOAuthEnabled: '',
  50. TelegramBotToken: '',
  51. TelegramBotName: '',
  52. });
  53. const [originInputs, setOriginInputs] = useState({});
  54. let [loading, setLoading] = useState(false);
  55. const [EmailDomainWhitelist, setEmailDomainWhitelist] = useState([]);
  56. const [restrictedDomainInput, setRestrictedDomainInput] = useState('');
  57. const [showPasswordWarningModal, setShowPasswordWarningModal] =
  58. useState(false);
  59. const theme = useTheme();
  60. const isDark = theme === 'dark';
  61. const getOptions = async () => {
  62. const res = await API.get('/api/option/');
  63. const { success, message, data } = res.data;
  64. if (success) {
  65. let newInputs = {};
  66. data.forEach((item) => {
  67. if (item.key === 'TopupGroupRatio') {
  68. item.value = JSON.stringify(JSON.parse(item.value), null, 2);
  69. }
  70. newInputs[item.key] = item.value;
  71. });
  72. setInputs({
  73. ...newInputs,
  74. EmailDomainWhitelist: newInputs.EmailDomainWhitelist.split(','),
  75. });
  76. setOriginInputs(newInputs);
  77. setEmailDomainWhitelist(
  78. newInputs.EmailDomainWhitelist.split(',').map((item) => {
  79. return { key: item, text: item, value: item };
  80. }),
  81. );
  82. } else {
  83. showError(message);
  84. }
  85. };
  86. useEffect(() => {
  87. getOptions().then();
  88. }, []);
  89. useEffect(() => {}, [inputs.EmailDomainWhitelist]);
  90. const updateOption = async (key, value) => {
  91. setLoading(true);
  92. switch (key) {
  93. case 'PasswordLoginEnabled':
  94. case 'PasswordRegisterEnabled':
  95. case 'EmailVerificationEnabled':
  96. case 'GitHubOAuthEnabled':
  97. case 'WeChatAuthEnabled':
  98. case 'TelegramOAuthEnabled':
  99. case 'TurnstileCheckEnabled':
  100. case 'EmailDomainRestrictionEnabled':
  101. case 'EmailAliasRestrictionEnabled':
  102. case 'SMTPSSLEnabled':
  103. case 'RegisterEnabled':
  104. value = inputs[key] === 'true' ? 'false' : 'true';
  105. break;
  106. default:
  107. break;
  108. }
  109. const res = await API.put('/api/option/', {
  110. key,
  111. value,
  112. });
  113. const { success, message } = res.data;
  114. if (success) {
  115. if (key === 'EmailDomainWhitelist') {
  116. value = value.split(',');
  117. }
  118. if (key === 'Price') {
  119. value = parseFloat(value);
  120. }
  121. setInputs((inputs) => ({
  122. ...inputs,
  123. [key]: value,
  124. }));
  125. } else {
  126. showError(message);
  127. }
  128. setLoading(false);
  129. };
  130. const handleInputChange = async (e, { name, value }) => {
  131. if (name === 'PasswordLoginEnabled' && inputs[name] === 'true') {
  132. // block disabling password login
  133. setShowPasswordWarningModal(true);
  134. return;
  135. }
  136. if (
  137. name === 'Notice' ||
  138. (name.startsWith('SMTP') && name !== 'SMTPSSLEnabled') ||
  139. name === 'ServerAddress' ||
  140. name === 'EpayId' ||
  141. name === 'EpayKey' ||
  142. name === 'Price' ||
  143. name === 'PayAddress' ||
  144. name === 'GitHubClientId' ||
  145. name === 'GitHubClientSecret' ||
  146. name === 'WeChatServerAddress' ||
  147. name === 'WeChatServerToken' ||
  148. name === 'WeChatAccountQRCodeImageURL' ||
  149. name === 'TurnstileSiteKey' ||
  150. name === 'TurnstileSecretKey' ||
  151. name === 'EmailDomainWhitelist' ||
  152. name === 'TopupGroupRatio' ||
  153. name === 'TelegramBotToken' ||
  154. name === 'TelegramBotName'
  155. ) {
  156. setInputs((inputs) => ({ ...inputs, [name]: value }));
  157. } else {
  158. await updateOption(name, value);
  159. }
  160. };
  161. const submitServerAddress = async () => {
  162. let ServerAddress = removeTrailingSlash(inputs.ServerAddress);
  163. await updateOption('ServerAddress', ServerAddress);
  164. };
  165. const submitPayAddress = async () => {
  166. if (inputs.ServerAddress === '') {
  167. showError('请先填写服务器地址');
  168. return;
  169. }
  170. if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) {
  171. if (!verifyJSON(inputs.TopupGroupRatio)) {
  172. showError('充值分组倍率不是合法的 JSON 字符串');
  173. return;
  174. }
  175. await updateOption('TopupGroupRatio', inputs.TopupGroupRatio);
  176. }
  177. let PayAddress = removeTrailingSlash(inputs.PayAddress);
  178. await updateOption('PayAddress', PayAddress);
  179. if (inputs.EpayId !== '') {
  180. await updateOption('EpayId', inputs.EpayId);
  181. }
  182. if (inputs.EpayKey !== '') {
  183. await updateOption('EpayKey', inputs.EpayKey);
  184. }
  185. await updateOption('Price', '' + inputs.Price);
  186. };
  187. const submitSMTP = async () => {
  188. if (originInputs['SMTPServer'] !== inputs.SMTPServer) {
  189. await updateOption('SMTPServer', inputs.SMTPServer);
  190. }
  191. if (originInputs['SMTPAccount'] !== inputs.SMTPAccount) {
  192. await updateOption('SMTPAccount', inputs.SMTPAccount);
  193. }
  194. if (originInputs['SMTPFrom'] !== inputs.SMTPFrom) {
  195. await updateOption('SMTPFrom', inputs.SMTPFrom);
  196. }
  197. if (
  198. originInputs['SMTPPort'] !== inputs.SMTPPort &&
  199. inputs.SMTPPort !== ''
  200. ) {
  201. await updateOption('SMTPPort', inputs.SMTPPort);
  202. }
  203. if (
  204. originInputs['SMTPToken'] !== inputs.SMTPToken &&
  205. inputs.SMTPToken !== ''
  206. ) {
  207. await updateOption('SMTPToken', inputs.SMTPToken);
  208. }
  209. };
  210. const submitEmailDomainWhitelist = async () => {
  211. if (
  212. originInputs['EmailDomainWhitelist'] !==
  213. inputs.EmailDomainWhitelist.join(',') &&
  214. inputs.SMTPToken !== ''
  215. ) {
  216. await updateOption(
  217. 'EmailDomainWhitelist',
  218. inputs.EmailDomainWhitelist.join(','),
  219. );
  220. }
  221. };
  222. const submitWeChat = async () => {
  223. if (originInputs['WeChatServerAddress'] !== inputs.WeChatServerAddress) {
  224. await updateOption(
  225. 'WeChatServerAddress',
  226. removeTrailingSlash(inputs.WeChatServerAddress),
  227. );
  228. }
  229. if (
  230. originInputs['WeChatAccountQRCodeImageURL'] !==
  231. inputs.WeChatAccountQRCodeImageURL
  232. ) {
  233. await updateOption(
  234. 'WeChatAccountQRCodeImageURL',
  235. inputs.WeChatAccountQRCodeImageURL,
  236. );
  237. }
  238. if (
  239. originInputs['WeChatServerToken'] !== inputs.WeChatServerToken &&
  240. inputs.WeChatServerToken !== ''
  241. ) {
  242. await updateOption('WeChatServerToken', inputs.WeChatServerToken);
  243. }
  244. };
  245. const submitGitHubOAuth = async () => {
  246. if (originInputs['GitHubClientId'] !== inputs.GitHubClientId) {
  247. await updateOption('GitHubClientId', inputs.GitHubClientId);
  248. }
  249. if (
  250. originInputs['GitHubClientSecret'] !== inputs.GitHubClientSecret &&
  251. inputs.GitHubClientSecret !== ''
  252. ) {
  253. await updateOption('GitHubClientSecret', inputs.GitHubClientSecret);
  254. }
  255. };
  256. const submitTelegramSettings = async () => {
  257. // await updateOption('TelegramOAuthEnabled', inputs.TelegramOAuthEnabled);
  258. await updateOption('TelegramBotToken', inputs.TelegramBotToken);
  259. await updateOption('TelegramBotName', inputs.TelegramBotName);
  260. };
  261. const submitTurnstile = async () => {
  262. if (originInputs['TurnstileSiteKey'] !== inputs.TurnstileSiteKey) {
  263. await updateOption('TurnstileSiteKey', inputs.TurnstileSiteKey);
  264. }
  265. if (
  266. originInputs['TurnstileSecretKey'] !== inputs.TurnstileSecretKey &&
  267. inputs.TurnstileSecretKey !== ''
  268. ) {
  269. await updateOption('TurnstileSecretKey', inputs.TurnstileSecretKey);
  270. }
  271. };
  272. const submitNewRestrictedDomain = () => {
  273. const localDomainList = inputs.EmailDomainWhitelist;
  274. if (
  275. restrictedDomainInput !== '' &&
  276. !localDomainList.includes(restrictedDomainInput)
  277. ) {
  278. setRestrictedDomainInput('');
  279. setInputs({
  280. ...inputs,
  281. EmailDomainWhitelist: [...localDomainList, restrictedDomainInput],
  282. });
  283. setEmailDomainWhitelist([
  284. ...EmailDomainWhitelist,
  285. {
  286. key: restrictedDomainInput,
  287. text: restrictedDomainInput,
  288. value: restrictedDomainInput,
  289. },
  290. ]);
  291. }
  292. };
  293. return (
  294. <Grid columns={1}>
  295. <Grid.Column>
  296. <Form loading={loading} inverted={isDark}>
  297. <Header as='h3' inverted={isDark}>通用设置</Header>
  298. <Form.Group widths='equal'>
  299. <Form.Input
  300. label='服务器地址'
  301. placeholder='例如:https://yourdomain.com'
  302. value={inputs.ServerAddress}
  303. name='ServerAddress'
  304. onChange={handleInputChange}
  305. />
  306. </Form.Group>
  307. <Form.Button onClick={submitServerAddress}>
  308. 更新服务器地址
  309. </Form.Button>
  310. <Divider />
  311. <Header as='h3' inverted={isDark}>
  312. 支付设置(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)
  313. </Header>
  314. <Form.Group widths='equal'>
  315. <Form.Input
  316. label='支付地址,不填写则不启用在线支付'
  317. placeholder='例如:https://yourdomain.com'
  318. value={inputs.PayAddress}
  319. name='PayAddress'
  320. onChange={handleInputChange}
  321. />
  322. <Form.Input
  323. label='易支付商户ID'
  324. placeholder='例如:0001'
  325. value={inputs.EpayId}
  326. name='EpayId'
  327. onChange={handleInputChange}
  328. />
  329. <Form.Input
  330. label='易支付商户密钥'
  331. placeholder='例如:dejhfueqhujasjmndbjkqaw'
  332. value={inputs.EpayKey}
  333. name='EpayKey'
  334. onChange={handleInputChange}
  335. />
  336. </Form.Group>
  337. <Form.Group widths='equal'>
  338. <Form.Input
  339. label='回调地址,不填写则使用上方服务器地址作为回调地址'
  340. placeholder='例如:https://yourdomain.com'
  341. value={inputs.CustomCallbackAddress}
  342. name='CustomCallbackAddress'
  343. onChange={handleInputChange}
  344. />
  345. <Form.Input
  346. label='充值价格(x元/美金)'
  347. placeholder='例如:7,就是7元/美金'
  348. value={inputs.Price}
  349. name='Price'
  350. min={0}
  351. onChange={handleInputChange}
  352. />
  353. <Form.Input
  354. label='最低充值美元数量(以美金为单位,如果使用额度请自行换算!)'
  355. placeholder='例如:2,就是最低充值2$'
  356. value={inputs.MinTopUp}
  357. name='MinTopUp'
  358. min={1}
  359. onChange={handleInputChange}
  360. />
  361. </Form.Group>
  362. <Form.Group widths='equal'>
  363. <Form.TextArea
  364. label='充值分组倍率'
  365. name='TopupGroupRatio'
  366. onChange={handleInputChange}
  367. style={{ minHeight: 250, fontFamily: 'JetBrains Mono, Consolas' }}
  368. autoComplete='new-password'
  369. value={inputs.TopupGroupRatio}
  370. placeholder='为一个 JSON 文本,键为组名称,值为倍率'
  371. />
  372. </Form.Group>
  373. <Form.Button onClick={submitPayAddress}>更新支付设置</Form.Button>
  374. <Divider />
  375. <Header as='h3' inverted={isDark}>配置登录注册</Header>
  376. <Form.Group inline>
  377. <Form.Checkbox
  378. checked={inputs.PasswordLoginEnabled === 'true'}
  379. label='允许通过密码进行登录'
  380. name='PasswordLoginEnabled'
  381. onChange={handleInputChange}
  382. />
  383. {showPasswordWarningModal && (
  384. <Modal
  385. open={showPasswordWarningModal}
  386. onClose={() => setShowPasswordWarningModal(false)}
  387. size={'tiny'}
  388. style={{ maxWidth: '450px' }}
  389. >
  390. <Modal.Header>警告</Modal.Header>
  391. <Modal.Content>
  392. <p>
  393. 取消密码登录将导致所有未绑定其他登录方式的用户(包括管理员)无法通过密码登录,确认取消?
  394. </p>
  395. </Modal.Content>
  396. <Modal.Actions>
  397. <Button onClick={() => setShowPasswordWarningModal(false)}>
  398. 取消
  399. </Button>
  400. <Button
  401. color='yellow'
  402. onClick={async () => {
  403. setShowPasswordWarningModal(false);
  404. await updateOption('PasswordLoginEnabled', 'false');
  405. }}
  406. >
  407. 确定
  408. </Button>
  409. </Modal.Actions>
  410. </Modal>
  411. )}
  412. <Form.Checkbox
  413. checked={inputs.PasswordRegisterEnabled === 'true'}
  414. label='允许通过密码进行注册'
  415. name='PasswordRegisterEnabled'
  416. onChange={handleInputChange}
  417. />
  418. <Form.Checkbox
  419. checked={inputs.EmailVerificationEnabled === 'true'}
  420. label='通过密码注册时需要进行邮箱验证'
  421. name='EmailVerificationEnabled'
  422. onChange={handleInputChange}
  423. />
  424. <Form.Checkbox
  425. checked={inputs.GitHubOAuthEnabled === 'true'}
  426. label='允许通过 GitHub 账户登录 & 注册'
  427. name='GitHubOAuthEnabled'
  428. onChange={handleInputChange}
  429. />
  430. <Form.Checkbox
  431. checked={inputs.WeChatAuthEnabled === 'true'}
  432. label='允许通过微信登录 & 注册'
  433. name='WeChatAuthEnabled'
  434. onChange={handleInputChange}
  435. />
  436. <Form.Checkbox
  437. checked={inputs.TelegramOAuthEnabled === 'true'}
  438. label='允许通过 Telegram 进行登录'
  439. name='TelegramOAuthEnabled'
  440. onChange={handleInputChange}
  441. />
  442. </Form.Group>
  443. <Form.Group inline>
  444. <Form.Checkbox
  445. checked={inputs.RegisterEnabled === 'true'}
  446. label='允许新用户注册(此项为否时,新用户将无法以任何方式进行注册)'
  447. name='RegisterEnabled'
  448. onChange={handleInputChange}
  449. />
  450. <Form.Checkbox
  451. checked={inputs.TurnstileCheckEnabled === 'true'}
  452. label='启用 Turnstile 用户校验'
  453. name='TurnstileCheckEnabled'
  454. onChange={handleInputChange}
  455. />
  456. </Form.Group>
  457. <Divider />
  458. <Header as='h3' inverted={isDark}>
  459. 配置邮箱域名白名单
  460. <Header.Subheader>
  461. 用以防止恶意用户利用临时邮箱批量注册
  462. </Header.Subheader>
  463. </Header>
  464. <Form.Group widths={3}>
  465. <Form.Checkbox
  466. label='启用邮箱域名白名单'
  467. name='EmailDomainRestrictionEnabled'
  468. onChange={handleInputChange}
  469. checked={inputs.EmailDomainRestrictionEnabled === 'true'}
  470. />
  471. </Form.Group>
  472. <Form.Group widths={3}>
  473. <Form.Checkbox
  474. label='启用邮箱别名限制(例如:ab.cd@gmail.com)'
  475. name='EmailAliasRestrictionEnabled'
  476. onChange={handleInputChange}
  477. checked={inputs.EmailAliasRestrictionEnabled === 'true'}
  478. />
  479. </Form.Group>
  480. <Form.Group widths={2}>
  481. <Form.Dropdown
  482. label='允许的邮箱域名'
  483. placeholder='允许的邮箱域名'
  484. name='EmailDomainWhitelist'
  485. required
  486. fluid
  487. multiple
  488. selection
  489. onChange={handleInputChange}
  490. value={inputs.EmailDomainWhitelist}
  491. autoComplete='new-password'
  492. options={EmailDomainWhitelist}
  493. />
  494. <Form.Input
  495. label='添加新的允许的邮箱域名'
  496. action={
  497. <Button
  498. type='button'
  499. onClick={() => {
  500. submitNewRestrictedDomain();
  501. }}
  502. >
  503. 填入
  504. </Button>
  505. }
  506. onKeyDown={(e) => {
  507. if (e.key === 'Enter') {
  508. submitNewRestrictedDomain();
  509. }
  510. }}
  511. autoComplete='new-password'
  512. placeholder='输入新的允许的邮箱域名'
  513. value={restrictedDomainInput}
  514. onChange={(e, { value }) => {
  515. setRestrictedDomainInput(value);
  516. }}
  517. />
  518. </Form.Group>
  519. <Form.Button onClick={submitEmailDomainWhitelist}>
  520. 保存邮箱域名白名单设置
  521. </Form.Button>
  522. <Divider />
  523. <Header as='h3' inverted={isDark}>
  524. 配置 SMTP
  525. <Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
  526. </Header>
  527. <Form.Group widths={3}>
  528. <Form.Input
  529. label='SMTP 服务器地址'
  530. name='SMTPServer'
  531. onChange={handleInputChange}
  532. autoComplete='new-password'
  533. value={inputs.SMTPServer}
  534. placeholder='例如:smtp.qq.com'
  535. />
  536. <Form.Input
  537. label='SMTP 端口'
  538. name='SMTPPort'
  539. onChange={handleInputChange}
  540. autoComplete='new-password'
  541. value={inputs.SMTPPort}
  542. placeholder='默认: 587'
  543. />
  544. <Form.Input
  545. label='SMTP 账户'
  546. name='SMTPAccount'
  547. onChange={handleInputChange}
  548. autoComplete='new-password'
  549. value={inputs.SMTPAccount}
  550. placeholder='通常是邮箱地址'
  551. />
  552. </Form.Group>
  553. <Form.Group widths={3}>
  554. <Form.Input
  555. label='SMTP 发送者邮箱'
  556. name='SMTPFrom'
  557. onChange={handleInputChange}
  558. autoComplete='new-password'
  559. value={inputs.SMTPFrom}
  560. placeholder='通常和邮箱地址保持一致'
  561. />
  562. <Form.Input
  563. label='SMTP 访问凭证'
  564. name='SMTPToken'
  565. onChange={handleInputChange}
  566. type='password'
  567. autoComplete='new-password'
  568. checked={inputs.RegisterEnabled === 'true'}
  569. placeholder='敏感信息不会发送到前端显示'
  570. />
  571. </Form.Group>
  572. <Form.Group widths={3}>
  573. <Form.Checkbox
  574. label='启用SMTP SSL(465端口强制开启)'
  575. name='SMTPSSLEnabled'
  576. onChange={handleInputChange}
  577. checked={inputs.SMTPSSLEnabled === 'true'}
  578. />
  579. </Form.Group>
  580. <Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
  581. <Divider />
  582. <Header as='h3' inverted={isDark}>
  583. 配置 GitHub OAuth App
  584. <Header.Subheader>
  585. 用以支持通过 GitHub 进行登录注册,
  586. <a
  587. href='https://github.com/settings/developers'
  588. target='_blank'
  589. rel='noreferrer'
  590. >
  591. 点击此处
  592. </a>
  593. 管理你的 GitHub OAuth App
  594. </Header.Subheader>
  595. </Header>
  596. <Message>
  597. Homepage URL 填 <code>{inputs.ServerAddress}</code>
  598. ,Authorization callback URL 填{' '}
  599. <code>{`${inputs.ServerAddress}/oauth/github`}</code>
  600. </Message>
  601. <Form.Group widths={3}>
  602. <Form.Input
  603. label='GitHub Client ID'
  604. name='GitHubClientId'
  605. onChange={handleInputChange}
  606. autoComplete='new-password'
  607. value={inputs.GitHubClientId}
  608. placeholder='输入你注册的 GitHub OAuth APP 的 ID'
  609. />
  610. <Form.Input
  611. label='GitHub Client Secret'
  612. name='GitHubClientSecret'
  613. onChange={handleInputChange}
  614. type='password'
  615. autoComplete='new-password'
  616. value={inputs.GitHubClientSecret}
  617. placeholder='敏感信息不会发送到前端显示'
  618. />
  619. </Form.Group>
  620. <Form.Button onClick={submitGitHubOAuth}>
  621. 保存 GitHub OAuth 设置
  622. </Form.Button>
  623. <Divider />
  624. <Header as='h3' inverted={isDark}>
  625. 配置 WeChat Server
  626. <Header.Subheader>
  627. 用以支持通过微信进行登录注册,
  628. <a
  629. href='https://github.com/songquanpeng/wechat-server'
  630. target='_blank'
  631. rel='noreferrer'
  632. >
  633. 点击此处
  634. </a>
  635. 了解 WeChat Server
  636. </Header.Subheader>
  637. </Header>
  638. <Form.Group widths={3}>
  639. <Form.Input
  640. label='WeChat Server 服务器地址'
  641. name='WeChatServerAddress'
  642. placeholder='例如:https://yourdomain.com'
  643. onChange={handleInputChange}
  644. autoComplete='new-password'
  645. value={inputs.WeChatServerAddress}
  646. />
  647. <Form.Input
  648. label='WeChat Server 访问凭证'
  649. name='WeChatServerToken'
  650. type='password'
  651. onChange={handleInputChange}
  652. autoComplete='new-password'
  653. value={inputs.WeChatServerToken}
  654. placeholder='敏感信息不会发送到前端显示'
  655. />
  656. <Form.Input
  657. label='微信公众号二维码图片链接'
  658. name='WeChatAccountQRCodeImageURL'
  659. onChange={handleInputChange}
  660. autoComplete='new-password'
  661. value={inputs.WeChatAccountQRCodeImageURL}
  662. placeholder='输入一个图片链接'
  663. />
  664. </Form.Group>
  665. <Form.Button onClick={submitWeChat}>
  666. 保存 WeChat Server 设置
  667. </Form.Button>
  668. <Divider />
  669. <Header as='h3' inverted={isDark}>配置 Telegram 登录</Header>
  670. <Form.Group inline>
  671. <Form.Input
  672. label='Telegram Bot Token'
  673. name='TelegramBotToken'
  674. onChange={handleInputChange}
  675. value={inputs.TelegramBotToken}
  676. placeholder='输入你的 Telegram Bot Token'
  677. />
  678. <Form.Input
  679. label='Telegram Bot 名称'
  680. name='TelegramBotName'
  681. onChange={handleInputChange}
  682. value={inputs.TelegramBotName}
  683. placeholder='输入你的 Telegram Bot 名称'
  684. />
  685. </Form.Group>
  686. <Form.Button onClick={submitTelegramSettings}>
  687. 保存 Telegram 登录设置
  688. </Form.Button>
  689. <Divider />
  690. <Header as='h3' inverted={isDark}>
  691. 配置 Turnstile
  692. <Header.Subheader>
  693. 用以支持用户校验,
  694. <a
  695. href='https://dash.cloudflare.com/'
  696. target='_blank'
  697. rel='noreferrer'
  698. >
  699. 点击此处
  700. </a>
  701. 管理你的 Turnstile Sites,推荐选择 Invisible Widget Type
  702. </Header.Subheader>
  703. </Header>
  704. <Form.Group widths={3}>
  705. <Form.Input
  706. label='Turnstile Site Key'
  707. name='TurnstileSiteKey'
  708. onChange={handleInputChange}
  709. autoComplete='new-password'
  710. value={inputs.TurnstileSiteKey}
  711. placeholder='输入你注册的 Turnstile Site Key'
  712. />
  713. <Form.Input
  714. label='Turnstile Secret Key'
  715. name='TurnstileSecretKey'
  716. onChange={handleInputChange}
  717. type='password'
  718. autoComplete='new-password'
  719. value={inputs.TurnstileSecretKey}
  720. placeholder='敏感信息不会发送到前端显示'
  721. />
  722. </Form.Group>
  723. <Form.Button onClick={submitTurnstile}>
  724. 保存 Turnstile 设置
  725. </Form.Button>
  726. </Form>
  727. </Grid.Column>
  728. </Grid>
  729. );
  730. };
  731. export default SystemSetting;