PersonalSetting.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import React, { useEffect, useState } from 'react';
  2. import { Button, Divider, Form, Header, Image, Message, Modal } from 'semantic-ui-react';
  3. import { Link } from 'react-router-dom';
  4. import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers';
  5. import Turnstile from 'react-turnstile';
  6. const PersonalSetting = () => {
  7. const [inputs, setInputs] = useState({
  8. wechat_verification_code: '',
  9. email_verification_code: '',
  10. email: '',
  11. });
  12. const [status, setStatus] = useState({});
  13. const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
  14. const [showEmailBindModal, setShowEmailBindModal] = useState(false);
  15. const [turnstileEnabled, setTurnstileEnabled] = useState(false);
  16. const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
  17. const [turnstileToken, setTurnstileToken] = useState('');
  18. const [loading, setLoading] = useState(false);
  19. useEffect(() => {
  20. let status = localStorage.getItem('status');
  21. if (status) {
  22. status = JSON.parse(status);
  23. setStatus(status);
  24. if (status.turnstile_check) {
  25. setTurnstileEnabled(true);
  26. setTurnstileSiteKey(status.turnstile_site_key);
  27. }
  28. }
  29. }, []);
  30. const handleInputChange = (e, { name, value }) => {
  31. setInputs((inputs) => ({ ...inputs, [name]: value }));
  32. };
  33. const generateAccessToken = async () => {
  34. const res = await API.get('/api/user/token');
  35. const { success, message, data } = res.data;
  36. if (success) {
  37. await copy(data);
  38. showSuccess(`令牌已重置并已复制到剪贴板:${data}`);
  39. } else {
  40. showError(message);
  41. }
  42. };
  43. const getAffLink = async () => {
  44. const res = await API.get('/api/user/aff');
  45. const { success, message, data } = res.data;
  46. if (success) {
  47. let link = `${window.location.origin}/register?aff=${data}`;
  48. await copy(link);
  49. showNotice(`邀请链接已复制到剪切板:${link}`);
  50. } else {
  51. showError(message);
  52. }
  53. };
  54. const bindWeChat = async () => {
  55. if (inputs.wechat_verification_code === '') return;
  56. const res = await API.get(
  57. `/api/oauth/wechat/bind?code=${inputs.wechat_verification_code}`
  58. );
  59. const { success, message } = res.data;
  60. if (success) {
  61. showSuccess('微信账户绑定成功!');
  62. setShowWeChatBindModal(false);
  63. } else {
  64. showError(message);
  65. }
  66. };
  67. const openGitHubOAuth = () => {
  68. window.open(
  69. `https://github.com/login/oauth/authorize?client_id=${status.github_client_id}&scope=user:email`
  70. );
  71. };
  72. const sendVerificationCode = async () => {
  73. if (inputs.email === '') return;
  74. if (turnstileEnabled && turnstileToken === '') {
  75. showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
  76. return;
  77. }
  78. setLoading(true);
  79. const res = await API.get(
  80. `/api/verification?email=${inputs.email}&turnstile=${turnstileToken}`
  81. );
  82. const { success, message } = res.data;
  83. if (success) {
  84. showSuccess('验证码发送成功,请检查邮箱!');
  85. } else {
  86. showError(message);
  87. }
  88. setLoading(false);
  89. };
  90. const bindEmail = async () => {
  91. if (inputs.email_verification_code === '') return;
  92. setLoading(true);
  93. const res = await API.get(
  94. `/api/oauth/email/bind?email=${inputs.email}&code=${inputs.email_verification_code}`
  95. );
  96. const { success, message } = res.data;
  97. if (success) {
  98. showSuccess('邮箱账户绑定成功!');
  99. setShowEmailBindModal(false);
  100. } else {
  101. showError(message);
  102. }
  103. setLoading(false);
  104. };
  105. return (
  106. <div style={{ lineHeight: '40px' }}>
  107. <Header as='h3'>通用设置</Header>
  108. <Message>
  109. 注意,此处生成的令牌用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。
  110. </Message>
  111. <Button as={Link} to={`/user/edit/`}>
  112. 更新个人信息
  113. </Button>
  114. <Button onClick={generateAccessToken}>生成系统访问令牌</Button>
  115. <Button onClick={getAffLink}>复制邀请链接</Button>
  116. <Divider />
  117. <Header as='h3'>账号绑定</Header>
  118. {
  119. status.wechat_login && (
  120. <Button
  121. onClick={() => {
  122. setShowWeChatBindModal(true);
  123. }}
  124. >
  125. 绑定微信账号
  126. </Button>
  127. )
  128. }
  129. <Modal
  130. onClose={() => setShowWeChatBindModal(false)}
  131. onOpen={() => setShowWeChatBindModal(true)}
  132. open={showWeChatBindModal}
  133. size={'mini'}
  134. >
  135. <Modal.Content>
  136. <Modal.Description>
  137. <Image src={status.wechat_qrcode} fluid />
  138. <div style={{ textAlign: 'center' }}>
  139. <p>
  140. 微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)
  141. </p>
  142. </div>
  143. <Form size='large'>
  144. <Form.Input
  145. fluid
  146. placeholder='验证码'
  147. name='wechat_verification_code'
  148. value={inputs.wechat_verification_code}
  149. onChange={handleInputChange}
  150. />
  151. <Button color='' fluid size='large' onClick={bindWeChat}>
  152. 绑定
  153. </Button>
  154. </Form>
  155. </Modal.Description>
  156. </Modal.Content>
  157. </Modal>
  158. {
  159. status.github_oauth && (
  160. <Button onClick={openGitHubOAuth}>绑定 GitHub 账号</Button>
  161. )
  162. }
  163. <Button
  164. onClick={() => {
  165. setShowEmailBindModal(true);
  166. }}
  167. >
  168. 绑定邮箱地址
  169. </Button>
  170. <Modal
  171. onClose={() => setShowEmailBindModal(false)}
  172. onOpen={() => setShowEmailBindModal(true)}
  173. open={showEmailBindModal}
  174. size={'tiny'}
  175. style={{ maxWidth: '450px' }}
  176. >
  177. <Modal.Header>绑定邮箱地址</Modal.Header>
  178. <Modal.Content>
  179. <Modal.Description>
  180. <Form size='large'>
  181. <Form.Input
  182. fluid
  183. placeholder='输入邮箱地址'
  184. onChange={handleInputChange}
  185. name='email'
  186. type='email'
  187. action={
  188. <Button onClick={sendVerificationCode} disabled={loading}>
  189. 获取验证码
  190. </Button>
  191. }
  192. />
  193. <Form.Input
  194. fluid
  195. placeholder='验证码'
  196. name='email_verification_code'
  197. value={inputs.email_verification_code}
  198. onChange={handleInputChange}
  199. />
  200. {turnstileEnabled ? (
  201. <Turnstile
  202. sitekey={turnstileSiteKey}
  203. onVerify={(token) => {
  204. setTurnstileToken(token);
  205. }}
  206. />
  207. ) : (
  208. <></>
  209. )}
  210. <Button
  211. color=''
  212. fluid
  213. size='large'
  214. onClick={bindEmail}
  215. loading={loading}
  216. >
  217. 绑定
  218. </Button>
  219. </Form>
  220. </Modal.Description>
  221. </Modal.Content>
  222. </Modal>
  223. </div>
  224. );
  225. };
  226. export default PersonalSetting;