chore: upgrade check
This commit is contained in:
+16
-2
@@ -1,9 +1,10 @@
|
||||
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom } from '@/atoms/user';
|
||||
import { setAtomStorage } from '@/atoms/utils';
|
||||
import { requestConfig } from '@/request-config';
|
||||
import {
|
||||
queryCurrentUserState,
|
||||
queryVersionInfo
|
||||
queryVersionInfo,
|
||||
updateCheck
|
||||
} from '@/services/profile/apis';
|
||||
import { RequestConfig, history } from '@umijs/max';
|
||||
|
||||
@@ -16,11 +17,24 @@ export async function getInitialState(): Promise<{
|
||||
}> {
|
||||
const { location } = history;
|
||||
|
||||
const getUpdateCheck = async () => {
|
||||
try {
|
||||
const data = await updateCheck();
|
||||
setAtomStorage(UpdateCheckAtom, data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('updateCheck error', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchUserInfo = async (): Promise<Global.UserInfo> => {
|
||||
try {
|
||||
const data = await queryCurrentUserState({
|
||||
skipErrorHandler: true
|
||||
});
|
||||
if (data.is_admin) {
|
||||
getUpdateCheck();
|
||||
}
|
||||
return data;
|
||||
} catch (error) {
|
||||
history.push(loginPath);
|
||||
|
||||
@@ -152,6 +152,10 @@
|
||||
font-size: var(--font-size-large);
|
||||
}
|
||||
|
||||
.font-size-20 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.font-400 {
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ export const GPUStackVersionAtom = atom<{
|
||||
git_commit: ''
|
||||
});
|
||||
|
||||
export const UpdateCheckAtom = atom<{
|
||||
latest_version: string;
|
||||
}>({
|
||||
latest_version: ''
|
||||
});
|
||||
|
||||
export const initialPasswordAtom = atomWithStorage<string>(
|
||||
'initialPassword',
|
||||
''
|
||||
|
||||
@@ -12,6 +12,7 @@ const Footer: React.FC = () => {
|
||||
const showVersion = () => {
|
||||
Modal.info({
|
||||
...modalConfig,
|
||||
width: 460,
|
||||
content: <VersionInfo intl={intl} />
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_4jkdkc8jcf7.js'
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_l9554igzbh.js'
|
||||
});
|
||||
|
||||
export default IconFont;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.version-box {
|
||||
display: flex;
|
||||
margin-bottom: 40px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
@@ -25,18 +24,25 @@
|
||||
.ver {
|
||||
line-height: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: var(--font-size-middle);
|
||||
color: var(--ant-color-text-secondary);
|
||||
gap: 10px;
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: 60px;
|
||||
font-size: var(--font-size-middle);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.val {
|
||||
color: var(--ant-color-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.upgrade {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
justify-content: space-between;
|
||||
padding-block: 20px 4px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border-top: 1px solid var(--ant-color-split);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,49 @@
|
||||
import Logo from '@/assets/images/gpustack-logo.png';
|
||||
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
||||
import { getAtomStorage } from '@/atoms/utils';
|
||||
import externalLinks from '@/constants/external-links';
|
||||
import { Button } from 'antd';
|
||||
import React from 'react';
|
||||
import './index.less';
|
||||
|
||||
const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
||||
// get the data attr from html
|
||||
const version = document.documentElement.getAttribute('data-version');
|
||||
const latestVersion = getAtomStorage(UpdateCheckAtom).latest_version;
|
||||
const currentVersion = getAtomStorage(GPUStackVersionAtom)?.version;
|
||||
|
||||
return (
|
||||
<div className="version-box">
|
||||
<div className="img">
|
||||
<img src={Logo} alt="logo" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="ver">
|
||||
<span className="label">
|
||||
{' '}
|
||||
{intl.formatMessage({ id: 'common.footer.version.server' })}
|
||||
</span>
|
||||
<span className="val">
|
||||
{getAtomStorage(GPUStackVersionAtom)?.version ||
|
||||
getAtomStorage(GPUStackVersionAtom)?.git_commit}
|
||||
</span>
|
||||
</div>
|
||||
<div className="ver">
|
||||
<span className="label">UI </span>
|
||||
<span className="val"> {version}</span>
|
||||
</div>
|
||||
|
||||
<div className="ver">
|
||||
<span className="label">
|
||||
{' '}
|
||||
{intl.formatMessage({ id: 'common.footer.version' })}
|
||||
</span>
|
||||
<span className="val">
|
||||
{getAtomStorage(GPUStackVersionAtom)?.version ||
|
||||
getAtomStorage(GPUStackVersionAtom)?.git_commit}
|
||||
</span>
|
||||
</div>
|
||||
{getAtomStorage(userAtom)?.is_admin && (
|
||||
<div className="upgrade">
|
||||
<span className="m-l-5">
|
||||
{latestVersion && latestVersion !== currentVersion
|
||||
? intl.formatMessage(
|
||||
{ id: 'users.version.update' },
|
||||
{ version: latestVersion }
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{ id: 'users.version.islatest' },
|
||||
{ version: currentVersion }
|
||||
)}
|
||||
</span>
|
||||
<Button type="link" href={externalLinks.release} target="_blank">
|
||||
{intl.formatMessage({ id: 'common.text.changelog' })}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,5 +2,6 @@ export default {
|
||||
documentation: 'https://docs.gpustack.ai/',
|
||||
github: 'https://github.com/gpustack/gpustack',
|
||||
discord: 'https://discord.gg/VXYJzuaqwD',
|
||||
site: 'https://gpustack.ai/'
|
||||
site: 'https://gpustack.ai/',
|
||||
release: 'https://github.com/gpustack/gpustack/releases'
|
||||
};
|
||||
|
||||
@@ -434,6 +434,26 @@ body {
|
||||
font-size: var(--font-size-middle);
|
||||
}
|
||||
}
|
||||
|
||||
.new-icon {
|
||||
position: relative;
|
||||
bottom: 3px;
|
||||
display: flex;
|
||||
color: #fff;
|
||||
width: 36px;
|
||||
height: 15px;
|
||||
margin-left: 5px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--ant-orange-5);
|
||||
border-radius: 8px 8px 8px 0;
|
||||
transform: scale(0.9);
|
||||
|
||||
.text {
|
||||
transform: scale(0.8);
|
||||
line-height: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-3
@@ -1,6 +1,6 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { userAtom } from '@/atoms/user';
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
||||
import ShortCuts, {
|
||||
modalConfig as ShortCutsConfig
|
||||
} from '@/components/short-cuts';
|
||||
@@ -88,6 +88,8 @@ const mapRoutes = (routes: IRoute[], role: string) => {
|
||||
|
||||
export default (props: any) => {
|
||||
const [userInfo] = useAtom(userAtom);
|
||||
const [version] = useAtom(GPUStackVersionAtom);
|
||||
const [updateCheck] = useAtom(UpdateCheckAtom);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const intl = useIntl();
|
||||
@@ -115,6 +117,7 @@ export default (props: any) => {
|
||||
const showVersion = () => {
|
||||
Modal.info({
|
||||
...modalConfig,
|
||||
width: 460,
|
||||
content: <VersionInfo intl={intl} />
|
||||
});
|
||||
};
|
||||
@@ -169,6 +172,14 @@ export default (props: any) => {
|
||||
[location.pathname]
|
||||
);
|
||||
|
||||
const showUpgrade = useMemo(() => {
|
||||
return (
|
||||
initialState?.currentUser?.is_admin &&
|
||||
updateCheck.latest_version &&
|
||||
updateCheck.latest_version !== version?.version
|
||||
);
|
||||
}, [updateCheck, version, initialState]);
|
||||
|
||||
const renderMenuHeader = useCallback(
|
||||
(logo, title) => {
|
||||
return (
|
||||
@@ -206,12 +217,13 @@ export default (props: any) => {
|
||||
setInitialState,
|
||||
intl,
|
||||
siderWidth: layoutProps.siderWidth,
|
||||
collapsed: layoutProps.collapsed
|
||||
collapsed: layoutProps.collapsed,
|
||||
showUpgrade
|
||||
});
|
||||
|
||||
return dom;
|
||||
},
|
||||
[intl]
|
||||
[intl, version, updateCheck]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -26,7 +26,7 @@ export const getRightRenderContent = (opts: {
|
||||
siderWidth: number;
|
||||
intl: any;
|
||||
}) => {
|
||||
const { intl, collapsed, siderWidth } = opts;
|
||||
const { intl, collapsed, siderWidth, showUpgrade } = opts;
|
||||
|
||||
const allLocals = getAllLocales();
|
||||
if (opts.runtimeConfig.rightRender) {
|
||||
@@ -109,14 +109,28 @@ export const getRightRenderContent = (opts: {
|
||||
mode: 'vertical',
|
||||
expandIcon: false,
|
||||
// inlineCollapsed: collapsed,
|
||||
triggerSubMenuAction: 'hover',
|
||||
triggerSubMenuAction: 'click',
|
||||
items: [
|
||||
{
|
||||
key: 'help',
|
||||
icon: <QuestionCircleOutlined />,
|
||||
label: (
|
||||
<span className="sub-title">
|
||||
{intl?.formatMessage?.({ id: 'common.button.help' })}
|
||||
<span className="sub-title ">
|
||||
<span className="flex-center">
|
||||
<span>{intl?.formatMessage?.({ id: 'common.button.help' })}</span>
|
||||
{showUpgrade && (
|
||||
<span
|
||||
className="m-l-5"
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: 'var(--ant-orange-5)'
|
||||
}}
|
||||
></span>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
children: helpList.map((item) => ({
|
||||
@@ -125,7 +139,16 @@ export const getRightRenderContent = (opts: {
|
||||
<span className="flex flex-center">
|
||||
{item.icon}
|
||||
{item.key === 'version' ? (
|
||||
<a className="m-l-8">{item.label}</a>
|
||||
<>
|
||||
<a className="m-l-8">{item.label}</a>
|
||||
{showUpgrade && (
|
||||
<span className="new-icon">
|
||||
<span className="text">
|
||||
{intl.formatMessage({ id: 'common.text.new' })}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<a
|
||||
className="m-l-8"
|
||||
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
'common.tips.title': 'Tips:',
|
||||
'common.input.range.start': 'start value',
|
||||
'common.input.range.end': 'end value',
|
||||
'common.footer.version': 'Versions',
|
||||
'common.footer.version': 'Version',
|
||||
'common.footer.version.title': 'Version Information',
|
||||
'common.footer.version.web': 'UI',
|
||||
'common.footer.version.server': 'Server',
|
||||
@@ -207,5 +207,8 @@ export default {
|
||||
'common.button.addLabel': 'Add Labels',
|
||||
'common.button.addSelector': 'Add Selectors',
|
||||
'common.button.addParams': 'Add Parameter',
|
||||
'common.text.here': 'here'
|
||||
'common.text.here': 'here',
|
||||
'common.text.latest': 'Latest',
|
||||
'common.text.new': 'New',
|
||||
'common.text.changelog': 'Release Notes'
|
||||
};
|
||||
|
||||
@@ -25,5 +25,7 @@ export default {
|
||||
'users.password.confirm': 'Confirm New Password',
|
||||
'users.password.confirm.empty': 'Please confirm the new password.',
|
||||
'users.password.confirm.error': 'The two passwords entered do not match.',
|
||||
'users.login.title': 'Log in to {name}'
|
||||
'users.login.title': 'Log in to {name}',
|
||||
'users.version.islatest': '{version} is the latest version',
|
||||
'users.version.update': 'Version {version} is available for update'
|
||||
};
|
||||
|
||||
@@ -200,5 +200,8 @@ export default {
|
||||
'common.button.addLabel': '添加标签',
|
||||
'common.button.addSelector': '添加选择器',
|
||||
'common.button.addParams': '添加参数',
|
||||
'common.text.here': '这里'
|
||||
'common.text.here': '这里',
|
||||
'common.text.latest': '最新',
|
||||
'common.text.new': '新',
|
||||
'common.text.changelog': '更新日志'
|
||||
};
|
||||
|
||||
@@ -24,5 +24,7 @@ export default {
|
||||
'users.password.confirm': '确认新密码',
|
||||
'users.password.confirm.empty': '请确认新密码',
|
||||
'users.password.confirm.error': '两次输入的密码不一致',
|
||||
'users.login.title': '登录 {name}'
|
||||
'users.login.title': '登录 {name}',
|
||||
'users.version.islatest': '{version} 已是最新版本',
|
||||
'users.version.update': '{version} 版本可供更新'
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { clearAtomStorage } from '@/atoms/utils';
|
||||
import { RequestConfig, history } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
|
||||
const NoBaseURLAPIs = ['/auth', '/v1-openai', '/version', '/proxy'];
|
||||
const NoBaseURLAPIs = ['/auth', '/v1-openai', '/version', '/proxy', '/update'];
|
||||
|
||||
export const requestConfig: RequestConfig = {
|
||||
errorConfig: {
|
||||
@@ -16,7 +16,6 @@ export const requestConfig: RequestConfig = {
|
||||
if (!opts?.skipErrorHandler && response?.status) {
|
||||
message.error(errMsg);
|
||||
}
|
||||
console.log('errorHandler+++++++++++++++', error, opts);
|
||||
if (response?.status === 401) {
|
||||
clearAtomStorage(userAtom);
|
||||
|
||||
@@ -26,7 +25,6 @@ export const requestConfig: RequestConfig = {
|
||||
},
|
||||
requestInterceptors: [
|
||||
(url, options) => {
|
||||
console.log('requestInterceptors+++++++++++++++', url, options);
|
||||
if (NoBaseURLAPIs.some((api) => url.startsWith(api))) {
|
||||
options.baseURL = '';
|
||||
return { url, options };
|
||||
|
||||
@@ -12,3 +12,9 @@ export async function queryVersionInfo() {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateCheck() {
|
||||
return request(`/update/`, {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user