LoginForm.js 11 KB

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