fix: added auth token refresh logic in event source provider error handler

This commit is contained in:
sawhil 2025-03-27 13:32:55 +05:30 committed by Sahil Khan
parent a4ed9e4d47
commit ba33c885d5
2 changed files with 72 additions and 11 deletions

View File

@ -50,6 +50,8 @@ function LiveLogsContainer(): JSX.Element {
handleCloseConnection, handleCloseConnection,
initialLoading, initialLoading,
isConnectionLoading, isConnectionLoading,
isConnectionError,
reconnectDueToError,
} = useEventSource(); } = useEventSource();
const compositeQuery = useGetCompositeQueryParam(); const compositeQuery = useGetCompositeQueryParam();
@ -153,6 +155,24 @@ function LiveLogsContainer(): JSX.Element {
handleStartNewConnection, handleStartNewConnection,
]); ]);
useEffect((): (() => void) | undefined => {
if (isConnectionError && reconnectDueToError && compositeQuery) {
console.log('uncaught refetch try from component', reconnectDueToError);
// Small delay to prevent immediate reconnection attempts
const reconnectTimer = setTimeout(() => {
handleStartNewConnection(compositeQuery);
}, 1000);
return (): void => clearTimeout(reconnectTimer);
}
return undefined;
}, [
isConnectionError,
reconnectDueToError,
compositeQuery,
handleStartNewConnection,
]);
useEffect(() => { useEffect(() => {
const prefetchedList = queryLocationState?.listQueryPayload[0]?.list; const prefetchedList = queryLocationState?.listQueryPayload[0]?.list;

View File

@ -1,6 +1,11 @@
import { apiV3 } from 'api/apiV1'; import { apiV3 } from 'api/apiV1';
import getLocalStorageApi from 'api/browser/localstorage/get';
import loginApi from 'api/user/login';
import { Logout } from 'api/utils';
import afterLogin from 'AppRoutes/utils';
import { ENVIRONMENT } from 'constants/env'; import { ENVIRONMENT } from 'constants/env';
import { LIVE_TAIL_HEARTBEAT_TIMEOUT } from 'constants/liveTail'; import { LIVE_TAIL_HEARTBEAT_TIMEOUT } from 'constants/liveTail';
import { LOCALSTORAGE } from 'constants/localStorage';
import { EventListener, EventSourcePolyfill } from 'event-source-polyfill'; import { EventListener, EventSourcePolyfill } from 'event-source-polyfill';
import { import {
createContext, createContext,
@ -13,14 +18,13 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { useAppContext } from './App/App';
interface IEventSourceContext { interface IEventSourceContext {
eventSourceInstance: EventSourcePolyfill | null; eventSourceInstance: EventSourcePolyfill | null;
isConnectionOpen: boolean; isConnectionOpen: boolean;
isConnectionLoading: boolean; isConnectionLoading: boolean;
isConnectionError: boolean; isConnectionError: boolean;
initialLoading: boolean; initialLoading: boolean;
reconnectDueToError: boolean;
handleStartOpenConnection: (urlProps: { handleStartOpenConnection: (urlProps: {
url?: string; url?: string;
queryString: string; queryString: string;
@ -35,6 +39,7 @@ const EventSourceContext = createContext<IEventSourceContext>({
isConnectionLoading: false, isConnectionLoading: false,
initialLoading: true, initialLoading: true,
isConnectionError: false, isConnectionError: false,
reconnectDueToError: false,
handleStartOpenConnection: () => {}, handleStartOpenConnection: () => {},
handleCloseConnection: () => {}, handleCloseConnection: () => {},
handleSetInitialLoading: () => {}, handleSetInitialLoading: () => {},
@ -47,9 +52,9 @@ export function EventSourceProvider({
const [isConnectionLoading, setIsConnectionLoading] = useState<boolean>(false); const [isConnectionLoading, setIsConnectionLoading] = useState<boolean>(false);
const [isConnectionError, setIsConnectionError] = useState<boolean>(false); const [isConnectionError, setIsConnectionError] = useState<boolean>(false);
const [initialLoading, setInitialLoading] = useState<boolean>(true); const [reconnectDueToError, setReconnectDueToError] = useState<boolean>(false);
const { user } = useAppContext(); const [initialLoading, setInitialLoading] = useState<boolean>(true);
const eventSourceRef = useRef<EventSourcePolyfill | null>(null); const eventSourceRef = useRef<EventSourcePolyfill | null>(null);
@ -63,15 +68,49 @@ export function EventSourceProvider({
setInitialLoading(false); setInitialLoading(false);
}, []); }, []);
const handleErrorConnection: EventListener = useCallback(() => { const handleErrorConnection: EventListener = useCallback(async () => {
setIsConnectionOpen(false); setIsConnectionOpen(false);
setIsConnectionLoading(false); setIsConnectionLoading(true);
setIsConnectionError(true);
setInitialLoading(false); setInitialLoading(false);
if (!eventSourceRef.current) return; try {
const response = await loginApi({
refreshToken: getLocalStorageApi(LOCALSTORAGE.REFRESH_AUTH_TOKEN) || '',
});
console.log('uncaught token refresh started', { response });
if (response.statusCode === 200) {
// Update tokens in local storage
afterLogin(
response.payload.userId,
response.payload.accessJwt,
response.payload.refreshJwt,
true,
);
// If token refresh was successful, we'll let the component
// handle reconnection through the reconnectDueToError state
setReconnectDueToError(true);
setIsConnectionError(true);
return;
}
// If token refresh failed, logout the user
if (!eventSourceRef.current) return;
eventSourceRef.current.close(); eventSourceRef.current.close();
setIsConnectionError(true);
Logout();
} catch (error) {
// If there was an error during token refresh, we'll just
// let the component handle the error state
console.error('Error refreshing token:', error);
setIsConnectionError(true);
if (!eventSourceRef.current) return;
eventSourceRef.current.close();
Logout();
}
}, []); }, []);
const destroyEventSourceSession = useCallback(() => { const destroyEventSourceSession = useCallback(() => {
@ -100,7 +139,7 @@ export function EventSourceProvider({
eventSourceRef.current = new EventSourcePolyfill(eventSourceUrl, { eventSourceRef.current = new EventSourcePolyfill(eventSourceUrl, {
headers: { headers: {
Authorization: `Bearer ${user?.accessJwt}`, Authorization: `Bearer ${getLocalStorageApi(LOCALSTORAGE.AUTH_TOKEN)}`,
}, },
heartbeatTimeout: LIVE_TAIL_HEARTBEAT_TIMEOUT, heartbeatTimeout: LIVE_TAIL_HEARTBEAT_TIMEOUT,
}); });
@ -111,7 +150,7 @@ export function EventSourceProvider({
eventSourceRef.current.addEventListener('error', handleErrorConnection); eventSourceRef.current.addEventListener('error', handleErrorConnection);
eventSourceRef.current.addEventListener('open', handleOpenConnection); eventSourceRef.current.addEventListener('open', handleOpenConnection);
}, },
[user, handleErrorConnection, handleOpenConnection], [handleErrorConnection, handleOpenConnection],
); );
useEffect( useEffect(
@ -128,6 +167,7 @@ export function EventSourceProvider({
isConnectionLoading, isConnectionLoading,
isConnectionOpen, isConnectionOpen,
initialLoading, initialLoading,
reconnectDueToError,
handleStartOpenConnection, handleStartOpenConnection,
handleCloseConnection, handleCloseConnection,
handleSetInitialLoading, handleSetInitialLoading,
@ -137,6 +177,7 @@ export function EventSourceProvider({
isConnectionLoading, isConnectionLoading,
isConnectionOpen, isConnectionOpen,
initialLoading, initialLoading,
reconnectDueToError,
handleStartOpenConnection, handleStartOpenConnection,
handleCloseConnection, handleCloseConnection,
handleSetInitialLoading, handleSetInitialLoading,