mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-23 18:29:21 -05:00
feat: enhance login error handling and add server unavailable message (#516)
This commit is contained in:
@@ -14,7 +14,10 @@ const initialState: AuthState = {
|
|||||||
// Create auth context
|
// Create auth context
|
||||||
const AuthContext = createContext<{
|
const AuthContext = createContext<{
|
||||||
auth: AuthState;
|
auth: AuthState;
|
||||||
login: (username: string, password: string) => Promise<{ success: boolean; isUsingDefaultPassword?: boolean }>;
|
login: (
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
) => Promise<{ success: boolean; isUsingDefaultPassword?: boolean; message?: string }>;
|
||||||
register: (username: string, password: string, isAdmin?: boolean) => Promise<boolean>;
|
register: (username: string, password: string, isAdmin?: boolean) => Promise<boolean>;
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
}>({
|
}>({
|
||||||
@@ -90,7 +93,10 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Login function
|
// Login function
|
||||||
const login = async (username: string, password: string): Promise<{ success: boolean; isUsingDefaultPassword?: boolean }> => {
|
const login = async (
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
): Promise<{ success: boolean; isUsingDefaultPassword?: boolean; message?: string }> => {
|
||||||
try {
|
try {
|
||||||
const response = await authService.login({ username, password });
|
const response = await authService.login({ username, password });
|
||||||
|
|
||||||
@@ -111,7 +117,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||||||
loading: false,
|
loading: false,
|
||||||
error: response.message || 'Authentication failed',
|
error: response.message || 'Authentication failed',
|
||||||
});
|
});
|
||||||
return { success: false };
|
return { success: false, message: response.message };
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setAuth({
|
setAuth({
|
||||||
@@ -119,7 +125,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||||||
loading: false,
|
loading: false,
|
||||||
error: 'Authentication failed',
|
error: 'Authentication failed',
|
||||||
});
|
});
|
||||||
return { success: false };
|
return { success: false, message: error instanceof Error ? error.message : undefined };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -127,7 +133,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||||||
const register = async (
|
const register = async (
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
isAdmin = false
|
isAdmin = false,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await authService.register({ username, password, isAdmin });
|
const response = await authService.register({ username, password, isAdmin });
|
||||||
|
|||||||
@@ -44,6 +44,24 @@ const LoginPage: React.FC = () => {
|
|||||||
return sanitizeReturnUrl(params.get('returnUrl'));
|
return sanitizeReturnUrl(params.get('returnUrl'));
|
||||||
}, [location.search]);
|
}, [location.search]);
|
||||||
|
|
||||||
|
const isServerUnavailableError = useCallback((message?: string) => {
|
||||||
|
if (!message) return false;
|
||||||
|
const normalized = message.toLowerCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
normalized.includes('failed to fetch') ||
|
||||||
|
normalized.includes('networkerror') ||
|
||||||
|
normalized.includes('network error') ||
|
||||||
|
normalized.includes('connection refused') ||
|
||||||
|
normalized.includes('unable to connect') ||
|
||||||
|
normalized.includes('fetch error') ||
|
||||||
|
normalized.includes('econnrefused') ||
|
||||||
|
normalized.includes('http 500') ||
|
||||||
|
normalized.includes('internal server error') ||
|
||||||
|
normalized.includes('proxy error')
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const buildRedirectTarget = useCallback(() => {
|
const buildRedirectTarget = useCallback(() => {
|
||||||
if (!returnUrl) {
|
if (!returnUrl) {
|
||||||
return '/';
|
return '/';
|
||||||
@@ -99,11 +117,21 @@ const LoginPage: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
redirectAfterLogin();
|
redirectAfterLogin();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const message = result.message;
|
||||||
|
if (isServerUnavailableError(message)) {
|
||||||
|
setError(t('auth.serverUnavailable'));
|
||||||
} else {
|
} else {
|
||||||
setError(t('auth.loginFailed'));
|
setError(t('auth.loginFailed'));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : undefined;
|
||||||
|
if (isServerUnavailableError(message)) {
|
||||||
|
setError(t('auth.serverUnavailable'));
|
||||||
|
} else {
|
||||||
setError(t('auth.loginError'));
|
setError(t('auth.loginError'));
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -131,13 +159,21 @@ const LoginPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="pointer-events-none absolute inset-0 -z-10">
|
<div className="pointer-events-none absolute inset-0 -z-10">
|
||||||
<svg className="h-full w-full opacity-[0.08] dark:opacity-[0.12]" xmlns="http://www.w3.org/2000/svg">
|
<svg
|
||||||
|
className="h-full w-full opacity-[0.08] dark:opacity-[0.12]"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<pattern id="grid" width="32" height="32" patternUnits="userSpaceOnUse">
|
<pattern id="grid" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||||
<path d="M 32 0 L 0 0 0 32" fill="none" stroke="currentColor" strokeWidth="0.5" />
|
<path d="M 32 0 L 0 0 0 32" fill="none" stroke="currentColor" strokeWidth="0.5" />
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs>
|
</defs>
|
||||||
<rect width="100%" height="100%" fill="url(#grid)" className="text-gray-400 dark:text-gray-300" />
|
<rect
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
fill="url(#grid)"
|
||||||
|
className="text-gray-400 dark:text-gray-300"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const login = async (credentials: LoginCredentials): Promise<AuthResponse
|
|||||||
console.error('Login error:', error);
|
console.error('Login error:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'An error occurred during login',
|
message: error instanceof Error ? error.message : 'An error occurred during login',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"emptyFields": "Username and password cannot be empty",
|
"emptyFields": "Username and password cannot be empty",
|
||||||
"loginFailed": "Login failed, please check your username and password",
|
"loginFailed": "Login failed, please check your username and password",
|
||||||
"loginError": "An error occurred during login",
|
"loginError": "An error occurred during login",
|
||||||
|
"serverUnavailable": "Unable to connect to the server. Please check your network connection or try again later",
|
||||||
"currentPassword": "Current Password",
|
"currentPassword": "Current Password",
|
||||||
"newPassword": "New Password",
|
"newPassword": "New Password",
|
||||||
"confirmPassword": "Confirm Password",
|
"confirmPassword": "Confirm Password",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"emptyFields": "Le nom d'utilisateur et le mot de passe ne peuvent pas être vides",
|
"emptyFields": "Le nom d'utilisateur et le mot de passe ne peuvent pas être vides",
|
||||||
"loginFailed": "Échec de la connexion, veuillez vérifier votre nom d'utilisateur et votre mot de passe",
|
"loginFailed": "Échec de la connexion, veuillez vérifier votre nom d'utilisateur et votre mot de passe",
|
||||||
"loginError": "Une erreur est survenue lors de la connexion",
|
"loginError": "Une erreur est survenue lors de la connexion",
|
||||||
|
"serverUnavailable": "Impossible de se connecter au serveur. Veuillez vérifier votre connexion réseau ou réessayer plus tard",
|
||||||
"currentPassword": "Mot de passe actuel",
|
"currentPassword": "Mot de passe actuel",
|
||||||
"newPassword": "Nouveau mot de passe",
|
"newPassword": "Nouveau mot de passe",
|
||||||
"confirmPassword": "Confirmer le mot de passe",
|
"confirmPassword": "Confirmer le mot de passe",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"emptyFields": "Kullanıcı adı ve şifre boş olamaz",
|
"emptyFields": "Kullanıcı adı ve şifre boş olamaz",
|
||||||
"loginFailed": "Giriş başarısız, lütfen kullanıcı adınızı ve şifrenizi kontrol edin",
|
"loginFailed": "Giriş başarısız, lütfen kullanıcı adınızı ve şifrenizi kontrol edin",
|
||||||
"loginError": "Giriş sırasında bir hata oluştu",
|
"loginError": "Giriş sırasında bir hata oluştu",
|
||||||
|
"serverUnavailable": "Sunucuya bağlanılamıyor. Lütfen ağ bağlantınızı kontrol edin veya daha sonra tekrar deneyin",
|
||||||
"currentPassword": "Mevcut Şifre",
|
"currentPassword": "Mevcut Şifre",
|
||||||
"newPassword": "Yeni Şifre",
|
"newPassword": "Yeni Şifre",
|
||||||
"confirmPassword": "Şifreyi Onayla",
|
"confirmPassword": "Şifreyi Onayla",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"emptyFields": "用户名和密码不能为空",
|
"emptyFields": "用户名和密码不能为空",
|
||||||
"loginFailed": "登录失败,请检查用户名和密码",
|
"loginFailed": "登录失败,请检查用户名和密码",
|
||||||
"loginError": "登录过程中出现错误",
|
"loginError": "登录过程中出现错误",
|
||||||
|
"serverUnavailable": "无法连接到服务器,请检查网络连接或稍后再试",
|
||||||
"currentPassword": "当前密码",
|
"currentPassword": "当前密码",
|
||||||
"newPassword": "新密码",
|
"newPassword": "新密码",
|
||||||
"confirmPassword": "确认密码",
|
"confirmPassword": "确认密码",
|
||||||
|
|||||||
Reference in New Issue
Block a user