UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

117 lines (116 loc) 5.72 kB
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime"; /* * Copyright 2025 The Kubernetes Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import React, { Suspense } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { Redirect, Route, Switch, useHistory } from 'react-router-dom'; import { getToken } from '../../lib/auth'; import { getCluster, getSelectedClusters } from '../../lib/cluster'; import { useClustersConf } from '../../lib/k8s'; import { createRouteURL, getDefaultRoutes, getRoutePath, getRouteUseClusterURL, NotFoundRoute, } from '../../lib/router'; import { useTypedSelector } from '../../redux/reducers/reducers'; import { uiSlice } from '../../redux/uiSlice'; import ErrorBoundary from '../common/ErrorBoundary'; import ErrorComponent from '../common/ErrorPage'; import { useSidebarItem } from '../Sidebar'; export default function RouteSwitcher(props) { // The NotFoundRoute always has to be evaluated in the last place. const routes = useTypedSelector(state => state.routes.routes); const routeFilters = useTypedSelector(state => state.routes.routeFilters); const defaultRoutes = Object.values(getDefaultRoutes()).concat(NotFoundRoute); const clusters = useClustersConf(); const filteredRoutes = Object.values(routes) .concat(defaultRoutes) .filter(route => !(routeFilters.length > 0 && routeFilters.filter(f => f(route)).length !== routeFilters.length) && !route.disabled); return (_jsx(Suspense, { fallback: null, children: _jsx(Switch, { children: filteredRoutes.map((route, index) => route.name === 'OidcAuth' ? (_jsx(Route, { path: route.path, component: () => _jsx(RouteComponent, { route: route }) }, index)) : (_jsx(AuthRoute, { path: getRoutePath(route), sidebar: route.sidebar, requiresAuth: !route.noAuthRequired, requiresCluster: getRouteUseClusterURL(route), exact: !!route.exact, clusters: clusters, requiresToken: props.requiresToken, children: _jsx(RouteComponent, { route: route }, getCluster()) }, `${getCluster()}`))) }) })); } function RouteErrorBoundary(props) { const { error, route } = props; const { t } = useTranslation(); return (_jsx(ErrorComponent, { title: t('Uh-oh! Something went wrong.'), error: error, message: t('translation|Error loading {{ routeName }}', { routeName: route.name }) })); } function RouteComponent({ route }) { const { t } = useTranslation(); const dispatch = useDispatch(); React.useEffect(() => { dispatch(uiSlice.actions.setHideAppBar(route.hideAppBar)); }, [route.hideAppBar]); React.useEffect(() => { dispatch(uiSlice.actions.setIsFullWidth(route.isFullWidth)); }, [route.isFullWidth]); return (_jsx(PageTitle, { title: t(route.name ? route.name : typeof route.sidebar === 'string' ? route.sidebar : route.sidebar?.item || ''), children: _jsx(ErrorBoundary, { fallback: (props) => (_jsx(RouteErrorBoundary, { error: props.error, route: route })), children: _jsx(route.component, {}) }) })); } function PageTitle({ title, children, }) { React.useEffect(() => { document.title = title || ''; }, [title]); return _jsx(_Fragment, { children: children }); } function AuthRoute(props) { const { children, sidebar, requiresAuth = true, requiresCluster = true, requiresToken, ...other } = props; const redirectRoute = getCluster() ? 'login' : 'chooser'; useSidebarItem(sidebar); function getRenderer({ location }) { if (!requiresAuth) { return children; } if (requiresCluster) { if (getSelectedClusters().length > 1) { // In multi-cluster mode, we do not know if one of them requires a token. return children; } const clusterName = getCluster(); if (!!clusterName) { if (!!getToken(clusterName) || !requiresToken()) { return children; } } } return (_jsx(Redirect, { to: { pathname: createRouteURL(redirectRoute), state: { from: location }, } })); } // If no auth is required for the view, or the token is set up, then // render the assigned component. Otherwise redirect to the login route. return _jsx(Route, { ...other, render: getRenderer }); } const PreviousRouteContext = React.createContext(0); export function PreviousRouteProvider({ children }) { const history = useHistory(); const [locationInfo, setLocationInfo] = React.useState(0); React.useEffect(() => { history.listen((location, action) => { if (action === 'PUSH') { setLocationInfo(levels => levels + 1); } else if (action === 'POP') { setLocationInfo(levels => levels - 1); } }); }, []); return (_jsx(PreviousRouteContext.Provider, { value: locationInfo, children: children })); } export function useHasPreviousRoute() { const routeLevels = React.useContext(PreviousRouteContext); return routeLevels >= 1; }