LoginForm.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import React, { useContext, useEffect, useState } from 'react';
  2. import { Link, useNavigate, useSearchParams } from 'react-router-dom';
  3. import { UserContext } from '../context/User';
  4. import { API, getLogo, showError, showInfo, showSuccess } from '../helpers';
  5. import { onGitHubOAuthClicked } from './utils';
  6. import Turnstile from 'react-turnstile';
  7. import {
  8. Button,
  9. Card,
  10. Divider,
  11. Form,
  12. Icon,
  13. Layout,
  14. Modal,
  15. } from '@douyinfe/semi-ui';
  16. import Title from '@douyinfe/semi-ui/lib/es/typography/title';
  17. import Text from '@douyinfe/semi-ui/lib/es/typography/text';
  18. import TelegramLoginButton from 'react-telegram-login';
  19. import { IconGithubLogo } from '@douyinfe/semi-icons';
  20. import WeChatIcon from './WeChatIcon';
  21. import { setUserData } from '../helpers/data.js';
  22. const LoginForm = () => {
  23. const [inputs, setInputs] = useState({
  24. username: '',
  25. password: '',
  26. wechat_verification_code: '',
  27. });
  28. const [searchParams, setSearchParams] = useSearchParams();
  29. const [submitted, setSubmitted] = useState(false);
  30. const { username, password } = inputs;
  31. const [userState, userDispatch] = useContext(UserContext);
  32. const [turnstileEnabled, setTurnstileEnabled] = useState(false);
  33. const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
  34. const [turnstileToken, setTurnstileToken] = useState('');
  35. let navigate = useNavigate();
  36. const [status, setStatus] = useState({});
  37. const logo = getLogo();
  38. useEffect(() => {
  39. if (searchParams.get('expired')) {
  40. showError('未登录或登录已过期,请重新登录!');
  41. }
  42. let status = localStorage.getItem('status');
  43. if (status) {
  44. status = JSON.parse(status);
  45. setStatus(status);
  46. if (status.turnstile_check) {
  47. setTurnstileEnabled(true);
  48. setTurnstileSiteKey(status.turnstile_site_key);
  49. }
  50. }
  51. }, []);
  52. const [showWeChatLoginModal, setShowWeChatLoginModal] = useState(false);
  53. const onWeChatLoginClicked = () => {
  54. setShowWeChatLoginModal(true);
  55. };
  56. const onSubmitWeChatVerificationCode = async () => {
  57. if (turnstileEnabled && turnstileToken === '') {
  58. showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
  59. return;
  60. }
  61. const res = await API.get(
  62. `/api/oauth/wechat?code=${inputs.wechat_verification_code}`,
  63. );
  64. const { success, message, data } = res.data;
  65. if (success) {
  66. userDispatch({ type: 'login', payload: data });
  67. localStorage.setItem('user', JSON.stringify(data));
  68. navigate('/');
  69. showSuccess('登录成功!');
  70. setShowWeChatLoginModal(false);
  71. } else {
  72. showError(message);
  73. }
  74. };
  75. function handleChange(name, value) {
  76. setInputs((inputs) => ({ ...inputs, [name]: value }));
  77. }
  78. async function handleSubmit(e) {
  79. if (turnstileEnabled && turnstileToken === '') {
  80. showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
  81. return;
  82. }
  83. setSubmitted(true);
  84. if (username && password) {
  85. const res = await API.post(
  86. `/api/user/login?turnstile=${turnstileToken}`,
  87. {
  88. username,
  89. password,
  90. },
  91. );
  92. const { success, message, data } = res.data;
  93. if (success) {
  94. userDispatch({ type: 'login', payload: data });
  95. setUserData(data);
  96. showSuccess('登录成功!');
  97. if (username === 'root' && password === '123456') {
  98. Modal.error({
  99. title: '您正在使用默认密码!',
  100. content: '请立刻修改默认密码!',
  101. centered: true,
  102. });
  103. }
  104. navigate('/token');
  105. } else {
  106. showError(message);
  107. }
  108. } else {
  109. showError('请输入用户名和密码!');
  110. }
  111. }
  112. // 添加Telegram登录处理函数
  113. const onTelegramLoginClicked = async (response) => {
  114. const fields = [
  115. 'id',
  116. 'first_name',
  117. 'last_name',
  118. 'username',
  119. 'photo_url',
  120. 'auth_date',
  121. 'hash',
  122. 'lang',
  123. ];
  124. const params = {};
  125. fields.forEach((field) => {
  126. if (response[field]) {
  127. params[field] = response[field];
  128. }
  129. });
  130. const res = await API.get(`/api/oauth/telegram/login`, { params });
  131. const { success, message, data } = res.data;
  132. if (success) {
  133. userDispatch({ type: 'login', payload: data });
  134. localStorage.setItem('user', JSON.stringify(data));
  135. showSuccess('登录成功!');
  136. navigate('/');
  137. } else {
  138. showError(message);
  139. }
  140. };
  141. return (
  142. <div>
  143. <Layout>
  144. <Layout.Header></Layout.Header>
  145. <Layout.Content>
  146. <div
  147. style={{
  148. justifyContent: 'center',
  149. display: 'flex',
  150. marginTop: 120,
  151. }}
  152. >
  153. <div style={{ width: 500 }}>
  154. <Card>
  155. <Title heading={2} style={{ textAlign: 'center' }}>
  156. 用户登录
  157. </Title>
  158. <Form>
  159. <Form.Input
  160. field={'username'}
  161. label={'用户名'}
  162. placeholder='用户名'
  163. name='username'
  164. onChange={(value) => handleChange('username', value)}
  165. />
  166. <Form.Input
  167. field={'password'}
  168. label={'密码'}
  169. placeholder='密码'
  170. name='password'
  171. type='password'
  172. onChange={(value) => handleChange('password', value)}
  173. />
  174. <Button
  175. theme='solid'
  176. style={{ width: '100%' }}
  177. type={'primary'}
  178. size='large'
  179. htmlType={'submit'}
  180. onClick={handleSubmit}
  181. >
  182. 登录
  183. </Button>
  184. </Form>
  185. <div
  186. style={{
  187. display: 'flex',
  188. justifyContent: 'space-between',
  189. marginTop: 20,
  190. }}
  191. >
  192. <Text>
  193. 没有账号请先 <Link to='/register'>注册账号</Link>
  194. </Text>
  195. <Text>
  196. 忘记密码 <Link to='/reset'>点击重置</Link>
  197. </Text>
  198. </div>
  199. {status.github_oauth ||
  200. status.wechat_login ||
  201. status.telegram_oauth ? (
  202. <>
  203. <Divider margin='12px' align='center'>
  204. 第三方登录
  205. </Divider>
  206. <div
  207. style={{
  208. display: 'flex',
  209. justifyContent: 'center',
  210. marginTop: 20,
  211. }}
  212. >
  213. {status.github_oauth ? (
  214. <Button
  215. type='primary'
  216. icon={<IconGithubLogo />}
  217. onClick={() =>
  218. onGitHubOAuthClicked(status.github_client_id)
  219. }
  220. />
  221. ) : (
  222. <></>
  223. )}
  224. {status.wechat_login ? (
  225. <Button
  226. type='primary'
  227. style={{ color: 'rgba(var(--semi-green-5), 1)' }}
  228. icon={<Icon svg={<WeChatIcon />} />}
  229. onClick={onWeChatLoginClicked}
  230. />
  231. ) : (
  232. <></>
  233. )}
  234. </div>
  235. {status.telegram_oauth ? (
  236. <>
  237. <div
  238. style={{
  239. display: 'flex',
  240. justifyContent: 'center',
  241. marginTop: 5,
  242. }}
  243. >
  244. <TelegramLoginButton
  245. dataOnauth={onTelegramLoginClicked}
  246. botName={status.telegram_bot_name}
  247. />
  248. </div>
  249. </>
  250. ) : (
  251. <></>
  252. )}
  253. </>
  254. ) : (
  255. <></>
  256. )}
  257. <Modal
  258. title='微信扫码登录'
  259. visible={showWeChatLoginModal}
  260. maskClosable={true}
  261. onOk={onSubmitWeChatVerificationCode}
  262. onCancel={() => setShowWeChatLoginModal(false)}
  263. okText={'登录'}
  264. size={'small'}
  265. centered={true}
  266. >
  267. <div
  268. style={{
  269. display: 'flex',
  270. alignItem: 'center',
  271. flexDirection: 'column',
  272. }}
  273. >
  274. <img src={status.wechat_qrcode} />
  275. </div>
  276. <div style={{ textAlign: 'center' }}>
  277. <p>
  278. 微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)
  279. </p>
  280. </div>
  281. <Form size='large'>
  282. <Form.Input
  283. field={'wechat_verification_code'}
  284. placeholder='验证码'
  285. label={'验证码'}
  286. value={inputs.wechat_verification_code}
  287. onChange={(value) =>
  288. handleChange('wechat_verification_code', value)
  289. }
  290. />
  291. </Form>
  292. </Modal>
  293. </Card>
  294. {turnstileEnabled ? (
  295. <div
  296. style={{
  297. display: 'flex',
  298. justifyContent: 'center',
  299. marginTop: 20,
  300. }}
  301. >
  302. <Turnstile
  303. sitekey={turnstileSiteKey}
  304. onVerify={(token) => {
  305. setTurnstileToken(token);
  306. }}
  307. />
  308. </div>
  309. ) : (
  310. <></>
  311. )}
  312. </div>
  313. </div>
  314. </Layout.Content>
  315. </Layout>
  316. </div>
  317. );
  318. };
  319. export default LoginForm;