From 2f2b5345053d930d76db0dd1b1ed99c91b5f2445 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:38:45 +0100 Subject: [PATCH 01/34] fix: redirect to logout page on fetch error --- src/components/Error.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index cda7648ee..b9e1732ef 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -20,7 +20,7 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - if (err?.status === 'FETCH_ERROR') window.location.href = '/' + if (err?.status === 'FETCH_ERROR') window.location.href = '/logout-otomi' const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error From bdc89f1764b0c91b7a68ec412b390209d35e5a53 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 08:47:10 +0100 Subject: [PATCH 02/34] fix: redirect logout --- src/components/AccountPopover.tsx | 4 +++- src/components/Error.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/AccountPopover.tsx b/src/components/AccountPopover.tsx index d1a29bffa..02f638ebe 100644 --- a/src/components/AccountPopover.tsx +++ b/src/components/AccountPopover.tsx @@ -5,6 +5,7 @@ import { useDeleteCloudttyMutation } from 'redux/otomiApi' import { useSession } from 'providers/Session' import { getDomain, getEmailNoSymbols, getUserTeams } from 'layouts/Shell' import { clearLocalStorage } from 'hooks/useLocalStorage' +import { useHistory } from 'react-router-dom' import MenuPopover from './MenuPopover' import { IconButtonAnimate } from './animate' import SettingMode from './SettingMode' @@ -14,6 +15,7 @@ type Props = { } export default function AccountPopover({ email }: Props) { + const history = useHistory() const [open, setOpen] = useState(null) const [del] = useDeleteCloudttyMutation() const { user, oboTeamId } = useSession() @@ -39,7 +41,7 @@ export default function AccountPopover({ email }: Props) { // cloudtty resources will be automatically deleted by the API after a 2-hour timeout } clearLocalStorage('oboTeamId') - window.location.href = '/logout-otomi' + history.push('/logout-otomi') } return ( diff --git a/src/components/Error.tsx b/src/components/Error.tsx index b9e1732ef..cda7648ee 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -20,7 +20,7 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - if (err?.status === 'FETCH_ERROR') window.location.href = '/logout-otomi' + if (err?.status === 'FETCH_ERROR') window.location.href = '/' const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error From ae511c7008fa2aa55d88f24c00dd6b358f5b11c1 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:28:29 +0100 Subject: [PATCH 03/34] test: logout route --- src/App.tsx | 2 ++ src/components/AccountPopover.tsx | 2 +- src/components/Logout.tsx | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/components/Logout.tsx diff --git a/src/App.tsx b/src/App.tsx index 0f4ed0aaa..ad21bdf95 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -54,6 +54,7 @@ import Project from 'pages/Project' import Policy from 'pages/Policy' import Maintenance from 'pages/Maintenance' import PrivateRoute from 'components/AuthzRoute' +import Logout from 'components/Logout' import { HttpErrorBadRequest } from './utils/error' import { NotistackProvider, SnackbarUtilsConfigurator } from './utils/snack' @@ -175,6 +176,7 @@ function App() { + diff --git a/src/components/AccountPopover.tsx b/src/components/AccountPopover.tsx index 02f638ebe..c0757ba5a 100644 --- a/src/components/AccountPopover.tsx +++ b/src/components/AccountPopover.tsx @@ -41,7 +41,7 @@ export default function AccountPopover({ email }: Props) { // cloudtty resources will be automatically deleted by the API after a 2-hour timeout } clearLocalStorage('oboTeamId') - history.push('/logout-otomi') + history.push('/logout') } return ( diff --git a/src/components/Logout.tsx b/src/components/Logout.tsx new file mode 100644 index 000000000..7b82c877a --- /dev/null +++ b/src/components/Logout.tsx @@ -0,0 +1,11 @@ +import { useEffect } from 'react' + +function Logout() { + useEffect(() => { + window.location.href = '/logout-otomi' + }, []) + + return null +} + +export default Logout From fa4b0dc504c7582b8f39e1d419a2527f65b52e8a Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:51:29 +0100 Subject: [PATCH 04/34] feat: add logout page/route --- src/App.tsx | 2 +- src/{components => pages}/Logout.tsx | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) rename src/{components => pages}/Logout.tsx (53%) diff --git a/src/App.tsx b/src/App.tsx index ad21bdf95..a07092be1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -54,7 +54,7 @@ import Project from 'pages/Project' import Policy from 'pages/Policy' import Maintenance from 'pages/Maintenance' import PrivateRoute from 'components/AuthzRoute' -import Logout from 'components/Logout' +import Logout from 'pages/Logout' import { HttpErrorBadRequest } from './utils/error' import { NotistackProvider, SnackbarUtilsConfigurator } from './utils/snack' diff --git a/src/components/Logout.tsx b/src/pages/Logout.tsx similarity index 53% rename from src/components/Logout.tsx rename to src/pages/Logout.tsx index 7b82c877a..46f753495 100644 --- a/src/components/Logout.tsx +++ b/src/pages/Logout.tsx @@ -1,11 +1,9 @@ import { useEffect } from 'react' -function Logout() { +export default function Logout() { + // This component will redirect the user to the Keycloak logout page useEffect(() => { window.location.href = '/logout-otomi' }, []) - return null } - -export default Logout From 29e6771f3fb4996eaee04d503d5ed36ba1976200 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:10:56 +0100 Subject: [PATCH 05/34] fix: clear errors before logout --- src/pages/Logout.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index 46f753495..9bacbf35a 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -1,8 +1,12 @@ import { useEffect } from 'react' +import { useAppDispatch } from 'redux/hooks' +import { setError } from 'redux/reducers' export default function Logout() { // This component will redirect the user to the Keycloak logout page + const dispatch = useAppDispatch() useEffect(() => { + dispatch(setError(undefined)) window.location.href = '/logout-otomi' }, []) return null From d39aa68aa4f360e647ed49458935660e26e19a7a Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:01:17 +0100 Subject: [PATCH 06/34] test: logout errors --- src/pages/Logout.tsx | 3 ++- src/redux/store.tsx | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index 9bacbf35a..1bf3cc1c2 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -1,12 +1,13 @@ import { useEffect } from 'react' import { useAppDispatch } from 'redux/hooks' -import { setError } from 'redux/reducers' +import { setDirty, setError } from 'redux/reducers' export default function Logout() { // This component will redirect the user to the Keycloak logout page const dispatch = useAppDispatch() useEffect(() => { dispatch(setError(undefined)) + dispatch(setDirty(false)) window.location.href = '/logout-otomi' }, []) return null diff --git a/src/redux/store.tsx b/src/redux/store.tsx index e1663000a..5cd660994 100644 --- a/src/redux/store.tsx +++ b/src/redux/store.tsx @@ -21,7 +21,10 @@ const interceptMiddleware: Middleware = (api: MiddlewareAPI) => (next) => (actio if (requestStatus === 'fulfilled') dispatch(setDirty(false)) } // exclude endpoints from dirty state - if (type === 'mutation' && ['workloadCatalog', 'createObjWizard'].includes(endpointName as string)) + if ( + type === 'mutation' && + ['workloadCatalog', 'createObjWizard', 'deleteCloudtty'].includes(endpointName as string) + ) dispatch(setDirty(false)) } } else if (payload) { From 705568185181dcafa568cc1b899ec29a619ca572 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:20:43 +0100 Subject: [PATCH 07/34] test: logout --- src/components/AccountPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AccountPopover.tsx b/src/components/AccountPopover.tsx index c0757ba5a..664b87db8 100644 --- a/src/components/AccountPopover.tsx +++ b/src/components/AccountPopover.tsx @@ -41,7 +41,7 @@ export default function AccountPopover({ email }: Props) { // cloudtty resources will be automatically deleted by the API after a 2-hour timeout } clearLocalStorage('oboTeamId') - history.push('/logout') + window.location.href = '/logout-otomi' } return ( From e8aa02c5898d4d731c4f9b213519c8e703b076c0 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:37:22 +0100 Subject: [PATCH 08/34] test: logout --- src/components/AccountPopover.tsx | 2 +- src/providers/Session.tsx | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/AccountPopover.tsx b/src/components/AccountPopover.tsx index 664b87db8..c0757ba5a 100644 --- a/src/components/AccountPopover.tsx +++ b/src/components/AccountPopover.tsx @@ -41,7 +41,7 @@ export default function AccountPopover({ email }: Props) { // cloudtty resources will be automatically deleted by the API after a 2-hour timeout } clearLocalStorage('oboTeamId') - window.location.href = '/logout-otomi' + history.push('/logout') } return ( diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 368f810bb..f2a9e403f 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -8,6 +8,7 @@ import { useLocalStorage } from 'hooks/useLocalStorage' import { ProviderContext, SnackbarKey } from 'notistack' import React, { useContext, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useLocation } from 'react-router-dom' import { useAppSelector } from 'redux/hooks' import { GetSessionApiResponse, @@ -90,6 +91,8 @@ type DroneBuildEvent = { } export default function SessionProvider({ children }: Props): React.ReactElement { + const { pathname } = useLocation() + console.log('pathname', pathname) const [oboTeamId, setOboTeamId] = useLocalStorage('oboTeamId', undefined) const { data: session, isLoading: isLoadingSession, refetch: refetchSession } = useGetSessionQuery() const url = `${window.location.origin.replace(/^http/, 'ws')}` @@ -278,6 +281,7 @@ export default function SessionProvider({ children }: Props): React.ReactElement if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) // no error and we stopped loading, so we can check the user + if (pathname === '/logout') window.location.href = '/logout-otomi' if (!session) throw new ApiErrorGatewayTimeout() if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return From faa7593155c0b14a2dbc64f42ea72a6738701988 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:41:04 +0100 Subject: [PATCH 09/34] fix: redirect logout page --- src/pages/Logout.tsx | 5 ----- src/providers/Session.tsx | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index 1bf3cc1c2..46f753495 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -1,13 +1,8 @@ import { useEffect } from 'react' -import { useAppDispatch } from 'redux/hooks' -import { setDirty, setError } from 'redux/reducers' export default function Logout() { // This component will redirect the user to the Keycloak logout page - const dispatch = useAppDispatch() useEffect(() => { - dispatch(setError(undefined)) - dispatch(setDirty(false)) window.location.href = '/logout-otomi' }, []) return null diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index f2a9e403f..07b7b9f97 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -92,7 +92,6 @@ type DroneBuildEvent = { export default function SessionProvider({ children }: Props): React.ReactElement { const { pathname } = useLocation() - console.log('pathname', pathname) const [oboTeamId, setOboTeamId] = useLocalStorage('oboTeamId', undefined) const { data: session, isLoading: isLoadingSession, refetch: refetchSession } = useGetSessionQuery() const url = `${window.location.origin.replace(/^http/, 'ws')}` @@ -277,11 +276,13 @@ export default function SessionProvider({ children }: Props): React.ReactElement // END HOOKS if (isLoadingSession) return + // redirect to the Keyclok logout page if the user tries to access the logout route + // prevents throwing an error and showing the error component + if (pathname === '/logout') window.location.href = '/logout-otomi' // if an error occured we keep rendering and let the error component show what happened if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) // no error and we stopped loading, so we can check the user - if (pathname === '/logout') window.location.href = '/logout-otomi' if (!session) throw new ApiErrorGatewayTimeout() if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return From 7b8b1b56839ae2f42ed8891cc330a03113dfd8e4 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:23:51 +0100 Subject: [PATCH 10/34] fix: logout component --- src/pages/Logout.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index 46f753495..8999a720b 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -1,8 +1,13 @@ import { useEffect } from 'react' +import { useAppDispatch } from 'redux/hooks' +import { setDirty, setError } from 'redux/reducers' export default function Logout() { // This component will redirect the user to the Keycloak logout page + const dispatch = useAppDispatch() useEffect(() => { + dispatch(setDirty(false)) + dispatch(setError(undefined)) window.location.href = '/logout-otomi' }, []) return null From a7946cff26b2aba9be52eff7e043b00f49f5c638 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 22:23:55 +0100 Subject: [PATCH 11/34] test: logout session --- src/providers/Session.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 07b7b9f97..dd17bdca2 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -278,7 +278,10 @@ export default function SessionProvider({ children }: Props): React.ReactElement if (isLoadingSession) return // redirect to the Keyclok logout page if the user tries to access the logout route // prevents throwing an error and showing the error component - if (pathname === '/logout') window.location.href = '/logout-otomi' + if (pathname === '/logout') { + window.location.href = '/logout-otomi' + return null + } // if an error occured we keep rendering and let the error component show what happened if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) From d3c54bf7f6b983d46150798e184d38e911d60613 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 22:50:50 +0100 Subject: [PATCH 12/34] test: logout --- src/components/Error.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index cda7648ee..4cfda8fe9 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -20,7 +20,7 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - if (err?.status === 'FETCH_ERROR') window.location.href = '/' + if (err?.status === 'FETCH_ERROR') window.location.reload() const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error From 2a43bd6c01e5ce7d6ba914cc6b80327f16ffcbc7 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:18:38 +0100 Subject: [PATCH 13/34] test: logout --- src/components/Error.tsx | 6 +++++- src/providers/Session.tsx | 18 +++++++++++++++--- src/utils/error.tsx | 6 ++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index 4cfda8fe9..d18692114 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -20,7 +20,11 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - if (err?.status === 'FETCH_ERROR') window.location.reload() + if (err?.status === 'FETCH_ERROR') { + window.location.href = '/' + dispatch(setError(undefined)) + return null + } const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index dd17bdca2..5251d7795 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -19,7 +19,12 @@ import { useGetSettingsInfoQuery, } from 'redux/otomiApi' import { useSocket, useSocketEvent } from 'socket.io-react-hook' -import { ApiErrorGatewayTimeout, ApiErrorUnauthorized, ApiErrorUnauthorizedNoGroups } from 'utils/error' +import { + ApiErrorGatewayTimeout, + ApiErrorServiceUnavailable, + ApiErrorUnauthorized, + ApiErrorUnauthorizedNoGroups, +} from 'utils/error' import snack from 'utils/snack' export interface SessionContext extends GetSessionApiResponse { @@ -93,7 +98,12 @@ type DroneBuildEvent = { export default function SessionProvider({ children }: Props): React.ReactElement { const { pathname } = useLocation() const [oboTeamId, setOboTeamId] = useLocalStorage('oboTeamId', undefined) - const { data: session, isLoading: isLoadingSession, refetch: refetchSession } = useGetSessionQuery() + const { + data: session, + isLoading: isLoadingSession, + refetch: refetchSession, + error: sessionError, + } = useGetSessionQuery() const url = `${window.location.origin.replace(/^http/, 'ws')}` const path = '/api/ws' const { data: settings, isLoading: isLoadingSettings, refetch: refetchSettings } = useGetSettingsInfoQuery() @@ -286,7 +296,9 @@ export default function SessionProvider({ children }: Props): React.ReactElement if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) // no error and we stopped loading, so we can check the user - if (!session) throw new ApiErrorGatewayTimeout() + const { originalStatus } = sessionError as any + if (originalStatus === 503) throw new ApiErrorServiceUnavailable() + if (originalStatus === 504) throw new ApiErrorGatewayTimeout() if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return if (apiDocs) setSpec(apiDocs) diff --git a/src/utils/error.tsx b/src/utils/error.tsx index 98701e7d1..86a58d938 100644 --- a/src/utils/error.tsx +++ b/src/utils/error.tsx @@ -28,6 +28,11 @@ class HttpErrorForbidden extends HttpError { } } +class ApiErrorServiceUnavailable extends HttpError { + constructor() { + super(e['The API could not be reached.'], 503) + } +} class ApiErrorGatewayTimeout extends HttpError { constructor() { super(e['The API could not be reached.'], 504) @@ -56,6 +61,7 @@ export { HttpError, HttpErrorBadRequest, HttpErrorForbidden, + ApiErrorServiceUnavailable, ApiErrorGatewayTimeout, ApiErrorUnauthorized, ApiErrorUnauthorizedNoGroups, From cab8a1f872dfb77d2518b8a0bce97b472be1ca41 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:21:31 +0100 Subject: [PATCH 14/34] fix: session error --- src/providers/Session.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 5251d7795..e2c3e1c0e 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -296,9 +296,11 @@ export default function SessionProvider({ children }: Props): React.ReactElement if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) // no error and we stopped loading, so we can check the user - const { originalStatus } = sessionError as any - if (originalStatus === 503) throw new ApiErrorServiceUnavailable() - if (originalStatus === 504) throw new ApiErrorGatewayTimeout() + if (sessionError) { + const { originalStatus } = sessionError as any + if (originalStatus === 503) throw new ApiErrorServiceUnavailable() + if (originalStatus === 504) throw new ApiErrorGatewayTimeout() + } if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return if (apiDocs) setSpec(apiDocs) From 069feb11ace715fb6d346a8b4130713133e81df6 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:48:11 +0100 Subject: [PATCH 15/34] test: fetch error --- src/components/Error.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index d18692114..c7c8c47d5 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -20,11 +20,11 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - if (err?.status === 'FETCH_ERROR') { - window.location.href = '/' - dispatch(setError(undefined)) - return null - } + // if (err?.status === 'FETCH_ERROR') { + // window.location.href = '/' + // dispatch(setError(undefined)) + // return null + // } const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error From 8b66483943f3e9721a6482187025f397f64a3b0c Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:49:16 +0100 Subject: [PATCH 16/34] test: error comp --- src/components/Error.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index c7c8c47d5..f5db1b2b3 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -40,6 +40,7 @@ export default function ({ error }: Props): React.ReactElement { case 403: icon = 'ic:baseline-do-not-disturb' break + case 503: case 504: icon = 'ant-design:api-outlined' break @@ -54,7 +55,12 @@ export default function ({ error }: Props): React.ReactElement { {text} ) - if (code === 504 || err instanceof ApiErrorUnauthorized || err instanceof ApiErrorUnauthorizedNoGroups) { + if ( + code === 503 || + code === 504 || + err instanceof ApiErrorUnauthorized || + err instanceof ApiErrorUnauthorizedNoGroups + ) { return renderButton(t('Logout', { ns: 'error' }) as string, () => { window.location.href = '/logout-otomi' }) From f53437b1881bbd8e2cf49906edf08ea00f41a4d7 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:06:24 +0100 Subject: [PATCH 17/34] test: logout --- src/components/AccountPopover.tsx | 17 +++++++---------- src/components/CodeEditor.tsx | 2 +- src/components/Error.tsx | 10 +++++----- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/components/AccountPopover.tsx b/src/components/AccountPopover.tsx index c0757ba5a..3cad85ebd 100644 --- a/src/components/AccountPopover.tsx +++ b/src/components/AccountPopover.tsx @@ -32,16 +32,13 @@ export default function AccountPopover({ email }: Props) { setOpen(null) } - const handleLogout = async () => { - try { - await del({ - body: { teamId: oboTeamId, domain, emailNoSymbols, isAdmin: user.isPlatformAdmin, userTeams }, - }) - } catch (error) { - // cloudtty resources will be automatically deleted by the API after a 2-hour timeout - } - clearLocalStorage('oboTeamId') - history.push('/logout') + const handleLogout = () => { + del({ + body: { teamId: oboTeamId, domain, emailNoSymbols, isAdmin: user.isPlatformAdmin, userTeams }, + }).finally(() => { + clearLocalStorage('oboTeamId') + history.push('/logout') + }) } return ( diff --git a/src/components/CodeEditor.tsx b/src/components/CodeEditor.tsx index 9163ea8fd..28a3e0eec 100644 --- a/src/components/CodeEditor.tsx +++ b/src/components/CodeEditor.tsx @@ -120,7 +120,7 @@ export default function CodeEditor({ useEffect(() => { const obj = fromYaml(modifiedCode) - setWorkloadValues(obj) + setWorkloadValues?.(obj) }, [modifiedCode]) return ( diff --git a/src/components/Error.tsx b/src/components/Error.tsx index f5db1b2b3..12c6746ac 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -20,11 +20,11 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - // if (err?.status === 'FETCH_ERROR') { - // window.location.href = '/' - // dispatch(setError(undefined)) - // return null - // } + if (err?.status === 'FETCH_ERROR') { + window.location.href = '/' + dispatch(setError(undefined)) + return null + } const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error From fe5b65a647b451bd37324c42d325023c03914ba9 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:56:41 +0100 Subject: [PATCH 18/34] test: logout --- src/components/Error.tsx | 6 +----- src/providers/Session.tsx | 6 ------ src/redux/store.tsx | 5 +---- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index 12c6746ac..87a9f037f 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -20,11 +20,7 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - if (err?.status === 'FETCH_ERROR') { - window.location.href = '/' - dispatch(setError(undefined)) - return null - } + if (err?.status === 'FETCH_ERROR') window.location.href = '/' const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index e2c3e1c0e..2c299eb2c 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -286,12 +286,6 @@ export default function SessionProvider({ children }: Props): React.ReactElement // END HOOKS if (isLoadingSession) return - // redirect to the Keyclok logout page if the user tries to access the logout route - // prevents throwing an error and showing the error component - if (pathname === '/logout') { - window.location.href = '/logout-otomi' - return null - } // if an error occured we keep rendering and let the error component show what happened if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) diff --git a/src/redux/store.tsx b/src/redux/store.tsx index 5cd660994..e1663000a 100644 --- a/src/redux/store.tsx +++ b/src/redux/store.tsx @@ -21,10 +21,7 @@ const interceptMiddleware: Middleware = (api: MiddlewareAPI) => (next) => (actio if (requestStatus === 'fulfilled') dispatch(setDirty(false)) } // exclude endpoints from dirty state - if ( - type === 'mutation' && - ['workloadCatalog', 'createObjWizard', 'deleteCloudtty'].includes(endpointName as string) - ) + if (type === 'mutation' && ['workloadCatalog', 'createObjWizard'].includes(endpointName as string)) dispatch(setDirty(false)) } } else if (payload) { From 02c58a66867b4a9927a846b8bd31cc566dba11b8 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:17:25 +0100 Subject: [PATCH 19/34] test: logout --- src/components/AccountPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AccountPopover.tsx b/src/components/AccountPopover.tsx index 3cad85ebd..725363b17 100644 --- a/src/components/AccountPopover.tsx +++ b/src/components/AccountPopover.tsx @@ -37,7 +37,7 @@ export default function AccountPopover({ email }: Props) { body: { teamId: oboTeamId, domain, emailNoSymbols, isAdmin: user.isPlatformAdmin, userTeams }, }).finally(() => { clearLocalStorage('oboTeamId') - history.push('/logout') + window.location.href = '/logout-otomi' }) } From 4497522d154f014fe44469ab14e03dd71202f5c3 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:13:31 +0100 Subject: [PATCH 20/34] test: logout --- src/components/AccountPopover.tsx | 2 +- src/providers/Session.tsx | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/AccountPopover.tsx b/src/components/AccountPopover.tsx index 725363b17..3cad85ebd 100644 --- a/src/components/AccountPopover.tsx +++ b/src/components/AccountPopover.tsx @@ -37,7 +37,7 @@ export default function AccountPopover({ email }: Props) { body: { teamId: oboTeamId, domain, emailNoSymbols, isAdmin: user.isPlatformAdmin, userTeams }, }).finally(() => { clearLocalStorage('oboTeamId') - window.location.href = '/logout-otomi' + history.push('/logout') }) } diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 2c299eb2c..c6f1b4a1a 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -1,3 +1,4 @@ +import { skipToken } from '@reduxjs/toolkit/dist/query' import { setSpec } from 'common/api-spec' import LinkCommit from 'components/LinkCommit' import LoadingScreen from 'components/LoadingScreen' @@ -97,22 +98,27 @@ type DroneBuildEvent = { export default function SessionProvider({ children }: Props): React.ReactElement { const { pathname } = useLocation() + const skipFetch = pathname === '/logout' || pathname === '/logout-otomi' const [oboTeamId, setOboTeamId] = useLocalStorage('oboTeamId', undefined) const { data: session, isLoading: isLoadingSession, refetch: refetchSession, error: sessionError, - } = useGetSessionQuery() + } = useGetSessionQuery(skipFetch && skipToken) const url = `${window.location.origin.replace(/^http/, 'ws')}` const path = '/api/ws' - const { data: settings, isLoading: isLoadingSettings, refetch: refetchSettings } = useGetSettingsInfoQuery() + const { + data: settings, + isLoading: isLoadingSettings, + refetch: refetchSettings, + } = useGetSettingsInfoQuery(skipFetch && skipToken) const { data: apps, isLoading: isLoadingApps, refetch: refetchAppsEnabled, } = useGetAppsQuery({ teamId: oboTeamId, picks: ['id', 'enabled'] }, { skip: !oboTeamId }) - const { data: apiDocs, isLoading: isLoadingApiDocs, error: errorApiDocs } = useApiDocsQuery() + const { data: apiDocs, isLoading: isLoadingApiDocs, error: errorApiDocs } = useApiDocsQuery(skipFetch && skipToken) const { socket, error: errorSocket } = useSocket({ url, path }) // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const { lastMessage: lastDbMessage } = useSocketEvent(socket, 'db') @@ -286,6 +292,11 @@ export default function SessionProvider({ children }: Props): React.ReactElement // END HOOKS if (isLoadingSession) return + // redirect to the Keyclok logout page if the user tries to access the logout route + // prevents throwing an error and showing the error component + if (pathname === '/logout') window.location.href = '/logout-otomi' + if (pathname === '/logout-otomi') return null + // if an error occured we keep rendering and let the error component show what happened if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) From 8f7a3bc65c71299c0a5e533c16fd2e14e6fa7ba2 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:16:51 +0100 Subject: [PATCH 21/34] test: logout --- src/components/CodeEditor.tsx | 2 +- src/components/Error.tsx | 4 +++- src/providers/Session.tsx | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/CodeEditor.tsx b/src/components/CodeEditor.tsx index 28a3e0eec..9163ea8fd 100644 --- a/src/components/CodeEditor.tsx +++ b/src/components/CodeEditor.tsx @@ -120,7 +120,7 @@ export default function CodeEditor({ useEffect(() => { const obj = fromYaml(modifiedCode) - setWorkloadValues?.(obj) + setWorkloadValues(obj) }, [modifiedCode]) return ( diff --git a/src/components/Error.tsx b/src/components/Error.tsx index 87a9f037f..aa196b9e0 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -12,15 +12,17 @@ interface Props { } export default function ({ error }: Props): React.ReactElement { + console.log('error comp', error) const dispatch = useAppDispatch() const globalError = useAppSelector(({ global: { error } }) => error) + console.log('globalError', globalError) const { t } = useTranslation('error') // END HOOKS const err = error ?? globalError if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - if (err?.status === 'FETCH_ERROR') window.location.href = '/' + // if (err?.status === 'FETCH_ERROR') window.location.href = '/' const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index c6f1b4a1a..e55c3bd22 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -290,6 +290,7 @@ export default function SessionProvider({ children }: Props): React.ReactElement }) }, [lastTektonMessage]) + console.log('sessionError', sessionError) // END HOOKS if (isLoadingSession) return // redirect to the Keyclok logout page if the user tries to access the logout route From a4a8ee274b37f04c3e3aae7a6de24d5e33cd583e Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:05:01 +0100 Subject: [PATCH 22/34] test: logout --- src/providers/Session.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index e55c3bd22..4c0230ebe 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -27,6 +27,7 @@ import { ApiErrorUnauthorizedNoGroups, } from 'utils/error' import snack from 'utils/snack' +import Cookies from 'js-cookie' export interface SessionContext extends GetSessionApiResponse { appsEnabled?: Record @@ -291,6 +292,8 @@ export default function SessionProvider({ children }: Props): React.ReactElement }, [lastTektonMessage]) console.log('sessionError', sessionError) + const cookies = Cookies.get() + console.log('cookies', cookies) // END HOOKS if (isLoadingSession) return // redirect to the Keyclok logout page if the user tries to access the logout route From 9e9d95bc7d9f5c2fdb1ebd28749d505c2a0b38c2 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:18:18 +0100 Subject: [PATCH 23/34] test: socket --- src/providers/Session.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 4c0230ebe..8f6122602 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -121,6 +121,8 @@ export default function SessionProvider({ children }: Props): React.ReactElement } = useGetAppsQuery({ teamId: oboTeamId, picks: ['id', 'enabled'] }, { skip: !oboTeamId }) const { data: apiDocs, isLoading: isLoadingApiDocs, error: errorApiDocs } = useApiDocsQuery(skipFetch && skipToken) const { socket, error: errorSocket } = useSocket({ url, path }) + console.log('socket', socket) + console.log('errorSocket', errorSocket) // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const { lastMessage: lastDbMessage } = useSocketEvent(socket, 'db') // eslint-disable-next-line @typescript-eslint/no-unsafe-argument From 059704894faed6955160f3f502771a2caa0c9786 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:35:05 +0100 Subject: [PATCH 24/34] test: logout --- src/providers/Session.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 8f6122602..67c79b95e 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -123,6 +123,8 @@ export default function SessionProvider({ children }: Props): React.ReactElement const { socket, error: errorSocket } = useSocket({ url, path }) console.log('socket', socket) console.log('errorSocket', errorSocket) + + if (socket.connected === false && sessionError) window.location.reload() // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const { lastMessage: lastDbMessage } = useSocketEvent(socket, 'db') // eslint-disable-next-line @typescript-eslint/no-unsafe-argument From 66af7c75c7e2b88d4883e038d31f296ed5c45874 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:52:17 +0100 Subject: [PATCH 25/34] test: logout --- src/components/Error.tsx | 2 -- src/providers/Session.tsx | 11 ++++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index aa196b9e0..fee1d844c 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -12,10 +12,8 @@ interface Props { } export default function ({ error }: Props): React.ReactElement { - console.log('error comp', error) const dispatch = useAppDispatch() const globalError = useAppSelector(({ global: { error } }) => error) - console.log('globalError', globalError) const { t } = useTranslation('error') // END HOOKS const err = error ?? globalError diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 67c79b95e..843d1265a 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -27,7 +27,6 @@ import { ApiErrorUnauthorizedNoGroups, } from 'utils/error' import snack from 'utils/snack' -import Cookies from 'js-cookie' export interface SessionContext extends GetSessionApiResponse { appsEnabled?: Record @@ -122,9 +121,8 @@ export default function SessionProvider({ children }: Props): React.ReactElement const { data: apiDocs, isLoading: isLoadingApiDocs, error: errorApiDocs } = useApiDocsQuery(skipFetch && skipToken) const { socket, error: errorSocket } = useSocket({ url, path }) console.log('socket', socket) - console.log('errorSocket', errorSocket) - if (socket.connected === false && sessionError) window.location.reload() + // if (socket.connected === false && sessionError) window.location.reload() // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const { lastMessage: lastDbMessage } = useSocketEvent(socket, 'db') // eslint-disable-next-line @typescript-eslint/no-unsafe-argument @@ -295,9 +293,6 @@ export default function SessionProvider({ children }: Props): React.ReactElement }) }, [lastTektonMessage]) - console.log('sessionError', sessionError) - const cookies = Cookies.get() - console.log('cookies', cookies) // END HOOKS if (isLoadingSession) return // redirect to the Keyclok logout page if the user tries to access the logout route @@ -310,9 +305,11 @@ export default function SessionProvider({ children }: Props): React.ReactElement keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) // no error and we stopped loading, so we can check the user if (sessionError) { - const { originalStatus } = sessionError as any + console.log('sessionError', sessionError) + const { originalStatus, status } = sessionError as any if (originalStatus === 503) throw new ApiErrorServiceUnavailable() if (originalStatus === 504) throw new ApiErrorGatewayTimeout() + if (status === 'FETCH_ERROR') window.location.href = '/' } if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return From 8bd4522b904f7072bdc17d2f77ec08a02d28a57a Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:04:07 +0100 Subject: [PATCH 26/34] test: logout --- src/providers/Session.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 843d1265a..0fc1f7c6a 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -309,7 +309,7 @@ export default function SessionProvider({ children }: Props): React.ReactElement const { originalStatus, status } = sessionError as any if (originalStatus === 503) throw new ApiErrorServiceUnavailable() if (originalStatus === 504) throw new ApiErrorGatewayTimeout() - if (status === 'FETCH_ERROR') window.location.href = '/' + if (status === 'FETCH_ERROR') return } if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return From b0888365ac2ffd5327054d69abea0541fa84b4c6 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:36:07 +0100 Subject: [PATCH 27/34] test: logout --- src/pages/Logout.tsx | 19 ++++++++++++++----- src/providers/Session.tsx | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index 8999a720b..b087a8247 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -1,14 +1,23 @@ -import { useEffect } from 'react' +import LoadingScreen from 'components/LoadingScreen' +import React, { useEffect } from 'react' import { useAppDispatch } from 'redux/hooks' import { setDirty, setError } from 'redux/reducers' -export default function Logout() { +interface Props { + waitAndLogout?: boolean +} + +export default function Logout({ waitAndLogout = false }: Props): React.ReactElement { // This component will redirect the user to the Keycloak logout page const dispatch = useAppDispatch() useEffect(() => { dispatch(setDirty(false)) dispatch(setError(undefined)) - window.location.href = '/logout-otomi' - }, []) - return null + if (waitAndLogout) { + setTimeout(() => { + window.location.href = '/logout-otomi' + }, 1000) + } else window.location.href = '/logout-otomi' + }, [waitAndLogout]) + return } diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 0fc1f7c6a..92a6b8fc6 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -7,6 +7,7 @@ import MessageTekton from 'components/MessageTekton' import MessageTrans from 'components/MessageTrans' import { useLocalStorage } from 'hooks/useLocalStorage' import { ProviderContext, SnackbarKey } from 'notistack' +import Logout from 'pages/Logout' import React, { useContext, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useLocation } from 'react-router-dom' @@ -309,7 +310,7 @@ export default function SessionProvider({ children }: Props): React.ReactElement const { originalStatus, status } = sessionError as any if (originalStatus === 503) throw new ApiErrorServiceUnavailable() if (originalStatus === 504) throw new ApiErrorGatewayTimeout() - if (status === 'FETCH_ERROR') return + if (status === 'FETCH_ERROR') return } if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return From 3d760752f29abe9136d07b9f3fec95d9ad3a07c2 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:24:55 +0100 Subject: [PATCH 28/34] test: logout --- src/pages/Logout.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index b087a8247..e6b1d2b59 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -15,9 +15,14 @@ export default function Logout({ waitAndLogout = false }: Props): React.ReactEle dispatch(setError(undefined)) if (waitAndLogout) { setTimeout(() => { - window.location.href = '/logout-otomi' + window.location.reload() }, 1000) } else window.location.href = '/logout-otomi' + return () => { + dispatch(setDirty(false)) + dispatch(setError(undefined)) + window.location.reload() + } }, [waitAndLogout]) return } From 0effc0fda812dcc414dfad4974c8c9ab3aa6d6dd Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:08:53 +0100 Subject: [PATCH 29/34] test: logout --- src/components/Error.tsx | 3 ++- src/pages/Logout.tsx | 10 +++++----- src/providers/Session.tsx | 6 +----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index fee1d844c..70d52dbe8 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -4,6 +4,7 @@ import Helmet from 'react-helmet' import { useTranslation } from 'react-i18next' import { useAppDispatch, useAppSelector } from 'redux/hooks' import { setError } from 'redux/reducers' +import Logout from 'pages/Logout' import { ApiErrorUnauthorized, ApiErrorUnauthorizedNoGroups, HttpError } from '../utils/error' import Iconify from './Iconify' @@ -20,7 +21,7 @@ export default function ({ error }: Props): React.ReactElement { if (!err) return null // redirect to login page if the error is a fetch error (session expired) // automatically triggers Keycloak to route the user to the Keycloak login page - // if (err?.status === 'FETCH_ERROR') window.location.href = '/' + if (err?.status === 'FETCH_ERROR') return const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error const errorCode = code || originalStatus || status || message || data?.error diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index e6b1d2b59..c77abebdf 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -4,25 +4,25 @@ import { useAppDispatch } from 'redux/hooks' import { setDirty, setError } from 'redux/reducers' interface Props { - waitAndLogout?: boolean + fetchError?: boolean } -export default function Logout({ waitAndLogout = false }: Props): React.ReactElement { +export default function Logout({ fetchError = false }: Props): React.ReactElement { // This component will redirect the user to the Keycloak logout page const dispatch = useAppDispatch() useEffect(() => { dispatch(setDirty(false)) dispatch(setError(undefined)) - if (waitAndLogout) { + if (fetchError) { setTimeout(() => { window.location.reload() - }, 1000) + }, 100) } else window.location.href = '/logout-otomi' return () => { dispatch(setDirty(false)) dispatch(setError(undefined)) window.location.reload() } - }, [waitAndLogout]) + }, [fetchError]) return } diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 92a6b8fc6..784f3dede 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -121,9 +121,6 @@ export default function SessionProvider({ children }: Props): React.ReactElement } = useGetAppsQuery({ teamId: oboTeamId, picks: ['id', 'enabled'] }, { skip: !oboTeamId }) const { data: apiDocs, isLoading: isLoadingApiDocs, error: errorApiDocs } = useApiDocsQuery(skipFetch && skipToken) const { socket, error: errorSocket } = useSocket({ url, path }) - console.log('socket', socket) - - // if (socket.connected === false && sessionError) window.location.reload() // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const { lastMessage: lastDbMessage } = useSocketEvent(socket, 'db') // eslint-disable-next-line @typescript-eslint/no-unsafe-argument @@ -306,11 +303,10 @@ export default function SessionProvider({ children }: Props): React.ReactElement keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) // no error and we stopped loading, so we can check the user if (sessionError) { - console.log('sessionError', sessionError) const { originalStatus, status } = sessionError as any if (originalStatus === 503) throw new ApiErrorServiceUnavailable() if (originalStatus === 504) throw new ApiErrorGatewayTimeout() - if (status === 'FETCH_ERROR') return + if (status === 'FETCH_ERROR') return } if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() if (isLoadingApiDocs || isLoadingApps || isLoadingSession || isLoadingSettings) return From 5a1fe6ff9d27e90c0e397dcce469ab76d2a67c68 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:41:23 +0100 Subject: [PATCH 30/34] test: logout --- src/components/Error.tsx | 3 +-- src/pages/Logout.tsx | 5 ++--- src/providers/Session.tsx | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index 70d52dbe8..d21ea58d9 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -19,8 +19,7 @@ export default function ({ error }: Props): React.ReactElement { // END HOOKS const err = error ?? globalError if (!err) return null - // redirect to login page if the error is a fetch error (session expired) - // automatically triggers Keycloak to route the user to the Keycloak login page + // redirect to logout page if the error is a fetch error (session expired) if (err?.status === 'FETCH_ERROR') return const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index c77abebdf..4b3b52b26 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -14,9 +14,8 @@ export default function Logout({ fetchError = false }: Props): React.ReactElemen dispatch(setDirty(false)) dispatch(setError(undefined)) if (fetchError) { - setTimeout(() => { - window.location.reload() - }, 100) + // automatically triggers Keycloak to route the user to the Keycloak login page + window.location.reload() } else window.location.href = '/logout-otomi' return () => { dispatch(setDirty(false)) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 784f3dede..c44bebaba 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -306,6 +306,7 @@ export default function SessionProvider({ children }: Props): React.ReactElement const { originalStatus, status } = sessionError as any if (originalStatus === 503) throw new ApiErrorServiceUnavailable() if (originalStatus === 504) throw new ApiErrorGatewayTimeout() + // redirect to logout page if the error is a fetch error (session expired) if (status === 'FETCH_ERROR') return } if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() From c720cb9f818bc7cca8e33e5d62a4989a81679032 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:03:12 +0100 Subject: [PATCH 31/34] test: logout --- src/pages/Logout.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index 4b3b52b26..691acf943 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -1,7 +1,5 @@ import LoadingScreen from 'components/LoadingScreen' import React, { useEffect } from 'react' -import { useAppDispatch } from 'redux/hooks' -import { setDirty, setError } from 'redux/reducers' interface Props { fetchError?: boolean @@ -9,17 +7,12 @@ interface Props { export default function Logout({ fetchError = false }: Props): React.ReactElement { // This component will redirect the user to the Keycloak logout page - const dispatch = useAppDispatch() useEffect(() => { - dispatch(setDirty(false)) - dispatch(setError(undefined)) if (fetchError) { // automatically triggers Keycloak to route the user to the Keycloak login page window.location.reload() } else window.location.href = '/logout-otomi' return () => { - dispatch(setDirty(false)) - dispatch(setError(undefined)) window.location.reload() } }, [fetchError]) From 3595ce138746546535ce42b23a35fbf987d40a5e Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:16:03 +0100 Subject: [PATCH 32/34] test: logout --- src/providers/Session.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index c44bebaba..461937d90 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -293,11 +293,6 @@ export default function SessionProvider({ children }: Props): React.ReactElement // END HOOKS if (isLoadingSession) return - // redirect to the Keyclok logout page if the user tries to access the logout route - // prevents throwing an error and showing the error component - if (pathname === '/logout') window.location.href = '/logout-otomi' - if (pathname === '/logout-otomi') return null - // if an error occured we keep rendering and let the error component show what happened if (errorSocket) keys.socket = snack.warning(`${t('Could not establish socket connection. Retrying...')}`, { key: keys.socket }) From eaa7061ce93ff89cd66ba61b9f5ca46645888b8c Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:32:10 +0100 Subject: [PATCH 33/34] fix: logout bug --- src/components/Error.tsx | 2 +- src/providers/Session.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Error.tsx b/src/components/Error.tsx index d21ea58d9..a4a63c821 100644 --- a/src/components/Error.tsx +++ b/src/components/Error.tsx @@ -19,7 +19,7 @@ export default function ({ error }: Props): React.ReactElement { // END HOOKS const err = error ?? globalError if (!err) return null - // redirect to logout page if the error is a fetch error (session expired) + // return the logout page if the error is a fetch error (session expired) if (err?.status === 'FETCH_ERROR') return const { title, message, data, code, originalStatus, status } = err || {} const errorMessage = title ? `${title}: ${message}` : message || data?.error diff --git a/src/providers/Session.tsx b/src/providers/Session.tsx index 461937d90..b90c43d36 100644 --- a/src/providers/Session.tsx +++ b/src/providers/Session.tsx @@ -301,7 +301,7 @@ export default function SessionProvider({ children }: Props): React.ReactElement const { originalStatus, status } = sessionError as any if (originalStatus === 503) throw new ApiErrorServiceUnavailable() if (originalStatus === 504) throw new ApiErrorGatewayTimeout() - // redirect to logout page if the error is a fetch error (session expired) + // return the logout page if the error is a fetch error (session expired) if (status === 'FETCH_ERROR') return } if (!session.user.isPlatformAdmin && session.user.teams.length === 0) throw new ApiErrorUnauthorizedNoGroups() From 509bd95193b3d69e76e1c9176c77ee1175dbd048 Mon Sep 17 00:00:00 2001 From: Ferruh Cihan <63190600+ferruhcihan@users.noreply.github.com> Date: Fri, 15 Nov 2024 08:28:55 +0100 Subject: [PATCH 34/34] feat: improve logout page comments --- src/pages/Logout.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pages/Logout.tsx b/src/pages/Logout.tsx index 691acf943..868e77ab9 100644 --- a/src/pages/Logout.tsx +++ b/src/pages/Logout.tsx @@ -6,12 +6,13 @@ interface Props { } export default function Logout({ fetchError = false }: Props): React.ReactElement { - // This component will redirect the user to the Keycloak logout page + // This component manages the logout process for users authenticated with Keycloak. + // - If a fetch error occurs, the page reloads automatically to handle potential session issues. + // - If no fetch error occurs, the user is redirected to the Keycloak logout page ('/logout-otomi' route). + // - On component unmount, the page reloads to ensure a clean and consistent state. useEffect(() => { - if (fetchError) { - // automatically triggers Keycloak to route the user to the Keycloak login page - window.location.reload() - } else window.location.href = '/logout-otomi' + if (fetchError) window.location.reload() + else window.location.href = '/logout-otomi' return () => { window.location.reload() }