chore: upgrade check

This commit is contained in:
jialin
2024-10-18 19:35:47 +08:00
parent 2fea3a4b40
commit 386f2969eb
20 changed files with 172 additions and 47 deletions
+16 -2
View File
@@ -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);
+4
View File
@@ -152,6 +152,10 @@
font-size: var(--font-size-large);
}
.font-size-20 {
font-size: 20px;
}
.font-400 {
font-weight: var(--font-weight-normal);
}
+6
View File
@@ -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',
''
+1
View File
@@ -12,6 +12,7 @@ const Footer: React.FC = () => {
const showVersion = () => {
Modal.info({
...modalConfig,
width: 460,
content: <VersionInfo intl={intl} />
});
};
+1 -1
View File
@@ -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;
+13 -7
View File
@@ -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);
}
}
+33 -18
View File
@@ -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 -1
View File
@@ -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'
};
+20
View File
@@ -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
View File
@@ -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 (
+28 -5
View File
@@ -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"
+5 -2
View File
@@ -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'
};
+3 -1
View File
@@ -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'
};
+4 -1
View File
@@ -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': '更新日志'
};
+3 -1
View File
@@ -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} 版本可供更新'
};
+1 -3
View File
@@ -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 };
+6
View File
@@ -12,3 +12,9 @@ export async function queryVersionInfo() {
method: 'GET'
});
}
export async function updateCheck() {
return request(`/update/`, {
method: 'GET'
});
}