diff --git a/src/containers/App/Content.tsx b/src/containers/App/Content.tsx index 7c4ea4cc36..3003cf0c6d 100644 --- a/src/containers/App/Content.tsx +++ b/src/containers/App/Content.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {connect} from 'react-redux'; import type {RedirectProps} from 'react-router-dom'; -import {Redirect, Route, Switch} from 'react-router-dom'; +import {Redirect, Route, Switch, useLocation} from 'react-router-dom'; import {AccessDenied} from '../../components/Errors/403'; import {PageError} from '../../components/Errors/PageError/PageError'; @@ -10,7 +10,7 @@ import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper'; import {useSlots} from '../../components/slots'; import type {SlotMap} from '../../components/slots/SlotMap'; import type {SlotComponent} from '../../components/slots/types'; -import routes from '../../routes'; +import routes, {checkIsClustersPage} from '../../routes'; import type {RootState} from '../../store'; import {authenticationApi} from '../../store/reducers/authentication/authentication'; import { @@ -18,6 +18,7 @@ import { useClusterWithoutAuthInUI, useMetaCapabilitiesLoaded, useMetaCapabilitiesQuery, + useMetaLoginAvailable, } from '../../store/reducers/capabilities/hooks'; import {nodesListApi} from '../../store/reducers/nodesList'; import {uiFactory} from '../../uiFactory/uiFactory'; @@ -160,7 +161,7 @@ export function Content(props: ContentProps) { exact: true, component: Clusters, slot: ClustersSlot, - wrapper: GetMetaCapabilities, + wrapper: ClustersDataWrapper, })} {/* Single cluster routes */} {routesSlots.map((route) => { @@ -191,9 +192,34 @@ function DataWrapper({children}: {children: React.ReactNode}) { ); } -function GetUser({children}: {children: React.ReactNode}) { +function ClustersDataWrapper({children}: {children: React.ReactNode}) { + return ( + + {children} + + ); +} + +function GetMetaUser({children}: {children: React.ReactNode}) { + const location = useLocation(); + + const isClustersPage = checkIsClustersPage(location.pathname); + + const isMetaLoginAvailable = useMetaLoginAvailable(); + + if (isClustersPage && isMetaLoginAvailable) { + return {children}; + } + return children; +} + +function GetUser({children, useMeta}: {children: React.ReactNode; useMeta?: boolean}) { const database = useDatabaseFromQuery(); - const {isLoading, error} = authenticationApi.useWhoamiQuery({database}); + + const {isLoading, error} = authenticationApi.useWhoamiQuery({ + database, + useMeta, + }); const {appTitle} = useAppTitle(); const errorProps = error ? {...uiFactory.clusterOrDatabaseAccessError} : undefined; @@ -262,8 +288,15 @@ function ContentWrapper(props: ContentWrapperProps) { const {singleClusterMode, isAuthenticated} = props; const authUnavailable = useClusterWithoutAuthInUI(); + const location = useLocation(); + const isClustersPage = checkIsClustersPage(location.pathname); + + const isMetaLoginAvailable = useMetaLoginAvailable(); + + const isClustersAuthUnavailable = isClustersPage && !isMetaLoginAvailable; + const renderNotAuthenticated = () => { - if (authUnavailable) { + if (authUnavailable || isClustersAuthUnavailable) { return ; } return ; diff --git a/src/containers/Authentication/Authentication.tsx b/src/containers/Authentication/Authentication.tsx index 5f87792b4b..3d32d337a8 100644 --- a/src/containers/Authentication/Authentication.tsx +++ b/src/containers/Authentication/Authentication.tsx @@ -4,9 +4,9 @@ import {Eye, EyeSlash, Xmark} from '@gravity-ui/icons'; import {Button, Link as ExternalLink, Icon, TextInput} from '@gravity-ui/uikit'; import {useHistory, useLocation} from 'react-router-dom'; -import {parseQuery} from '../../routes'; +import {checkIsClustersPage, parseQuery} from '../../routes'; import {authenticationApi} from '../../store/reducers/authentication/authentication'; -import {useLoginWithDatabase} from '../../store/reducers/capabilities/hooks'; +import {useLoginWithDatabase, useMetaLoginAvailable} from '../../store/reducers/capabilities/hooks'; import {cn} from '../../utils/cn'; import {isDatabaseError, isPasswordError, isUserError} from './utils'; @@ -27,7 +27,10 @@ function Authentication({closable = false}: AuthenticationProps) { const needDatabase = useLoginWithDatabase(); - const [authenticate, {isLoading}] = authenticationApi.useAuthenticateMutation(undefined); + const isClustersPage = checkIsClustersPage(location.pathname); + const isMetaLoginAvailable = useMetaLoginAvailable(); + + const [authenticate, {isLoading}] = authenticationApi.useAuthenticateMutation(); const {returnUrl, database: databaseFromQuery} = parseQuery(location); @@ -53,8 +56,10 @@ function Authentication({closable = false}: AuthenticationProps) { setPasswordError(''); }; + const useMeta = isClustersPage && isMetaLoginAvailable; + const onLoginClick = () => { - authenticate({user: login, password, database}) + authenticate({user: login, password, database, useMeta}) .unwrap() .then(() => { if (returnUrl) { diff --git a/src/containers/Header/Header.tsx b/src/containers/Header/Header.tsx index 2df3a5a174..0cb76136f8 100644 --- a/src/containers/Header/Header.tsx +++ b/src/containers/Header/Header.tsx @@ -15,6 +15,7 @@ import {useHistory, useLocation} from 'react-router-dom'; import {getConnectToDBDialog} from '../../components/ConnectToDB/ConnectToDBDialog'; import {InternalLink} from '../../components/InternalLink'; +import {checkIsClustersPage, checkIsTenantPage} from '../../routes'; import { useAddClusterFeatureAvailable, useDatabasesAvailable, @@ -64,8 +65,8 @@ function Header() { const location = useLocation(); const history = useHistory(); - const isDatabasePage = location.pathname === '/tenant'; - const isClustersPage = location.pathname === '/clusters'; + const isDatabasePage = checkIsTenantPage(location.pathname); + const isClustersPage = checkIsClustersPage(location.pathname); const {isLoading: isClustersLoading, error: clustersError} = clustersApi.useGetClustersListQuery(undefined, { diff --git a/src/routes.ts b/src/routes.ts index 0fdeb78bd9..73baf6d7e4 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -146,3 +146,11 @@ type TabletPageQuery = QueryParamsTypeFromQueryObject(this.getPath('/meta/whoami'), {}, {}); + } + getMetaCapabilities() { return this.get( this.getPath('/capabilities'), diff --git a/src/store/reducers/authentication/authentication.ts b/src/store/reducers/authentication/authentication.ts index ee9f307e57..9965a05144 100644 --- a/src/store/reducers/authentication/authentication.ts +++ b/src/store/reducers/authentication/authentication.ts @@ -52,9 +52,17 @@ export const {selectIsUserAllowedToMakeChanges, selectIsViewerUser, selectUser} export const authenticationApi = api.injectEndpoints({ endpoints: (build) => ({ whoami: build.query({ - queryFn: async ({database}: {database?: string}, {dispatch}) => { + queryFn: async ( + {database, useMeta}: {database?: string; useMeta?: boolean}, + {dispatch}, + ) => { try { - const data = await window.api.viewer.whoami({database}); + let data: TUserToken; + if (useMeta && window.api.meta) { + data = await window.api.meta.metaWhoami(); + } else { + data = await window.api.viewer.whoami({database}); + } dispatch(setUser(data)); return {data}; } catch (error) { @@ -68,11 +76,17 @@ export const authenticationApi = api.injectEndpoints({ }), authenticate: build.mutation({ queryFn: async ( - params: {user: string; password: string; database?: string}, + params: {user: string; password: string; database?: string; useMeta?: boolean}, {dispatch}, ) => { try { - const data = await window.api.auth.authenticate(params); + const {useMeta, ...rest} = params; + let data; + if (useMeta) { + data = await window.api.meta?.metaAuthenticate(rest); + } else { + data = await window.api.auth.authenticate(rest); + } dispatch(setIsAuthenticated(true)); return {data}; } catch (error) { diff --git a/src/store/reducers/capabilities/hooks.ts b/src/store/reducers/capabilities/hooks.ts index 4fe87b3fae..54d5658441 100644 --- a/src/store/reducers/capabilities/hooks.ts +++ b/src/store/reducers/capabilities/hooks.ts @@ -176,3 +176,7 @@ export const useClusterEventsAvailable = () => { export const useDatabasesAvailable = () => { return useGetMetaFeatureVersion('/meta/databases') >= 1; }; + +export const useMetaLoginAvailable = () => { + return useGetMetaFeatureVersion('/meta/login') >= 1; +}; diff --git a/src/types/api/capabilities.ts b/src/types/api/capabilities.ts index 728085ab5f..d28c1453ed 100644 --- a/src/types/api/capabilities.ts +++ b/src/types/api/capabilities.ts @@ -52,4 +52,5 @@ export type MetaCapability = | '/meta/update_cluster' | '/meta/delete_cluster' | '/meta/events' + | '/meta/login' | '/meta/databases';