mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	refactor: simplify login redirect logic (#1987)
This commit is contained in:
		
							parent
							
								
									23e43b9114
								
							
						
					
					
						commit
						ae3d6c06cf
					
				| @ -5,7 +5,7 @@ import { LayoutPicker } from 'component/layout/LayoutPicker/LayoutPicker'; | ||||
| import Loader from 'component/common/Loader/Loader'; | ||||
| import NotFound from 'component/common/NotFound/NotFound'; | ||||
| import { ProtectedRoute } from 'component/common/ProtectedRoute/ProtectedRoute'; | ||||
| import SWRProvider from 'component/providers/SWRProvider/SWRProvider'; | ||||
| import { SWRProvider } from 'component/providers/SWRProvider/SWRProvider'; | ||||
| import ToastRenderer from 'component/common/ToastRenderer/ToastRenderer'; | ||||
| import { routes } from 'component/menu/routes'; | ||||
| import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails'; | ||||
| @ -21,7 +21,6 @@ export const App = () => { | ||||
|     const { authDetails } = useAuthDetails(); | ||||
|     const { user } = useAuthUser(); | ||||
|     const { isOss } = useUiConfig(); | ||||
|     const isLoggedIn = Boolean(user?.id); | ||||
|     const hasFetchedAuth = Boolean(authDetails || user); | ||||
|     usePlausibleTracker(); | ||||
| 
 | ||||
| @ -30,7 +29,7 @@ export const App = () => { | ||||
|         : routes; | ||||
| 
 | ||||
|     return ( | ||||
|         <SWRProvider isUnauthorized={!isLoggedIn}> | ||||
|         <SWRProvider> | ||||
|             <Suspense fallback={<Loader />}> | ||||
|                 <ConditionallyRender | ||||
|                     condition={!hasFetchedAuth} | ||||
|  | ||||
| @ -1,78 +1,18 @@ | ||||
| import { mutate, SWRConfig, useSWRConfig } from 'swr'; | ||||
| import { useNavigate } from 'react-router'; | ||||
| import useToast from 'hooks/useToast'; | ||||
| import { formatApiPath } from 'utils/formatPath'; | ||||
| import { SWRConfig } from 'swr'; | ||||
| import React from 'react'; | ||||
| import { USER_ENDPOINT_PATH } from 'hooks/api/getters/useAuth/useAuthEndpoint'; | ||||
| import { ResponseError } from 'utils/apiUtils'; | ||||
| import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; | ||||
| 
 | ||||
| interface ISWRProviderProps { | ||||
|     isUnauthorized: boolean; | ||||
| } | ||||
| export const SWRProvider: React.FC = ({ children }) => { | ||||
|     const { refetchUser } = useAuthUser(); | ||||
| 
 | ||||
| const INVALID_TOKEN_ERROR = 'InvalidTokenError'; | ||||
| 
 | ||||
| const SWRProvider: React.FC<ISWRProviderProps> = ({ | ||||
|     children, | ||||
|     isUnauthorized, | ||||
| }) => { | ||||
|     const { cache } = useSWRConfig(); | ||||
|     const navigate = useNavigate(); | ||||
|     const { setToastApiError } = useToast(); | ||||
| 
 | ||||
|     // @ts-expect-error
 | ||||
|     const handleFetchError = error => { | ||||
|         if (error.status === 401) { | ||||
|             const path = location.pathname; | ||||
|             // Only populate user with authDetails if 401 and
 | ||||
|             // error is not invalid token
 | ||||
|             if (error?.info?.name !== INVALID_TOKEN_ERROR) { | ||||
|                 mutate(USER_ENDPOINT_PATH, { ...error.info }, false); | ||||
|             } | ||||
| 
 | ||||
|             if ( | ||||
|                 path === formatApiPath('login') || | ||||
|                 path === formatApiPath('new-user') || | ||||
|                 path === formatApiPath('reset-password') || | ||||
|                 path === formatApiPath('forgotten-password') | ||||
|             ) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             // @ts-expect-error
 | ||||
|             cache.clear(); | ||||
| 
 | ||||
|             navigate('/login'); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!isUnauthorized) { | ||||
|             setToastApiError(error.message); | ||||
|     const onError = (error: Error) => { | ||||
|         if (error instanceof ResponseError && error.status === 401) { | ||||
|             // Refetch the user's data if they appear to be logged out.
 | ||||
|             // This may trigger a login page redirect in ProtectedRoute.
 | ||||
|             refetchUser(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <SWRConfig | ||||
|             value={{ | ||||
|                 onErrorRetry: ( | ||||
|                     error, | ||||
|                     _key, | ||||
|                     _config, | ||||
|                     revalidate, | ||||
|                     { retryCount } | ||||
|                 ) => { | ||||
|                     // Never retry on 404 or 401.
 | ||||
|                     if (error.status < 499) { | ||||
|                         return error; | ||||
|                     } | ||||
| 
 | ||||
|                     setTimeout(() => revalidate({ retryCount }), 5000); | ||||
|                 }, | ||||
|                 onError: handleFetchError, | ||||
|             }} | ||||
|         > | ||||
|             {children} | ||||
|         </SWRConfig> | ||||
|     ); | ||||
|     return <SWRConfig value={{ onError }}>{children}</SWRConfig>; | ||||
| }; | ||||
| 
 | ||||
| export default SWRProvider; | ||||
|  | ||||
| @ -1,24 +1,23 @@ | ||||
| import { ResponseError } from 'utils/apiUtils'; | ||||
| 
 | ||||
| const handleErrorResponses = (target: string) => async (res: Response) => { | ||||
|     if (!res.ok) { | ||||
|         const error = new Error( | ||||
|             `An error occurred while trying to get ${target}` | ||||
|         throw new ResponseError( | ||||
|             target, | ||||
|             res.status, | ||||
|             await parseErrorResponse(res) | ||||
|         ); | ||||
|         // Try to resolve body, but don't rethrow res.json is not a function
 | ||||
|         try { | ||||
|             // @ts-expect-error
 | ||||
|             error.info = await res.json(); | ||||
|         } catch (e) { | ||||
|             // @ts-expect-error
 | ||||
|             error.info = {}; | ||||
|         } | ||||
|         // @ts-expect-error
 | ||||
|         error.status = res.status; | ||||
|         // @ts-expect-error
 | ||||
|         error.statusText = res.statusText; | ||||
|         throw error; | ||||
|     } | ||||
| 
 | ||||
|     return res; | ||||
| }; | ||||
| 
 | ||||
| const parseErrorResponse = async (res: Response): Promise<unknown> => { | ||||
|     try { | ||||
|         return await res.json(); | ||||
|     } catch { | ||||
|         return res.statusText; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| export default handleErrorResponses; | ||||
|  | ||||
| @ -57,6 +57,18 @@ export class NotFoundError extends Error { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class ResponseError extends Error { | ||||
|     status: number; | ||||
|     body: unknown; | ||||
| 
 | ||||
|     constructor(target: string, status: number, body: unknown) { | ||||
|         super(`An error occurred while trying to get ${target}.`); | ||||
|         this.name = 'ResponseError'; | ||||
|         this.status = status; | ||||
|         this.body = body; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const headers = { | ||||
|     Accept: 'application/json', | ||||
|     'Content-Type': 'application/json', | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user