Skip to content

Commit

Permalink
找回密码
Browse files Browse the repository at this point in the history
  • Loading branch information
xmdhs committed Nov 24, 2023
1 parent 8bdff06 commit 50e4be3
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 36 deletions.
8 changes: 6 additions & 2 deletions frontend/src/Route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import Layout from '@/views/Layout'
import UserAdmin from "@/views/admin/UserAdmin";
import NeedLogin from "@/components/NeedLogin";
import Index from "@/views/Index";
import SignUpEmail from "@/views/SignUpEmail";
import SendEmail from "@/views/SendEmail";
import { sendForgotEmail, sendRegEmail } from "@/apis/apis";
import Forgot from "@/views/Forgot";

const router = createBrowserRouter([
{ path: "*", Component: Root },
Expand All @@ -24,7 +26,9 @@ function Root() {
<Route path="/*" element={<p>404</p>} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/register_email" element={<SignUpEmail />} />
<Route path="/register_email" element={<SendEmail title="注册" sendService={sendRegEmail} />} />
<Route path="/forgot_email" element={<SendEmail title="找回密码" anyEmail sendService={sendForgotEmail} />} />
<Route path="/forgot" element={<Forgot />} />

<Route element={<NeedLogin><Outlet /></NeedLogin>}>
<Route path="/profile" element={<Profile />} />
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/apis/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,28 @@ export async function sendRegEmail(email: string, captchaToken: string) {
})
})
return await apiGet<unknown>(r)
}

export async function sendForgotEmail(email: string, captchaToken: string) {
const r = await fetch(root() + "/api/v1/user/forgot_email", {
method: "POST",
body: JSON.stringify({
"email": email,
"captchaToken": captchaToken
})
})
return await apiGet<unknown>(r)
}


export async function forgotPassWord(email: string, emailJwt: string, password: string) {
const r = await fetch(root() + "/api/v1/user/forgot", {
method: "POST",
body: JSON.stringify({
"email": email,
"emailJwt": emailJwt,
"passWord": password,
})
})
return await apiGet<unknown>(r)
}
120 changes: 120 additions & 0 deletions frontend/src/views/Forgot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import CssBaseline from '@mui/material/CssBaseline';
import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import TextField from '@mui/material/TextField';
import { useTitle } from 'ahooks';
import { useEffect, useState } from 'react';
import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
import Loading from '@/components/Loading';
import { produce } from 'immer';
import { forgotPassWord } from '@/apis/apis';
import { useNavigate } from 'react-router-dom';

export default function Forgot() {
const [err, setErr] = useState("")
useTitle("找回密码")
const [passerr, setPasserr] = useState("")
const [pass, setPass] = useState({
pass1: "",
pass2: "",
})
const [load, setLoad] = useState(false)
const [email, setEmail] = useState("")
const [code, setCode] = useState("")
const navigate = useNavigate();

useEffect(() => {
if (pass.pass1 != pass.pass2 && pass.pass2 != "") {
setPasserr("密码不相等")
return
}
setPasserr("")
}, [pass.pass1, pass.pass2])

const u = new URL(location.href)

useEffect(() => {
setEmail(u.searchParams.get("email") ?? "")
setCode(u.searchParams.get("code") ?? "")
}, [u.searchParams])


const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setLoad(true)
forgotPassWord(email, code, pass.pass1).then(() => {
navigate("/")
}).catch(e => {
setErr(String(e))
}).finally(() => { setLoad(false) })
}


return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<Box
sx={{
marginTop: 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
找回密码
</Typography>
<Box component="form" noValidate onSubmit={onSubmit} sx={{ mt: 3 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
margin='dense'
fullWidth
label="新密码"
type="password"
required
onChange={p => setPass(produce(v => { v.pass1 = p.target.value }))}
autoComplete="new-password"
/>
</Grid>
<Grid item xs={12}>
<TextField
margin='dense'
fullWidth
label="确认新密码"
type="password"
required
error={passerr != ""}
helperText={passerr}
onChange={p => setPass(produce(v => { v.pass2 = p.target.value }))}
autoComplete="new-password"
/>
</Grid>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
提交
</Button>
</Box>
</Box>
<Snackbar anchorOrigin={{ vertical: 'top', horizontal: 'center' }} open={err !== ""}>
<Alert onClose={() => setErr("")} severity="error">{err}</Alert>
</Snackbar>

{load && <Loading />}
</Container>
)
}
19 changes: 15 additions & 4 deletions frontend/src/views/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
import { useSetAtom } from 'jotai';
import { token, user } from '@/store/store'
import { login } from '@/apis/apis'
import { getConfig, login } from '@/apis/apis'
import { Link as RouterLink, useNavigate } from "react-router-dom";
import Loading from '@/components/Loading'
import CheckInput, { refType } from '@/components/CheckInput'
import useTitle from '@/hooks/useTitle';
import CaptchaWidget from '@/components/CaptchaWidget';
import type { refType as CaptchaWidgetRef } from '@/components/CaptchaWidget'
import { ApiErr } from '@/apis/error';
import { useRequest } from 'ahooks';



Expand All @@ -35,6 +36,16 @@ export default function SignIn() {
const captchaRef = React.useRef<CaptchaWidgetRef>(null)
const [captchaToken, setCaptchaToken] = useState("");

const server = useRequest(getConfig, {
cacheKey: "/api/v1/config",
staleTime: 60000,
onError: e => {
console.warn(e)
setErr(String(e))
}
})


const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

Expand Down Expand Up @@ -66,7 +77,7 @@ export default function SignIn() {
switch (v.code) {
case 10:
setErr("验证码错误")
return
return
case 6:
setErr("密码或用户名错误")
return
Expand Down Expand Up @@ -137,9 +148,9 @@ export default function SignIn() {
</Button>
<Grid container>
<Grid item xs>
{/* <Link href="#" variant="body2">
{server.data?.NeedEmail && <Link component={RouterLink} to="/forgot_email" variant="body2">
忘记密码?
</Link> */}
</Link>}
</Grid>
<Grid item>
<Link component={RouterLink} to="/register" variant="body2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Select from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';
import TextField from '@mui/material/TextField';
import { useRequest, useTitle } from 'ahooks';
import { getConfig, sendRegEmail } from '@/apis/apis';
import { getConfig } from '@/apis/apis';
import { useEffect, useRef, useState } from 'react';
import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
Expand All @@ -26,14 +26,14 @@ import { useNavigate } from "react-router-dom";
import { ApiErr } from '@/apis/error';
import Loading from '@/components/Loading';

export default function SignUpEmail() {
export default function SendEmail({ title, anyEmail = false, sendService }: { title: string, anyEmail?: boolean, sendService: (email: string, captchaToken: string) => Promise<unknown> }) {
const [err, setErr] = useState("");
const [domain, setDomain] = useState("");
const [email, setEmail] = useState("")
const captchaRef = useRef<CaptchaWidgetRef>(null)
const [captchaToken, setCaptchaToken] = useState("");
const [open, setOpen] = useState(false);
useTitle("注册")
useTitle(title)
const navigate = useNavigate();
const [helperText, setHelperText] = useState("")
const [loading, setLoading] = useState(false);
Expand Down Expand Up @@ -73,7 +73,7 @@ export default function SignUpEmail() {
return email
})()

if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(sendEmail)){
if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(sendEmail)) {
setHelperText("邮箱格式错误")
return
}
Expand All @@ -82,7 +82,7 @@ export default function SignUpEmail() {
return
}
setLoading(true)
sendRegEmail(sendEmail, captchaToken).then(() => setOpen(true)).catch(e => {
sendService(sendEmail, captchaToken).then(() => setOpen(true)).catch(e => {
captchaRef.current?.reload()
console.warn(e)
if (e instanceof ApiErr) {
Expand Down Expand Up @@ -120,7 +120,7 @@ export default function SignUpEmail() {
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
输入邮箱
{title}
</Typography>
<Box component="form" noValidate onSubmit={onSubmit} sx={{ mt: 3 }}>
<Grid container spacing={2}>
Expand All @@ -135,7 +135,7 @@ export default function SignUpEmail() {
onChange={emailonChange}
/>
{
server.data?.AllowDomain.length != 0 &&
server.data?.AllowDomain.length != 0 && !anyEmail &&
<FormControl>
<InputLabel>域名</InputLabel>
<Select label="域名" value={domain} onChange={v => setDomain(v.target.value)}>
Expand Down Expand Up @@ -164,7 +164,7 @@ export default function SignUpEmail() {
<Dialog open={open}>
<DialogTitle>邮件已发送</DialogTitle>
<DialogContent>
<Typography>请到收件箱(或垃圾箱)点击验证链接以继续完成注册</Typography>
<Typography>请到收件箱(或垃圾箱)点击验证链接以继续</Typography>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>返回首页</Button>
Expand Down
1 change: 1 addition & 0 deletions handle/handelerror/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var errorHandlers = []errorHandler{
{auth.ErrTokenInvalid, model.ErrAuth, 401, slog.LevelDebug},
{email.ErrTokenInvalid, model.ErrAuth, 401, slog.LevelDebug},
{email.ErrSendLimit, model.ErrEmailSend, 403, slog.LevelDebug},
{service.ErrUsername, model.ErrPassWord, 401, slog.LevelInfo},
}

func (h *HandleError) Service(ctx context.Context, w http.ResponseWriter, err error) {
Expand Down
Loading

0 comments on commit 50e4be3

Please sign in to comment.