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. import LinuxDoIcon from './LinuxDoIcon.js';
  30. const LoginForm = () => {
  31. const [inputs, setInputs] = useState({
  32. username: '',
  33. password: '',
  34. wechat_verification_code: '',
  35. });
  36. const [searchParams, setSearchParams] = useSearchParams();
  37. const [submitted, setSubmitted] = useState(false);
  38. const { username, password } = inputs;
  39. const [userState, userDispatch] = useContext(UserContext);
  40. const [turnstileEnabled, setTurnstileEnabled] = useState(false);
  41. const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
  42. const [turnstileToken, setTurnstileToken] = useState('');
  43. let navigate = useNavigate();
  44. const [status, setStatus] = useState({});
  45. const logo = getLogo();
  46. useEffect(() => {
  47. if (searchParams.get('expired')) {
  48. showError('未登录或登录已过期,请重新登录!');
  49. }
  50. let status = localStorage.getItem('status');
  51. if (status) {
  52. status = JSON.parse(status);
  53. setStatus(status);
  54. if (status.turnstile_check) {
  55. setTurnstileEnabled(true);
  56. setTurnstileSiteKey(status.turnstile_site_key);
  57. }
  58. }
  59. }, []);
  60. const [showWeChatLoginModal, setShowWeChatLoginModal] = useState(false);
  61. const onWeChatLoginClicked = () => {
  62. setShowWeChatLoginModal(true);
  63. };
  64. const onSubmitWeChatVerificationCode = async () => {
  65. if (turnstileEnabled && turnstileToken === '') {
  66. showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
  67. return;
  68. }
  69. const res = await API.get(
  70. `/api/oauth/wechat?code=${inputs.wechat_verification_code}`,
  71. );
  72. const { success, message, data } = res.data;
  73. if (success) {
  74. userDispatch({ type: 'login', payload: data });
  75. localStorage.setItem('user', JSON.stringify(data));
  76. setUserData(data);
  77. updateAPI();
  78. navigate('/');
  79. showSuccess('登录成功!');
  80. setShowWeChatLoginModal(false);
  81. } else {
  82. showError(message);
  83. }
  84. };
  85. function handleChange(name, value) {
  86. setInputs((inputs) => ({ ...inputs, [name]: value }));
  87. }
  88. async function handleSubmit(e) {
  89. if (turnstileEnabled && turnstileToken === '') {
  90. showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
  91. return;
  92. }
  93. setSubmitted(true);
  94. if (username && password) {
  95. const res = await API.post(
  96. `/api/user/login?turnstile=${turnstileToken}`,
  97. {
  98. username,
  99. password,
  100. },
  101. );
  102. const { success, message, data } = res.data;
  103. if (success) {
  104. userDispatch({ type: 'login', payload: data });
  105. setUserData(data);
  106. updateAPI();
  107. showSuccess('登录成功!');
  108. if (username === 'root' && password === '123456') {
  109. Modal.error({
  110. title: '您正在使用默认密码!',
  111. content: '请立刻修改默认密码!',
  112. centered: true,
  113. });
  114. }
  115. navigate('/token');
  116. } else {
  117. showError(message);
  118. }
  119. } else {
  120. showError('请输入用户名和密码!');
  121. }
  122. }
  123. // 添加Telegram登录处理函数
  124. const onTelegramLoginClicked = async (response) => {
  125. const fields = [
  126. 'id',
  127. 'first_name',
  128. 'last_name',
  129. 'username',
  130. 'photo_url',
  131. 'auth_date',
  132. 'hash',
  133. 'lang',
  134. ];
  135. const params = {};
  136. fields.forEach((field) => {
  137. if (response[field]) {
  138. params[field] = response[field];
  139. }
  140. });
  141. const res = await API.get(`/api/oauth/telegram/login`, { params });
  142. const { success, message, data } = res.data;
  143. if (success) {
  144. userDispatch({ type: 'login', payload: data });
  145. localStorage.setItem('user', JSON.stringify(data));
  146. showSuccess('登录成功!');
  147. setUserData(data);
  148. updateAPI();
  149. navigate('/');
  150. } else {
  151. showError(message);
  152. }
  153. };
  154. return (
  155. <div>
  156. <Layout>
  157. <Layout.Header></Layout.Header>
  158. <Layout.Content>
  159. <div
  160. style={{
  161. justifyContent: 'center',
  162. display: 'flex',
  163. marginTop: 120,
  164. }}
  165. >
  166. <div style={{ width: 500 }}>
  167. <Card>
  168. <Title heading={2} style={{ textAlign: 'center' }}>
  169. 用户登录
  170. </Title>
  171. <Form>
  172. <Form.Input
  173. field={'username'}
  174. label={'用户名'}
  175. placeholder='用户名'
  176. name='username'
  177. onChange={(value) => handleChange('username', value)}
  178. />
  179. <Form.Input
  180. field={'password'}
  181. label={'密码'}
  182. placeholder='密码'
  183. name='password'
  184. type='password'
  185. onChange={(value) => handleChange('password', value)}
  186. />
  187. <Button
  188. theme='solid'
  189. style={{ width: '100%' }}
  190. type={'primary'}
  191. size='large'
  192. htmlType={'submit'}
  193. onClick={handleSubmit}
  194. >
  195. 登录
  196. </Button>
  197. </Form>
  198. <div
  199. style={{
  200. display: 'flex',
  201. justifyContent: 'space-between',
  202. marginTop: 20,
  203. }}
  204. >
  205. <Text>
  206. 没有账号请先 <Link to='/register'>注册账号</Link>
  207. </Text>
  208. <Text>
  209. 忘记密码 <Link to='/reset'>点击重置</Link>
  210. </Text>
  211. </div>
  212. {status.github_oauth ||
  213. status.wechat_login ||
  214. status.telegram_oauth ||
  215. status.linuxdo_oauth ? (
  216. <>
  217. <Divider margin='12px' align='center'>
  218. 第三方登录
  219. </Divider>
  220. <div
  221. style={{
  222. display: 'flex',
  223. justifyContent: 'center',
  224. marginTop: 20,
  225. }}
  226. >
  227. {status.github_oauth ? (
  228. <Button
  229. type='primary'
  230. icon={<IconGithubLogo />}
  231. onClick={() =>
  232. onGitHubOAuthClicked(status.github_client_id)
  233. }
  234. />
  235. ) : (
  236. <></>
  237. )}
  238. {status.linuxdo_oauth ? (
  239. <Button
  240. icon={<LinuxDoIcon />}
  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;