feat(auth): enhance password change flow and error handling
- Return response from useProfileSetting hook after password change
This commit is contained in:
@@ -120,7 +120,7 @@ const DialogComponent: React.FC<DialogComponentProps> = ({ dialog, onClose }) =>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', flex: 1 }}>
|
||||
{getDialogIcon()}
|
||||
<Typography variant="h6" component="span">
|
||||
{config.title || t('dialog.defaultTitle')}
|
||||
{config.title || t('dialog.confirm')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<IconButton
|
||||
|
||||
@@ -394,12 +394,12 @@ export const useLoginForm = () => {
|
||||
password: {
|
||||
required: t('login.passwordPlaceholder'),
|
||||
minLength: {
|
||||
value: 8,
|
||||
value: 6,
|
||||
message: t('setting.passwordMinLength')
|
||||
}
|
||||
},
|
||||
confirmPassword: {
|
||||
required: t('confirmPasswordRequired'),
|
||||
required: t('login.confirmPasswordRequired'),
|
||||
validate: (value: string) => {
|
||||
const pwd = registerForm.getValues('password');
|
||||
return value === pwd || t('setting.passwordMismatch');
|
||||
|
||||
@@ -42,6 +42,7 @@ export function useProfileSetting() {
|
||||
password: oldPassword,
|
||||
new_password: newPassword,
|
||||
});
|
||||
return res;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -262,6 +262,11 @@ export default {
|
||||
emailInvalid: 'Invalid email address',
|
||||
passwordLabel: 'Password',
|
||||
passwordPlaceholder: 'Please input password',
|
||||
confirmPasswordRequired: 'Please confirm your password',
|
||||
confirmPassword: 'Confirm password',
|
||||
confirmPasswordMessage: 'Please confirm your password!',
|
||||
confirmPasswordNonMatchMessage:
|
||||
'The confirm password that you entered do not match!',
|
||||
rememberMe: 'Remember me',
|
||||
signInTip: 'Don’t have an account?',
|
||||
signUpTip: 'Already have an account?',
|
||||
@@ -942,7 +947,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
||||
serverTypeRequired: 'Please select server type',
|
||||
testConnection: 'Test Connection',
|
||||
testing: 'Testing...',
|
||||
connectionSuccess: 'Connection successful! Found {count} tools',
|
||||
connectionSuccess: 'Connection successful! Found {{count}} tools',
|
||||
availableTools: 'Available Tools',
|
||||
connectionFailed: 'Connection failed',
|
||||
testFailed: 'Test failed',
|
||||
|
||||
@@ -244,6 +244,11 @@ export default {
|
||||
emailInvalid: '无效的邮箱地址',
|
||||
passwordLabel: '密码',
|
||||
passwordPlaceholder: '请输入密码',
|
||||
confirmPasswordRequired: '请确认您的密码',
|
||||
confirmPassword: '确认密码',
|
||||
confirmPasswordMessage: '请确认您的密码!',
|
||||
confirmPasswordNonMatchMessage:
|
||||
'确认密码与密码不匹配!',
|
||||
rememberMe: '记住我',
|
||||
signInTip: '没有帐户?',
|
||||
signUpTip: '已经有帐户?',
|
||||
@@ -941,7 +946,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
serverTypeRequired: '请选择服务器类型',
|
||||
testConnection: '测试连接',
|
||||
testing: '测试中...',
|
||||
connectionSuccess: '连接成功!发现 {count} 个工具',
|
||||
connectionSuccess: '连接成功!发现 {{count}} 个工具',
|
||||
availableTools: '可用工具',
|
||||
connectionFailed: '连接失败',
|
||||
testFailed: '测试失败',
|
||||
|
||||
@@ -224,7 +224,7 @@ const Login = () => {
|
||||
fullWidth
|
||||
id="register-confirm-password"
|
||||
type={showConfirmPassword ? 'text' : 'password'}
|
||||
placeholder={t('confirmPassword')}
|
||||
placeholder={t('login.confirmPassword')}
|
||||
autoComplete="new-password"
|
||||
{...registerForm.register('confirmPassword', registerValidation.confirmPassword)}
|
||||
error={!!registerForm.formState.errors.confirmPassword}
|
||||
|
||||
@@ -20,7 +20,8 @@ import logger from '@/utils/logger';
|
||||
interface ChangePasswordDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
changeUserPassword: (data: { password: string; new_password: string }) => Promise<void>;
|
||||
onSuccess: () => void;
|
||||
changeUserPassword: (data: { password: string; new_password: string }) => Promise<any>;
|
||||
}
|
||||
|
||||
interface PasswordFormData {
|
||||
@@ -32,7 +33,7 @@ interface PasswordFormData {
|
||||
/**
|
||||
* 修改密码对话框
|
||||
*/
|
||||
function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePasswordDialogProps) {
|
||||
function ChangePasswordDialog({ open, onClose, onSuccess, changeUserPassword }: ChangePasswordDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const { showMessage } = useSnackbar();
|
||||
|
||||
@@ -133,20 +134,19 @@ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePassw
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
await changeUserPassword({
|
||||
const res = await changeUserPassword({
|
||||
password: formData.currentPassword,
|
||||
new_password: formData.newPassword
|
||||
});
|
||||
|
||||
logger.info('修改密码成功:', res);
|
||||
|
||||
showMessage.success(t('setting.passwordChangeSuccess'));
|
||||
handleClose();
|
||||
// delay 1000 ms
|
||||
setTimeout(() => onSuccess(), 1000);
|
||||
} catch (error: any) {
|
||||
logger.error('修改密码失败:', error);
|
||||
if (error.response?.status === 400) {
|
||||
showMessage.error(t('setting.currentPasswordIncorrect'));
|
||||
} else {
|
||||
showMessage.error(t('setting.passwordChangeError'));
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -217,7 +217,7 @@ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePassw
|
||||
value={formData.newPassword}
|
||||
onChange={handleInputChange('newPassword')}
|
||||
error={!!errors.newPassword}
|
||||
helperText={errors.newPassword || '密码长度至少6位'}
|
||||
helperText={errors.newPassword}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
|
||||
@@ -7,11 +7,14 @@ import ChangePasswordDialog from "./components/ChangePasswordDialog";
|
||||
import { useProfileSetting } from "@/hooks/setting-hooks";
|
||||
import logger from "@/utils/logger";
|
||||
import { useUserData } from "@/hooks/useUserData";
|
||||
import { useAuth } from '@/hooks/login-hooks';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
function ProfileSetting() {
|
||||
const { t } = useTranslation();
|
||||
const { userInfo, updateUserInfo: updateUserInfoFunc, changeUserPassword: changeUserPasswordFunc } = useProfileSetting();
|
||||
const { refreshUserData } = useUserData();
|
||||
const { logout } = useAuth();
|
||||
const [passwordDialogOpen, setPasswordDialogOpen] = useState(false);
|
||||
|
||||
logger.debug('userInfo', userInfo);
|
||||
@@ -66,6 +69,10 @@ function ProfileSetting() {
|
||||
<ChangePasswordDialog
|
||||
open={passwordDialogOpen}
|
||||
onClose={handleClosePasswordDialog}
|
||||
onSuccess={() => {
|
||||
// 使用 useAuth 的 logout,确保清除本地存储令牌并跳转登录
|
||||
logout();
|
||||
}}
|
||||
changeUserPassword={changeUserPasswordFunc}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
ExitToApp as ExitToAppIcon
|
||||
} from '@mui/icons-material';
|
||||
import { useTeamSetting } from '@/hooks/setting-hooks';
|
||||
import { useDialog } from '@/hooks/useDialog';
|
||||
|
||||
interface TenantUser {
|
||||
id: string;
|
||||
@@ -88,8 +89,15 @@ function TeamsSetting() {
|
||||
}
|
||||
};
|
||||
|
||||
const dialog = useDialog();
|
||||
|
||||
const handleDeleteUser = async (userId: string) => {
|
||||
await deleteUser(userId);
|
||||
dialog.confirm({
|
||||
content: t('setting.sureDelete'),
|
||||
onConfirm: async () => {
|
||||
await deleteUser(userId);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleAgreeTenant = async (tenantId: string) => {
|
||||
|
||||
@@ -184,17 +184,29 @@ request.interceptors.response.use(
|
||||
});
|
||||
|
||||
const { status, data } = error.response || {};
|
||||
if (status == 401) {
|
||||
logger.info('401', data)
|
||||
if (status == 401 || status == 400) {
|
||||
logger.info('401 || 400', data)
|
||||
const detail = data['detail']
|
||||
if (detail) {
|
||||
const error = new CustomError(detail || i18n.t('message.requestError'));
|
||||
if (detail.includes('not registered')) {
|
||||
if (status == 401) {
|
||||
const arr = ['not registered', 'Password error', 'Email and password do not match']
|
||||
const redirectArr = ['Invalid or expired token', 'No Authorization']
|
||||
if (arr.some(item => detail.includes(item))) {
|
||||
error.code = data?.code || -1;
|
||||
error.response = data;
|
||||
snackbar.error(detail);
|
||||
} else if (redirectArr.some(item => detail.includes(item))) {
|
||||
redirectToLogin();
|
||||
} else {
|
||||
error.code = data?.code || -1;
|
||||
error.response = data;
|
||||
snackbar.error(detail);
|
||||
}
|
||||
} else {
|
||||
error.code = data?.code || -1;
|
||||
error.response = data;
|
||||
snackbar.error(detail);
|
||||
} else {
|
||||
redirectToLogin();
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user