Files
yggdrasil-go/frontend/src/reset.tsx
Gardel f6ac467265
Some checks failed
Release / build (push) Failing after 3m38s
feat: password reset support
2025-03-30 22:22:24 +08:00

155 lines
6.1 KiB
TypeScript

/*
* Copyright (C) 2023-2025. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import React from 'react';
import Button from '@mui/material/Button';
import {
Box,
Container,
FilledInput,
FormControl,
IconButton,
InputAdornment,
InputLabel,
Paper,
TextField
} from '@mui/material';
import {Visibility, VisibilityOff} from '@mui/icons-material';
import {AppState} from './types';
import './reset.css';
import {SubmitHandler, useForm} from 'react-hook-form';
import axios from 'axios';
import {useSnackbar} from 'notistack';
type Inputs = {
username: string,
profileName: string,
password: string
};
function PasswordReset(props: { appData: AppState, setAppData: React.Dispatch<React.SetStateAction<AppState>> }) {
const {appData, setAppData} = props;
const {enqueueSnackbar} = useSnackbar();
const {register, handleSubmit, formState: {errors}} = useForm<Inputs>();
const [submitting, setSubmitting] = React.useState(false);
const onSubmit: SubmitHandler<Inputs> = data => {
setSubmitting(true);
const hash = window.location.hash;
if (!hash) {
setSubmitting(false);
enqueueSnackbar('链接失效,请重新打开', {variant: 'error'});
return;
}
const params = new URLSearchParams(hash.substring(1));
axios.post('/authserver/resetPassword', {
email: data.username,
password: data.password,
accessToken: params.get('passwordResetToken'),
})
.then(() => {
toLogin();
window.location.replace('/profile/')
enqueueSnackbar("重置成功", {variant: 'success'});
})
.catch(e => {
const response = e.response;
if (response && response.status >= 400 && response.status < 500) {
let errorMessage = response.data.errorMessage ?? response.data;
enqueueSnackbar('重置失败: ' + errorMessage, {variant: 'error'});
} else {
enqueueSnackbar('网络错误:' + e.message, {variant: 'error'});
}
})
.finally(() => setSubmitting(false))
};
const [showPassword, setShowPassword] = React.useState(false);
const handleClickShowPassword = () => setShowPassword((show) => !show);
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
};
const toLogin = () => {
setAppData({
...appData,
passwordReset: false
});
}
return (
<Container maxWidth={'sm'}>
<Paper className={'reset-card'}>
<section className="header">
<h1></h1>
</section>
<Box component="form" autoComplete="off" onSubmit={handleSubmit(onSubmit)}>
<div className='username'>
<TextField
id="username-input"
name='username'
fullWidth
label="邮箱"
variant="filled"
required
error={errors.username && true}
type='email'
slotProps={{
htmlInput: {
...register('username', {required: true})
}
}}
/>
</div>
<div className='password'>
<FormControl fullWidth variant="filled" required error={errors.password && true}>
<InputLabel htmlFor="password-input"></InputLabel>
<FilledInput
id="password-input"
name="password"
required
type={showPassword ? 'text' : 'password'}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="显示密码"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end">
{showPassword ? <VisibilityOff/> : <Visibility/>}
</IconButton>
</InputAdornment>
}
inputProps={{
minLength: '6',
...register('password', {required: true, minLength: 6})
}}
/>
</FormControl>
</div>
<div className='button-container'>
<Button variant='contained' onClick={toLogin}></Button>
<Button variant='contained' type='submit' disabled={submitting}></Button>
</div>
</Box>
</Paper>
</Container>
);
}
export default PasswordReset;