From fe72f855542751c60c1f496ad1e3c42c0ba95188 Mon Sep 17 00:00:00 2001 From: Martial BE Date: Fri, 22 Dec 2023 10:48:52 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20=E8=A1=A5=E5=85=A8=E5=BF=98?= =?UTF-8?q?=E8=AE=B0=E5=AF=86=E7=A0=81web=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/routes/OtherRoutes.js | 10 ++ .../Authentication/Auth/ForgetPassword.js | 68 +++++++++ web/src/views/Authentication/Auth/Login.js | 2 - .../Authentication/Auth/ResetPassword.js | 66 ++++++++ .../Authentication/AuthForms/AuthLogin.js | 11 +- .../AuthForms/ForgetPasswordForm.js | 144 ++++++++++++++++++ .../AuthForms/ResetPasswordForm.js | 62 ++++++++ 7 files changed, 358 insertions(+), 5 deletions(-) create mode 100644 web/src/views/Authentication/Auth/ForgetPassword.js create mode 100644 web/src/views/Authentication/Auth/ResetPassword.js create mode 100644 web/src/views/Authentication/AuthForms/ForgetPasswordForm.js create mode 100644 web/src/views/Authentication/AuthForms/ResetPasswordForm.js diff --git a/web/src/routes/OtherRoutes.js b/web/src/routes/OtherRoutes.js index 870307a0..085c4add 100644 --- a/web/src/routes/OtherRoutes.js +++ b/web/src/routes/OtherRoutes.js @@ -8,6 +8,8 @@ import MinimalLayout from 'layout/MinimalLayout'; const AuthLogin = Loadable(lazy(() => import('views/Authentication/Auth/Login'))); const AuthRegister = Loadable(lazy(() => import('views/Authentication/Auth/Register'))); const GitHubOAuth = Loadable(lazy(() => import('views/Authentication/Auth/GitHubOAuth'))); +const ForgetPassword = Loadable(lazy(() => import('views/Authentication/Auth/ForgetPassword'))); +const ResetPassword = Loadable(lazy(() => import('views/Authentication/Auth/ResetPassword'))); const Home = Loadable(lazy(() => import('views/Home'))); const About = Loadable(lazy(() => import('views/About'))); const NotFoundView = Loadable(lazy(() => import('views/Error'))); @@ -34,6 +36,14 @@ const OtherRoutes = { path: '/register', element: }, + { + path: '/reset', + element: + }, + { + path: '/user/reset', + element: + }, { path: '/oauth/github', element: diff --git a/web/src/views/Authentication/Auth/ForgetPassword.js b/web/src/views/Authentication/Auth/ForgetPassword.js new file mode 100644 index 00000000..bac570e4 --- /dev/null +++ b/web/src/views/Authentication/Auth/ForgetPassword.js @@ -0,0 +1,68 @@ +import { Link } from 'react-router-dom'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import { Divider, Grid, Stack, Typography, useMediaQuery } from '@mui/material'; + +// project imports +import AuthWrapper from '../AuthWrapper'; +import AuthCardWrapper from '../AuthCardWrapper'; +import ForgetPasswordForm from '../AuthForms/ForgetPasswordForm'; +import Logo from 'ui-component/Logo'; + +// assets + +// ================================|| AUTH3 - LOGIN ||================================ // + +const ForgetPassword = () => { + const theme = useTheme(); + const matchDownSM = useMediaQuery(theme.breakpoints.down('md')); + + return ( + + + + + + + + + + + + + + + + + + 密码重置 + + + + + + + + + + + + + + + 登录 + + + + + + + + + + + ); +}; + +export default ForgetPassword; diff --git a/web/src/views/Authentication/Auth/Login.js b/web/src/views/Authentication/Auth/Login.js index 524e0f70..965d8470 100644 --- a/web/src/views/Authentication/Auth/Login.js +++ b/web/src/views/Authentication/Auth/Login.js @@ -10,8 +10,6 @@ import AuthCardWrapper from '../AuthCardWrapper'; import AuthLogin from '../AuthForms/AuthLogin'; import Logo from 'ui-component/Logo'; -// assets - // ================================|| AUTH3 - LOGIN ||================================ // const Login = () => { diff --git a/web/src/views/Authentication/Auth/ResetPassword.js b/web/src/views/Authentication/Auth/ResetPassword.js new file mode 100644 index 00000000..75c2566d --- /dev/null +++ b/web/src/views/Authentication/Auth/ResetPassword.js @@ -0,0 +1,66 @@ +import { Link } from 'react-router-dom'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import { Divider, Grid, Stack, Typography, useMediaQuery } from '@mui/material'; + +// project imports +import AuthWrapper from '../AuthWrapper'; +import AuthCardWrapper from '../AuthCardWrapper'; +import ResetPasswordForm from '../AuthForms/ResetPasswordForm'; +import Logo from 'ui-component/Logo'; + +// ================================|| AUTH3 - LOGIN ||================================ // + +const ResetPassword = () => { + const theme = useTheme(); + const matchDownSM = useMediaQuery(theme.breakpoints.down('md')); + + return ( + + + + + + + + + + + + + + + + + + 密码重置确认 + + + + + + + + + + + + + + + 登录 + + + + + + + + + + + ); +}; + +export default ResetPassword; diff --git a/web/src/views/Authentication/AuthForms/AuthLogin.js b/web/src/views/Authentication/AuthForms/AuthLogin.js index 06f83dc2..cb421946 100644 --- a/web/src/views/Authentication/AuthForms/AuthLogin.js +++ b/web/src/views/Authentication/AuthForms/AuthLogin.js @@ -1,15 +1,14 @@ import { useState } from 'react'; import { useSelector } from 'react-redux'; +import { Link } from 'react-router-dom'; // material-ui import { useTheme } from '@mui/material/styles'; import { Box, Button, - // Checkbox, Divider, FormControl, - // FormControlLabel, FormHelperText, Grid, IconButton, @@ -236,7 +235,13 @@ const LoginForm = ({ ...others }) => { } label="记住我" /> */} - + 忘记密码? diff --git a/web/src/views/Authentication/AuthForms/ForgetPasswordForm.js b/web/src/views/Authentication/AuthForms/ForgetPasswordForm.js new file mode 100644 index 00000000..82135bf0 --- /dev/null +++ b/web/src/views/Authentication/AuthForms/ForgetPasswordForm.js @@ -0,0 +1,144 @@ +import { useState, useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import Turnstile from 'react-turnstile'; +import { API } from 'utils/api'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import { Box, Button, FormControl, FormHelperText, InputLabel, OutlinedInput, Typography } from '@mui/material'; + +// third party +import * as Yup from 'yup'; +import { Formik } from 'formik'; + +// project imports +import AnimateButton from 'ui-component/extended/AnimateButton'; + +// assets +import { showError, showInfo } from 'utils/common'; + +// ===========================|| FIREBASE - REGISTER ||=========================== // + +const ForgetPasswordForm = ({ ...others }) => { + const theme = useTheme(); + const siteInfo = useSelector((state) => state.siteInfo); + + const [sendEmail, setSendEmail] = useState(false); + const [turnstileEnabled, setTurnstileEnabled] = useState(false); + const [turnstileSiteKey, setTurnstileSiteKey] = useState(''); + const [turnstileToken, setTurnstileToken] = useState(''); + const [disableButton, setDisableButton] = useState(false); + const [countdown, setCountdown] = useState(30); + + const submit = async (values, { setSubmitting }) => { + setDisableButton(true); + setSubmitting(true); + if (turnstileEnabled && turnstileToken === '') { + showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!'); + setSubmitting(false); + return; + } + const res = await API.get(`/api/reset_password?email=${values.email}&turnstile=${turnstileToken}`); + const { success, message } = res.data; + if (success) { + showSuccess('重置邮件发送成功,请检查邮箱!'); + setSendEmail(true); + } else { + showError(message); + setDisableButton(false); + setCountdown(30); + } + setSubmitting(false); + }; + + useEffect(() => { + let countdownInterval = null; + if (disableButton && countdown > 0) { + countdownInterval = setInterval(() => { + setCountdown(countdown - 1); + }, 1000); + } else if (countdown === 0) { + setDisableButton(false); + setCountdown(30); + } + return () => clearInterval(countdownInterval); + }, [disableButton, countdown]); + + useEffect(() => { + if (siteInfo.turnstile_check) { + setTurnstileEnabled(true); + setTurnstileSiteKey(siteInfo.turnstile_site_key); + } + }, [siteInfo]); + + return ( + <> + {sendEmail ? ( + + 重置邮件发送成功,请检查邮箱! + + ) : ( + + {({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => ( +
+ + Email + + {touched.email && errors.email && ( + + {errors.email} + + )} + + + {turnstileEnabled ? ( + { + setTurnstileToken(token); + }} + /> + ) : ( + <> + )} + + + + + + + + )} +
+ )} + + ); +}; + +export default ForgetPasswordForm; diff --git a/web/src/views/Authentication/AuthForms/ResetPasswordForm.js b/web/src/views/Authentication/AuthForms/ResetPasswordForm.js new file mode 100644 index 00000000..632bbde3 --- /dev/null +++ b/web/src/views/Authentication/AuthForms/ResetPasswordForm.js @@ -0,0 +1,62 @@ +import { useState, useEffect } from 'react'; +import { useSearchParams } from 'react-router-dom'; + +// material-ui +import { Button, Stack, Typography, Alert } from '@mui/material'; + +// assets +import { showError, showInfo } from 'utils/common'; + +// ===========================|| FIREBASE - REGISTER ||=========================== // + +const ResetPasswordForm = () => { + const [searchParams] = useSearchParams(); + const [inputs, setInputs] = useState({ + email: '', + token: '' + }); + const [newPassword, setNewPassword] = useState(''); + + const submit = async () => { + const res = await API.post(`/api/user/reset`, inputs); + const { success, message } = res.data; + if (success) { + let password = res.data.data; + setNewPassword(password); + navigator.clipboard.writeText(password); + showInfo(`新密码已复制到剪贴板:${password}`); + } else { + showError(message); + } + }; + + useEffect(() => { + let email = searchParams.get('email'); + let token = searchParams.get('token'); + setInputs({ + token, + email + }); + }, []); + + return ( + + {!inputs.email || !inputs.token ? ( + + 无效的链接 + + ) : newPassword ? ( + + 你的新密码是: {newPassword}
+ 请登录后及时修改密码 +
+ ) : ( + + )} +
+ ); +}; + +export default ResetPasswordForm;