From 350a022ea33f65544580c1cef54705d456090fae Mon Sep 17 00:00:00 2001 From: samanhappy Date: Wed, 17 Dec 2025 13:24:07 +0800 Subject: [PATCH] feat: enhance login error handling and add server unavailable message (#516) --- frontend/src/contexts/AuthContext.tsx | 20 +++++++----- frontend/src/pages/LoginPage.tsx | 44 ++++++++++++++++++++++++--- frontend/src/services/authService.ts | 2 +- locales/en.json | 1 + locales/fr.json | 1 + locales/tr.json | 1 + locales/zh.json | 1 + 7 files changed, 58 insertions(+), 12 deletions(-) diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index c5db75c..4b1e0fe 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -14,14 +14,17 @@ const initialState: AuthState = { // Create auth context const AuthContext = createContext<{ 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; logout: () => void; }>({ auth: initialState, login: async () => ({ success: false }), register: async () => false, - logout: () => { }, + logout: () => {}, }); // Auth provider component @@ -90,7 +93,10 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => }, []); // 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 { const response = await authService.login({ username, password }); @@ -111,7 +117,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => loading: false, error: response.message || 'Authentication failed', }); - return { success: false }; + return { success: false, message: response.message }; } } catch (error) { setAuth({ @@ -119,7 +125,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => loading: false, 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 ( username: string, password: string, - isAdmin = false + isAdmin = false, ): Promise => { try { const response = await authService.register({ username, password, isAdmin }); @@ -175,4 +181,4 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => }; // Custom hook to use auth context -export const useAuth = () => useContext(AuthContext); \ No newline at end of file +export const useAuth = () => useContext(AuthContext); diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index f39018d..0e47085 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -44,6 +44,24 @@ const LoginPage: React.FC = () => { return sanitizeReturnUrl(params.get('returnUrl')); }, [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(() => { if (!returnUrl) { return '/'; @@ -100,10 +118,20 @@ const LoginPage: React.FC = () => { redirectAfterLogin(); } } else { - setError(t('auth.loginFailed')); + const message = result.message; + if (isServerUnavailableError(message)) { + setError(t('auth.serverUnavailable')); + } else { + setError(t('auth.loginFailed')); + } } } catch (err) { - setError(t('auth.loginError')); + const message = err instanceof Error ? err.message : undefined; + if (isServerUnavailableError(message)) { + setError(t('auth.serverUnavailable')); + } else { + setError(t('auth.loginError')); + } } finally { setLoading(false); } @@ -131,13 +159,21 @@ const LoginPage: React.FC = () => { }} />
- + - +
diff --git a/frontend/src/services/authService.ts b/frontend/src/services/authService.ts index ddfbdd6..a5f9255 100644 --- a/frontend/src/services/authService.ts +++ b/frontend/src/services/authService.ts @@ -29,7 +29,7 @@ export const login = async (credentials: LoginCredentials): Promise