diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3a2ca4d..8c1713a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -7,7 +7,6 @@ import MainContextProvider from "./contexts/mainContext"; import ProjectSheet from "./pages/projectSheet"; import DeploymentSheet from "./pages/deploymentSheet"; import DeviceMenuPage from "./pages/deviceMenu"; -import DeviceSheet from "./components/deviceSheet/deviceSheetMain"; import DeviceSheetPage from "./pages/deviceSheet"; import { theme } from "./theme"; import { LinearProgress, ThemeProvider } from "@mui/material"; @@ -18,9 +17,9 @@ import SiteMenuPage from "./pages/siteMenu"; import SiteSheetPage from "./pages/siteSheet"; import SnackContextProvider from "./contexts/snackContext"; import { AuthContext } from "./contexts/AuthContextProvider"; -import { useContext } from "react"; +import { useContext} from "react"; import FilesContextProvider from "./contexts/filesContext"; -; +import RouteWrapper from "./components/RouteWrapper"; // Env var processed by nginx OpenAPI.BASE = window._env_.REACT_APP_API_PATH || "/api/v1"; @@ -39,43 +38,103 @@ function App() { - }> - {/* }> */} + +
+ + } + /> + {/* } /> */} } - > - }> - }> + element={ + + + + } + /> + + + + } + /> + + + + } + /> } - > + element={ + + + + } + /> } - > + element={ + + + + } + /> } - > + element={ + + + + } + /> } - > + element={ + + + + } + /> } - > + element={ + + + + } + /> } - > + element={ + + + + } + /> } - > - }> + element={ + + + + } + /> + +
+ + } + /> diff --git a/frontend/src/components/RouteWrapper.tsx b/frontend/src/components/RouteWrapper.tsx new file mode 100644 index 0000000..43d821e --- /dev/null +++ b/frontend/src/components/RouteWrapper.tsx @@ -0,0 +1,16 @@ +import { useRefreshToken } from "../hooks/useRefreshToken"; + +/** + * A wrapper around the element rendered by the React Router component. + * + * Useful to trigger code when the route changes. + */ +const RouteWrapper = ({children}: {children: React.ReactNode}) => { + useRefreshToken(); + + return ( + <>{children} + ); +} + +export default RouteWrapper; diff --git a/frontend/src/hooks/useRefreshToken.ts b/frontend/src/hooks/useRefreshToken.ts new file mode 100644 index 0000000..e4f9cee --- /dev/null +++ b/frontend/src/hooks/useRefreshToken.ts @@ -0,0 +1,40 @@ +import { useState, useEffect, useContext } from 'react'; +import { useLocation } from 'react-router-dom'; + +import { OpenAPI } from '../client'; +import { AuthContext } from '../contexts/AuthContextProvider'; +import keycloak from '../keycloak';; + + +const TOKEN_MIN_VALIDITY = 120; + + +export const useRefreshToken = () => { + const [pending, setPending] = useState(true); + const location = useLocation(); + const { logout } = useContext(AuthContext); + + const onTokenRefresh = (token: string | undefined) => { + if (token) { + OpenAPI.TOKEN = token; + } + } + + useEffect(() => { + const refreshToken = async () => { + try { + const refreshed = await keycloak.updateToken(TOKEN_MIN_VALIDITY); + if (refreshed) { + onTokenRefresh(keycloak.token); + } + setPending(false); + } catch (error) { + logout(); + } + } + + refreshToken(); + }, [location]); + + return { pending }; +};