chore: upgrade info
This commit is contained in:
+7
-2
@@ -79,10 +79,15 @@ export async function getInitialState(): Promise<{
|
||||
const getAppVersionInfo = async () => {
|
||||
try {
|
||||
const data = await queryVersionInfo();
|
||||
const isProduction = data.version?.indexOf('0.0.0') === -1;
|
||||
|
||||
const isDev = data.version?.indexOf('0.0.0') > -1;
|
||||
const isRc = data.version?.indexOf('rc') > -1;
|
||||
|
||||
setAtomStorage(GPUStackVersionAtom, {
|
||||
...data,
|
||||
isProduction
|
||||
isProd: !isDev && !isRc,
|
||||
isDev,
|
||||
isRc
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('queryVersionInfo error', error);
|
||||
|
||||
+6
-2
@@ -6,11 +6,15 @@ export const userAtom = atomWithStorage<any>('userInfo', null);
|
||||
export const GPUStackVersionAtom = atom<{
|
||||
version: string;
|
||||
git_commit: string;
|
||||
isProduction: boolean;
|
||||
isProd: boolean;
|
||||
isDev?: boolean;
|
||||
isRc?: boolean;
|
||||
}>({
|
||||
version: '',
|
||||
git_commit: '',
|
||||
isProduction: false
|
||||
isProd: false,
|
||||
isDev: false,
|
||||
isRc: false
|
||||
});
|
||||
|
||||
export const UpdateCheckAtom = atom<{
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
import Logo from '@/assets/images/gpustack-logo.png';
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
||||
import { getAtomStorage } from '@/atoms/utils';
|
||||
import externalLinks from '@/constants/external-links';
|
||||
import { Button } from 'antd';
|
||||
import { useAtom } from 'jotai';
|
||||
import React from 'react';
|
||||
import './index.less';
|
||||
|
||||
const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
||||
const latestVersion = getAtomStorage(UpdateCheckAtom).latest_version;
|
||||
const currentVersion = getAtomStorage(GPUStackVersionAtom)?.version;
|
||||
const [gpuStackVersionAtom] = useAtom(GPUStackVersionAtom);
|
||||
const [userDataAtom] = useAtom(userAtom);
|
||||
const [updateCheck] = useAtom(UpdateCheckAtom);
|
||||
|
||||
const isProd =
|
||||
currentVersion?.indexOf('rc') === -1 &&
|
||||
currentVersion?.indexOf('0.0.0') === -1;
|
||||
// current version info
|
||||
const {
|
||||
version: currentVersion,
|
||||
git_commit,
|
||||
isProd,
|
||||
isDev
|
||||
} = gpuStackVersionAtom;
|
||||
|
||||
// user info
|
||||
const { is_admin } = userDataAtom || {};
|
||||
|
||||
// update check latest version info
|
||||
const { latest_version: latestVersion } = updateCheck;
|
||||
|
||||
const uiVersion = document.documentElement.getAttribute('data-version');
|
||||
|
||||
@@ -29,10 +40,7 @@ const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
||||
</span>
|
||||
)}
|
||||
{isProd ? (
|
||||
<span className="val">
|
||||
{getAtomStorage(GPUStackVersionAtom)?.version ||
|
||||
getAtomStorage(GPUStackVersionAtom)?.git_commit}
|
||||
</span>
|
||||
<span className="val">{currentVersion || git_commit}</span>
|
||||
) : (
|
||||
<span className="val dev">
|
||||
<span className="item">
|
||||
@@ -40,9 +48,7 @@ const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
||||
{' '}
|
||||
{intl.formatMessage({ id: 'common.footer.version.server' })}
|
||||
</span>
|
||||
{currentVersion.indexOf('0.0.0') > -1
|
||||
? getAtomStorage(GPUStackVersionAtom)?.git_commit
|
||||
: getAtomStorage(GPUStackVersionAtom)?.version}
|
||||
{isDev ? git_commit : currentVersion}
|
||||
</span>
|
||||
<span className="item">
|
||||
<span className="tl">UI</span>
|
||||
@@ -51,12 +57,10 @@ const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{getAtomStorage(userAtom)?.is_admin && isProd && (
|
||||
{is_admin && isProd && (
|
||||
<div className="upgrade-text">
|
||||
<span className="m-l-5">
|
||||
{latestVersion &&
|
||||
latestVersion !== currentVersion &&
|
||||
latestVersion.indexOf('0.0.0') === -1
|
||||
{latestVersion && latestVersion !== currentVersion && !isDev
|
||||
? intl.formatMessage(
|
||||
{ id: 'users.version.update' },
|
||||
{ version: latestVersion }
|
||||
|
||||
@@ -122,12 +122,12 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
initialState?.currentUser?.is_admin &&
|
||||
updateCheck.latest_version &&
|
||||
updateCheck.latest_version !== version?.version &&
|
||||
updateCheck.latest_version?.indexOf('0.0.0') === -1 &&
|
||||
updateCheck.latest_version?.indexOf('rc') === -1
|
||||
version?.isProd
|
||||
);
|
||||
}, [
|
||||
updateCheck.latest_version,
|
||||
version.version,
|
||||
version.isProd,
|
||||
initialState?.currentUser?.is_admin
|
||||
]);
|
||||
|
||||
|
||||
+18
-221
@@ -1,16 +1,11 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { routeCacheAtom, setRouteCache } from '@/atoms/route-cache';
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
||||
import { userAtom } from '@/atoms/user';
|
||||
import DarkMask from '@/components/dark-mask';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import ShortCuts, {
|
||||
modalConfig as ShortCutsConfig
|
||||
} from '@/components/short-cuts';
|
||||
import VersionInfo, { modalConfig } from '@/components/version-info';
|
||||
import routeCachekey from '@/config/route-cachekey';
|
||||
import { DEFAULT_ENTER_PAGE } from '@/config/settings';
|
||||
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||
import useOverlayScroller from '@/hooks/use-overlay-scroller';
|
||||
import useUserSettings from '@/hooks/use-user-settings';
|
||||
import { logout } from '@/pages/login/apis';
|
||||
@@ -18,7 +13,6 @@ import { useAccessMarkedRoutes } from '@@/plugin-access';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import { ProLayout } from '@ant-design/pro-components';
|
||||
import {
|
||||
Link,
|
||||
Outlet,
|
||||
dropByCacheKey,
|
||||
history,
|
||||
@@ -29,18 +23,17 @@ import {
|
||||
useNavigate,
|
||||
type IRoute
|
||||
} from '@umijs/max';
|
||||
import { Button, ConfigProvider, Modal, Tooltip, theme } from 'antd';
|
||||
import { Button, ConfigProvider, Modal, theme } from 'antd';
|
||||
import 'driver.js/dist/driver.css';
|
||||
import { useAtom } from 'jotai';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { PageContainerInner } from '../pages/_components/page-box';
|
||||
import Exception from './Exception';
|
||||
import './Layout.css';
|
||||
import { LogoIcon, SLogoIcon } from './Logo';
|
||||
import ErrorBoundary from './error-boundary';
|
||||
import { ExtraContent } from './extraRender';
|
||||
import { getRightRenderContent } from './rightRender';
|
||||
import { patchRoutes } from './runtime';
|
||||
import SiderMenu from './sider-menu';
|
||||
|
||||
@@ -57,16 +50,11 @@ const NO_CONTAINER_PAGES = [
|
||||
|
||||
const loginPath = DEFAULT_ENTER_PAGE.login;
|
||||
|
||||
type InitialStateType = {
|
||||
fetchUserInfo: () => Promise<Global.UserInfo>;
|
||||
currentUser?: Global.UserInfo;
|
||||
};
|
||||
|
||||
// Filter out the routes that need to be displayed, where filterFn indicates the levels that should not be shown
|
||||
const filterRoutes = (
|
||||
routes: IRoute[],
|
||||
filterFn: (route: IRoute) => boolean
|
||||
) => {
|
||||
): any[] => {
|
||||
if (routes.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -117,21 +105,13 @@ export default (props: any) => {
|
||||
defer: false
|
||||
});
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const { themeData, setTheme, setUserSettings, userSettings, isDarkTheme } =
|
||||
useUserSettings();
|
||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||
const { initialize: initializeMenu } = useOverlayScroller();
|
||||
const { themeData, setUserSettings, userSettings } = useUserSettings();
|
||||
const [userInfo] = useAtom(userAtom);
|
||||
const [routeCache] = useAtom(routeCacheAtom);
|
||||
const [version] = useAtom(GPUStackVersionAtom);
|
||||
const [updateCheck] = useAtom(UpdateCheckAtom);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const intl = useIntl();
|
||||
const { clientRoutes, pluginManager } = useAppData();
|
||||
// const [collapsed, setCollapsed] = useState(userSettings.collapsed || false);
|
||||
const [collapseValue, setCollapseValue] = useState(false);
|
||||
const [collapseKeys, setCollapseKeys] = useState<Set<string>>(new Set());
|
||||
const { clientRoutes } = useAppData();
|
||||
|
||||
const initialInfo = (useModel && useModel('@@initialState')) || {
|
||||
initialState: undefined,
|
||||
@@ -150,23 +130,6 @@ export default (props: any) => {
|
||||
return intl.formatMessage({ id: args.id });
|
||||
};
|
||||
|
||||
const showVersion = () => {
|
||||
saveScrollHeight();
|
||||
modal.info({
|
||||
...modalConfig,
|
||||
width: 460,
|
||||
content: <VersionInfo intl={intl} />,
|
||||
onCancel: restoreScrollHeight
|
||||
});
|
||||
};
|
||||
|
||||
const showShortcuts = () => {
|
||||
Modal.info({
|
||||
...ShortCutsConfig,
|
||||
content: <ShortCuts intl={intl} />
|
||||
});
|
||||
};
|
||||
|
||||
const initRouteCacheValue = (pathname) => {
|
||||
if (routeCache.get(pathname) === undefined && routeCachekey[pathname]) {
|
||||
setRouteCache(pathname, false);
|
||||
@@ -184,22 +147,17 @@ export default (props: any) => {
|
||||
|
||||
const runtimeConfig = {
|
||||
...initialInfo,
|
||||
logout: async (userInfo) => {
|
||||
logout: async () => {
|
||||
await logout();
|
||||
navigate(loginPath);
|
||||
},
|
||||
showVersion: () => {
|
||||
return showVersion();
|
||||
},
|
||||
showShortcuts: () => {
|
||||
return showShortcuts();
|
||||
},
|
||||
showVersion: () => {},
|
||||
showShortcuts: () => {},
|
||||
notFound: <span>404 not found</span>
|
||||
};
|
||||
|
||||
const handleToggleCollapse = (e: any) => {
|
||||
e.stopPropagation();
|
||||
// setCollapsed(!collapsed);
|
||||
setUserSettings({
|
||||
...userSettings,
|
||||
collapsed: !userSettings.collapsed
|
||||
@@ -235,38 +193,6 @@ export default (props: any) => {
|
||||
|
||||
console.log('matchedRoute=========', matchedRoute, route);
|
||||
|
||||
const allRouteKeys = useMemo(() => {
|
||||
const keys = new Set<string>();
|
||||
const childrenRoutes = route?.children || [];
|
||||
const traverseRoutes = (routes) => {
|
||||
routes.forEach((r) => {
|
||||
if (r.path) {
|
||||
keys.add(r.path);
|
||||
}
|
||||
if (r.children) {
|
||||
traverseRoutes(r.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
traverseRoutes(childrenRoutes);
|
||||
|
||||
return keys;
|
||||
}, [route?.children]);
|
||||
|
||||
const showUpgrade = useMemo(() => {
|
||||
return (
|
||||
initialState?.currentUser?.is_admin &&
|
||||
updateCheck.latest_version &&
|
||||
updateCheck.latest_version !== version?.version &&
|
||||
updateCheck.latest_version?.indexOf('0.0.0') === -1 &&
|
||||
updateCheck.latest_version?.indexOf('rc') === -1
|
||||
);
|
||||
}, [
|
||||
updateCheck.latest_version,
|
||||
version.version,
|
||||
initialState?.currentUser?.is_admin
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const body = document.querySelector('body');
|
||||
if (body) {
|
||||
@@ -306,92 +232,13 @@ export default (props: any) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleToggleGroup = (menuItemProps, e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (collapseKeys.has(menuItemProps.key)) {
|
||||
collapseKeys.delete(menuItemProps.key);
|
||||
} else {
|
||||
collapseKeys.add(menuItemProps.key);
|
||||
}
|
||||
setCollapseKeys(new Set(collapseKeys));
|
||||
};
|
||||
|
||||
const menuContentRender = (menuProps, defaultDom) => {
|
||||
const menuContentRender = (menuProps: any, defaultDom: React.ReactNode) => {
|
||||
return <SiderMenu {...menuProps}></SiderMenu>;
|
||||
};
|
||||
|
||||
const actionRender = (layoutProps) => {
|
||||
const dom = getRightRenderContent({
|
||||
runtimeConfig,
|
||||
loading,
|
||||
initialState,
|
||||
setInitialState,
|
||||
intl,
|
||||
isDarkTheme: userSettings.isDarkTheme,
|
||||
siderWidth: layoutProps?.siderWidth,
|
||||
collapsed: layoutProps?.collapsed,
|
||||
showUpgrade
|
||||
});
|
||||
|
||||
return dom;
|
||||
};
|
||||
|
||||
const menuItemRender = (menuItemProps, defaultDom) => {
|
||||
if (menuItemProps.isUrl || menuItemProps.children) {
|
||||
return defaultDom;
|
||||
}
|
||||
if (menuItemProps.path && location.pathname !== menuItemProps.path) {
|
||||
return (
|
||||
<Tooltip
|
||||
title={collapsed ? menuItemProps.name : false}
|
||||
placement="right"
|
||||
>
|
||||
<Link
|
||||
to={menuItemProps.path.replace('/*', '')}
|
||||
target={menuItemProps.target}
|
||||
>
|
||||
{defaultDom}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tooltip title={collapsed ? menuItemProps.name : false} placement="right">
|
||||
{defaultDom}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const menuDataRender = (menuData) => {
|
||||
const currentItem = menuData.find((s) => location.pathname === s.path);
|
||||
const result = menuData.map((item) => {
|
||||
const newItem = { ...item };
|
||||
|
||||
const selected =
|
||||
location.pathname === newItem.path ||
|
||||
location.pathname.indexOf(newItem.path) > -1;
|
||||
|
||||
if (newItem.icon) {
|
||||
newItem.icon = selected ? (
|
||||
<IconFont type={newItem.selectedIcon} />
|
||||
) : (
|
||||
<IconFont type={newItem.defaultIcon} />
|
||||
);
|
||||
}
|
||||
if (newItem.children) {
|
||||
newItem.children = menuDataRender(newItem.children);
|
||||
}
|
||||
return newItem;
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const onPageChange = (route) => {
|
||||
const onPageChange = (route: any) => {
|
||||
const { location } = history;
|
||||
const { pathname } = location;
|
||||
console.log('onPageChange', pathname, route);
|
||||
|
||||
initRouteCacheValue(pathname);
|
||||
dropRouteCache(pathname);
|
||||
@@ -413,7 +260,7 @@ export default (props: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
const onMenuHeaderClick = (e) => {
|
||||
const onMenuHeaderClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const pagepath = initialState?.currentUser?.is_admin
|
||||
@@ -423,65 +270,13 @@ export default (props: any) => {
|
||||
navigate(pagepath);
|
||||
};
|
||||
|
||||
const onCollapse = (value) => {
|
||||
const onCollapse = (value: boolean) => {
|
||||
setUserSettings({
|
||||
...userSettings,
|
||||
collapsed: value
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// clear previous marks and measures
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
// record start time
|
||||
performance.mark('route-start');
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
// record end time
|
||||
performance.mark('route-end');
|
||||
|
||||
// make sure `route-start` exists
|
||||
if (performance.getEntriesByName('route-start').length > 0) {
|
||||
performance.measure('route-change', 'route-start', 'route-end');
|
||||
|
||||
const measure = performance.getEntriesByName('route-change')[0];
|
||||
console.log(
|
||||
`[Performance] Route change to ${location.pathname} took ${measure.duration.toFixed(2)}ms`
|
||||
);
|
||||
|
||||
// clear marks and measures
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
} else {
|
||||
console.warn('Missing performance mark: route-start');
|
||||
}
|
||||
});
|
||||
}, [location.pathname]);
|
||||
|
||||
const onRenderCallback = (id, phase, actualDuration) => {
|
||||
console.log(
|
||||
`[Profiler] Route: ${id} - Phase: ${phase} - Render time: ${actualDuration.toFixed(2)}ms`
|
||||
);
|
||||
};
|
||||
|
||||
const currentTheme = useMemo(() => {
|
||||
const data = {
|
||||
algorithm: userSettings.isDarkTheme
|
||||
? theme.darkAlgorithm
|
||||
: theme.defaultAlgorithm,
|
||||
...themeData
|
||||
};
|
||||
return data;
|
||||
}, [userSettings.isDarkTheme, themeData]);
|
||||
|
||||
const outlet = runtimeConfig.childrenRender ? (
|
||||
runtimeConfig.childrenRender(<Outlet />, props)
|
||||
) : (
|
||||
<Outlet />
|
||||
);
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
componentSize="large"
|
||||
@@ -530,7 +325,7 @@ export default (props: any) => {
|
||||
type: 'group'
|
||||
}}
|
||||
splitMenus={true}
|
||||
logo={collapsed ? SLogoIcon : LogoIcon}
|
||||
logo={userSettings.collapsed ? <SLogoIcon /> : <LogoIcon />}
|
||||
menuContentRender={menuContentRender}
|
||||
disableContentMargin
|
||||
{...runtimeConfig}
|
||||
@@ -544,9 +339,11 @@ export default (props: any) => {
|
||||
noAccessible={runtimeConfig?.noAccessible}
|
||||
>
|
||||
{isNoContainerPage ? (
|
||||
outlet
|
||||
<Outlet />
|
||||
) : (
|
||||
<PageContainerInner>{outlet}</PageContainerInner>
|
||||
<PageContainerInner>
|
||||
<Outlet />
|
||||
</PageContainerInner>
|
||||
)}
|
||||
</Exception>
|
||||
</ProLayout>
|
||||
|
||||
@@ -88,7 +88,7 @@ const useStyles = createStyles(({ token, css }) => ({
|
||||
const LoginForm = () => {
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const { styles } = useStyles();
|
||||
const [userInfo, setUserInfo] = useAtom(userAtom);
|
||||
const [, setUserInfo] = useAtom(userAtom);
|
||||
const { initialState, setInitialState } = useModel('@@initialState') || {};
|
||||
const [authError, setAuthError] = useState<Error | null>(null);
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -34,7 +34,7 @@ const PasswordForm: React.FC = () => {
|
||||
});
|
||||
|
||||
await setUserInfo({
|
||||
...userInfo,
|
||||
...(userInfo || {}),
|
||||
require_password_change: false
|
||||
});
|
||||
setInitialPassword('');
|
||||
|
||||
Reference in New Issue
Block a user