@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
117 lines (116 loc) • 5.72 kB
JavaScript
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;
}