LoginForm.js 11 KB

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