build: sync config
This commit is contained in:
@@ -19,6 +19,9 @@ export default defineConfig({
|
||||
history: {
|
||||
type: 'hash'
|
||||
},
|
||||
define: {
|
||||
'process.env.ENABLE_ENTERPRISE': process.env.ENABLE_ENTERPRISE
|
||||
},
|
||||
analyze: {
|
||||
analyzerMode: 'server',
|
||||
analyzerPort: 8888,
|
||||
|
||||
@@ -302,6 +302,26 @@ export default [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'apikeys',
|
||||
path: '/api-keys',
|
||||
key: 'apikeys',
|
||||
hideInMenu: true,
|
||||
selectedIcon: 'icon-key-filled',
|
||||
icon: 'icon-key',
|
||||
defaultIcon: 'icon-key',
|
||||
component: './api-keys'
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
path: '/settings',
|
||||
key: 'settings',
|
||||
hideInMenu: true,
|
||||
selectedIcon: 'icon-settings-filled',
|
||||
icon: 'icon-settings',
|
||||
defaultIcon: 'icon-settings',
|
||||
component: './settings'
|
||||
},
|
||||
{
|
||||
name: 'profile',
|
||||
path: '/profile',
|
||||
|
||||
@@ -3,13 +3,19 @@
|
||||
"author": "jialin",
|
||||
"scripts": {
|
||||
"build": "max build",
|
||||
"build:enterprise": "node ./scripts/run-enterprise-build.cjs",
|
||||
"check:locales": "npx tsx ./src/locales/check.ts",
|
||||
"dev": "max dev",
|
||||
"dev:enterprise": "node ./scripts/run-enterprise-dev.cjs",
|
||||
"format": "prettier --cache --write .",
|
||||
"postinstall": "max setup",
|
||||
"prepare": "husky",
|
||||
"preview": "max preview",
|
||||
"setup": "max setup",
|
||||
"sync:enterprise-config:clean": "node ./scripts/sync-enterprise-config.cjs clean",
|
||||
"sync:enterprise-config": "node ./scripts/sync-enterprise-config.cjs",
|
||||
"sync:enterprise-global:clean": "npm run sync:enterprise-config:clean",
|
||||
"sync:enterprise-global": "npm run sync:enterprise-config",
|
||||
"start": "npm run dev"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -94,6 +100,7 @@
|
||||
"babel-plugin-named-asset-import": "^0.3.8",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||
"compression-webpack-plugin": "^11.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^7.1.2",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
|
||||
const syncScript = path.resolve(__dirname, './sync-enterprise-config.cjs');
|
||||
|
||||
const runNodeScript = (args) => {
|
||||
const result = spawnSync(process.execPath, [syncScript, ...args], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
if (typeof result.status === 'number' && result.status !== 0) {
|
||||
process.exit(result.status);
|
||||
}
|
||||
};
|
||||
|
||||
const runBuild = () =>
|
||||
spawnSync('max', ['build'], {
|
||||
env: {
|
||||
...process.env,
|
||||
ENABLE_ENTERPRISE: 'true'
|
||||
},
|
||||
shell: true,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
let buildResult;
|
||||
|
||||
try {
|
||||
runNodeScript([]);
|
||||
buildResult = runBuild();
|
||||
} finally {
|
||||
try {
|
||||
runNodeScript(['clean']);
|
||||
} catch (error) {
|
||||
console.error('Failed to clean enterprise config:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (buildResult?.error) {
|
||||
throw buildResult.error;
|
||||
}
|
||||
|
||||
process.exit(buildResult?.status ?? 0);
|
||||
@@ -0,0 +1,69 @@
|
||||
const { spawn, spawnSync } = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
|
||||
const syncScript = path.resolve(__dirname, './sync-enterprise-config.cjs');
|
||||
|
||||
const runNodeScript = (args) => {
|
||||
const result = spawnSync(process.execPath, [syncScript, ...args], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
if (typeof result.status === 'number' && result.status !== 0) {
|
||||
process.exit(result.status);
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
};
|
||||
|
||||
runNodeScript([]);
|
||||
|
||||
const child = spawn('max', ['dev'], {
|
||||
env: {
|
||||
...process.env,
|
||||
ENABLE_ENTERPRISE: 'true'
|
||||
},
|
||||
shell: true,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
let cleaned = false;
|
||||
|
||||
const clean = () => {
|
||||
if (cleaned) {
|
||||
return;
|
||||
}
|
||||
cleaned = true;
|
||||
try {
|
||||
runNodeScript(['clean']);
|
||||
} catch (error) {
|
||||
console.error('Failed to clean enterprise config:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const forwardSignal = (signal) => {
|
||||
if (!child.killed) {
|
||||
child.kill(signal);
|
||||
}
|
||||
};
|
||||
|
||||
['SIGINT', 'SIGTERM', 'SIGHUP'].forEach((signal) => {
|
||||
process.on(signal, () => {
|
||||
forwardSignal(signal);
|
||||
});
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
clean();
|
||||
throw error;
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
clean();
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const workspaceGlobalPath = path.resolve(__dirname, '../../../global.tsx');
|
||||
const workspaceDependenciesPath = path.resolve(__dirname, '../../../dependencies.json');
|
||||
const workspaceTsconfigPath = path.resolve(__dirname, '../../../tsconfig.json');
|
||||
const targetGlobalPath = path.resolve(__dirname, '../src/global.tsx');
|
||||
const targetPackageJsonPath = path.resolve(__dirname, '../package.json');
|
||||
const targetTsconfigPath = path.resolve(__dirname, '../tsconfig.json');
|
||||
const mode = process.argv[2] || 'append';
|
||||
const blockStart = '// ENTERPRISE_PLUGIN_BLOCK_START';
|
||||
const blockEnd = '// ENTERPRISE_PLUGIN_BLOCK_END';
|
||||
|
||||
const workspaceSource = fs.readFileSync(workspaceGlobalPath, 'utf8').trim();
|
||||
const blockContent = `\n${blockStart}\n${workspaceSource}\n${blockEnd}\n`;
|
||||
|
||||
const stripBlock = (content) => {
|
||||
const pattern = new RegExp(`\\n?${blockStart}[\\s\\S]*?${blockEnd}\\n?`, 'g');
|
||||
return content.replace(pattern, '\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|
||||
};
|
||||
|
||||
const currentContent = fs.readFileSync(targetGlobalPath, 'utf8');
|
||||
const strippedContent = stripBlock(currentContent);
|
||||
|
||||
const readJson = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
const writeJson = (filePath, data) => {
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`);
|
||||
};
|
||||
|
||||
const syncPackageJson = () => {
|
||||
const sourceDependencies =
|
||||
readJson(workspaceDependenciesPath).dependencies || {};
|
||||
const targetPackageJson = readJson(targetPackageJsonPath);
|
||||
const currentDependencies = {
|
||||
...(targetPackageJson.dependencies || {})
|
||||
};
|
||||
|
||||
if (mode === 'clean') {
|
||||
Object.keys(sourceDependencies).forEach((dependencyName) => {
|
||||
delete currentDependencies[dependencyName];
|
||||
});
|
||||
} else {
|
||||
Object.assign(currentDependencies, sourceDependencies);
|
||||
}
|
||||
|
||||
targetPackageJson.dependencies = currentDependencies;
|
||||
writeJson(targetPackageJsonPath, targetPackageJson);
|
||||
console.log(
|
||||
`${mode === 'clean' ? 'Cleaned' : 'Synced'} enterprise dependencies in ${targetPackageJsonPath}`,
|
||||
);
|
||||
};
|
||||
|
||||
const syncTsconfig = () => {
|
||||
const sourcePaths =
|
||||
readJson(workspaceTsconfigPath).compilerOptions?.paths || {};
|
||||
const targetTsconfig = readJson(targetTsconfigPath);
|
||||
const currentPaths = {
|
||||
...(targetTsconfig.compilerOptions?.paths || {})
|
||||
};
|
||||
|
||||
if (mode === 'clean') {
|
||||
Object.keys(sourcePaths).forEach((pathKey) => {
|
||||
delete currentPaths[pathKey];
|
||||
});
|
||||
} else {
|
||||
Object.assign(currentPaths, sourcePaths);
|
||||
}
|
||||
|
||||
targetTsconfig.compilerOptions = targetTsconfig.compilerOptions || {};
|
||||
targetTsconfig.compilerOptions.paths = currentPaths;
|
||||
|
||||
writeJson(targetTsconfigPath, targetTsconfig);
|
||||
console.log(
|
||||
`${mode === 'clean' ? 'Cleaned' : 'Synced'} enterprise tsconfig paths in ${targetTsconfigPath}`,
|
||||
);
|
||||
};
|
||||
|
||||
if (mode === 'clean') {
|
||||
if (strippedContent !== currentContent) {
|
||||
fs.writeFileSync(targetGlobalPath, strippedContent);
|
||||
console.log(`Cleaned enterprise plugin block from ${targetGlobalPath}`);
|
||||
} else {
|
||||
console.log(`Enterprise plugin block already absent: ${targetGlobalPath}`);
|
||||
}
|
||||
} else {
|
||||
const updatedContent = `${strippedContent.trimEnd()}${blockContent}`;
|
||||
if (updatedContent !== currentContent) {
|
||||
fs.writeFileSync(targetGlobalPath, updatedContent);
|
||||
console.log(`Appended enterprise plugin block to ${targetGlobalPath}`);
|
||||
} else {
|
||||
console.log(`Enterprise plugin block already up to date: ${targetGlobalPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
syncPackageJson();
|
||||
syncTsconfig();
|
||||
+26
-2
@@ -1,6 +1,9 @@
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom } from '@/atoms/user';
|
||||
import { setAtomStorage } from '@/atoms/utils';
|
||||
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||
import { getGPUStackPlugin } from '@/plugins';
|
||||
import { GPUStackPluginManager } from '@/plugins/manager';
|
||||
import { mergeEnterpriseRoutes } from '@/plugins/route-merger';
|
||||
import { requestConfig } from '@/request-config';
|
||||
import {
|
||||
queryCurrentUserState,
|
||||
@@ -14,6 +17,7 @@ import {
|
||||
readState,
|
||||
writeState
|
||||
} from '@/utils/localstore/index';
|
||||
import '@gpustack/core-ui/style.css';
|
||||
import { RequestConfig, history } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
|
||||
@@ -33,8 +37,18 @@ const checkDefaultPage = async (userInfo: any) => {
|
||||
export async function getInitialState(): Promise<{
|
||||
fetchUserInfo: () => Promise<Global.UserInfo>;
|
||||
currentUser?: Global.UserInfo;
|
||||
pluginData?: Record<string, any>;
|
||||
}> {
|
||||
const { location } = history;
|
||||
const enterprisePlugin = getGPUStackPlugin();
|
||||
|
||||
// initialize plugins and merge enterprise locales
|
||||
let pluginData = {};
|
||||
try {
|
||||
pluginData = await GPUStackPluginManager.initialize();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize plugins:', error);
|
||||
}
|
||||
|
||||
const getUpdateCheck = async () => {
|
||||
try {
|
||||
@@ -103,11 +117,13 @@ export async function getInitialState(): Promise<{
|
||||
checkDefaultPage(userInfo);
|
||||
return {
|
||||
fetchUserInfo,
|
||||
currentUser: userInfo
|
||||
currentUser: userInfo,
|
||||
pluginData
|
||||
};
|
||||
}
|
||||
return {
|
||||
fetchUserInfo
|
||||
fetchUserInfo,
|
||||
pluginData
|
||||
};
|
||||
}
|
||||
|
||||
@@ -115,3 +131,11 @@ export const request: RequestConfig = {
|
||||
baseURL: `/${GPUSTACK_API_BASE_URL}`,
|
||||
...requestConfig
|
||||
};
|
||||
|
||||
/**
|
||||
* 动态修改路由
|
||||
* @param routes 路由配置
|
||||
*/
|
||||
export function patchClientRoutes({ routes }: any) {
|
||||
mergeEnterpriseRoutes(routes);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
InfiniteScroller,
|
||||
ResizeContainer,
|
||||
TemplateCardSkeleton
|
||||
TemplateCardSkeleton,
|
||||
useScrollerContext
|
||||
} from '@gpustack/core-ui';
|
||||
import { useScrollerContext } from '@gpustack/core-ui/lib/components/infinite-scroller/use-scroller-context';
|
||||
import { Spin } from 'antd';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const COLOR_PRIMARY = '#007BFF';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { COLOR_PRIMARY } from './index';
|
||||
import { COLOR_PRIMARY } from './constants';
|
||||
|
||||
export default {
|
||||
'root-entry-name': 'variable',
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { COLOR_PRIMARY } from './constants';
|
||||
import dark from './dark';
|
||||
import light from './light';
|
||||
|
||||
export const COLOR_PRIMARY = '#007BFF';
|
||||
|
||||
export default {
|
||||
light,
|
||||
dark,
|
||||
colorPrimary: COLOR_PRIMARY
|
||||
};
|
||||
|
||||
export { COLOR_PRIMARY };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { COLOR_PRIMARY } from './index';
|
||||
import { COLOR_PRIMARY } from './constants';
|
||||
|
||||
export default {
|
||||
'root-entry-name': 'variable',
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { userSettingsHelperAtom } from '@/atoms/settings';
|
||||
import { getAtomStorage, setAtomStorage } from '@/atoms/utils';
|
||||
|
||||
export default function useUserSettingsStorage() {
|
||||
const setStorageUserSettings = (value: Record<string, any>) => {
|
||||
setAtomStorage(userSettingsHelperAtom, value || {});
|
||||
};
|
||||
|
||||
const getStorageUserSettings = () => {
|
||||
return getAtomStorage(userSettingsHelperAtom);
|
||||
};
|
||||
|
||||
return {
|
||||
setStorageUserSettings,
|
||||
getStorageUserSettings
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { userSettingsHelperAtom } from '@/atoms/settings';
|
||||
import themeConfig from '@/config/theme';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { userSettingsHelperAtom } from '../atoms/settings';
|
||||
import themeConfig from '../config/theme';
|
||||
|
||||
type Theme = 'light' | 'realDark' | 'auto';
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import VersionInfo, { modalConfig } from '@/components/version-info';
|
||||
import externalLinks from '@/constants/external-links';
|
||||
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||
import { logout } from '@/pages/login/apis';
|
||||
import { getGPUStackPlugin } from '@/plugins';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import {
|
||||
DiscordOutlined,
|
||||
@@ -105,6 +106,7 @@ const CustomItem = styled.div`
|
||||
|
||||
export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
const { isDarkTheme } = props;
|
||||
const pluginManager = getGPUStackPlugin();
|
||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [version] = useAtom(GPUStackVersionAtom);
|
||||
@@ -117,6 +119,7 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
};
|
||||
|
||||
const { initialState } = initialInfo;
|
||||
console.log('plugin+++++++++', pluginManager);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -291,6 +294,18 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
</NewLabel>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
history.push('/settings');
|
||||
}}
|
||||
style={{
|
||||
color: 'var(--ant-color-text-tertiary)'
|
||||
}}
|
||||
>
|
||||
<IconFont type="icon-settings-02" style={{ fontSize: 18 }} />
|
||||
</Button>
|
||||
<DropdownActions menu={{ ...helpMenu }} popupRender={helpPopupRender}>
|
||||
<IconWrapper>
|
||||
<IconFont
|
||||
|
||||
+17
-3
@@ -3,8 +3,10 @@ import { userAtom } from '@/atoms/user';
|
||||
import DarkMask from '@/components/dark-mask';
|
||||
import routeCachekey from '@/config/route-cachekey';
|
||||
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||
import { COLOR_PRIMARY } from '@/config/theme';
|
||||
import useOverlayScroller from '@/hooks/use-overlay-scroller';
|
||||
import useUserSettings from '@/hooks/use-user-settings';
|
||||
import useUserSettingsStorage from '@/hooks/use-user-settings-storage';
|
||||
import useAddResource from '@/pages/dashboard/hooks/use-add-resource';
|
||||
import { logout } from '@/pages/login/apis';
|
||||
import {
|
||||
@@ -128,6 +130,7 @@ export default (props: any) => {
|
||||
});
|
||||
const [, contextHolder] = Modal.useModal();
|
||||
const { themeData, setUserSettings, userSettings } = useUserSettings();
|
||||
const userSettingsStorage = useUserSettingsStorage();
|
||||
const [userInfo] = useAtom(userAtom);
|
||||
const [routeCache] = useAtom(routeCacheAtom);
|
||||
const location = useLocation();
|
||||
@@ -205,7 +208,7 @@ export default (props: any) => {
|
||||
|
||||
const role = initialState?.currentUser?.is_admin ? 'admin' : 'user';
|
||||
const [route] = useAccessMarkedRoutes(mapRoutes(newRoutes, role));
|
||||
|
||||
console.log('route++++++++', route, clientRoutes);
|
||||
patchRoutes({
|
||||
routes: route.children,
|
||||
initialState: initialInfo.initialState
|
||||
@@ -363,7 +366,13 @@ export default (props: any) => {
|
||||
apiBaseUrl: GPUSTACK_API_BASE_URL,
|
||||
theme: userSettings.theme,
|
||||
iconUrl: '//at.alicdn.com/t/c/font_4613488_8fi68fmt1th.js',
|
||||
isDarkTheme: userSettings.isDarkTheme
|
||||
isDarkTheme: userSettings.isDarkTheme,
|
||||
defaultColorPrimary: COLOR_PRIMARY
|
||||
}}
|
||||
hooks={{
|
||||
useUserSettings: useUserSettings,
|
||||
useUserSettingsStorage: () => userSettingsStorage,
|
||||
useIntl: useIntl
|
||||
}}
|
||||
i18n={intl}
|
||||
locale={{
|
||||
@@ -371,7 +380,12 @@ export default (props: any) => {
|
||||
setLocale: setLocale
|
||||
}}
|
||||
services={{
|
||||
request: request
|
||||
request: request,
|
||||
router: {
|
||||
push: (path: string) => navigate(path),
|
||||
replace: (path: string) => navigate(path, { replace: true }),
|
||||
goBack: () => navigate(-1)
|
||||
}
|
||||
}}
|
||||
localStore={{
|
||||
readColumnSettings,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// columns.ts
|
||||
import { tableSorter } from '@/config/settings';
|
||||
import { AutoTooltip, DropdownButtons } from '@gpustack/core-ui';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { AutoTooltip, DropdownButtons, icons } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Tag } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
InfiniteScroller,
|
||||
ResizeContainer,
|
||||
TemplateCardSkeleton
|
||||
TemplateCardSkeleton,
|
||||
useScrollerContext
|
||||
} from '@gpustack/core-ui';
|
||||
import { useScrollerContext } from '@gpustack/core-ui/lib/components/infinite-scroller/use-scroller-context';
|
||||
import { Spin } from 'antd';
|
||||
import React from 'react';
|
||||
import backendListCss from '../styles/backend-list.less';
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
GPUDriverMap,
|
||||
ManufacturerMap
|
||||
} from '@/pages/resources/config/gpu-driver';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { icons } from '@gpustack/core-ui';
|
||||
import jsYaml from 'js-yaml';
|
||||
import { trim } from 'lodash';
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
import { DropdownButtons, IconFont, useDownloadLogs } from '@gpustack/core-ui';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import {
|
||||
DropdownButtons,
|
||||
IconFont,
|
||||
icons,
|
||||
useDownloadLogs
|
||||
} from '@gpustack/core-ui';
|
||||
import { BENCHMARKS_API } from '../apis';
|
||||
import { BenchmarkStatusValueMap } from '../config';
|
||||
import { BenchmarkListItem as ListItem } from '../config/types';
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { CollapsePanel, IconFont, ScrollSpyTabs } from '@gpustack/core-ui';
|
||||
import { useWrapperContext } from '@gpustack/core-ui/lib/components/column-wrapper/use-wrapper-context';
|
||||
import {
|
||||
CollapsePanel,
|
||||
IconFont,
|
||||
ScrollSpyTabs,
|
||||
useWrapperContext
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import {
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
FilterBar,
|
||||
IconFont,
|
||||
Table as SealTable,
|
||||
TableOrder,
|
||||
TableProvider
|
||||
} from '@gpustack/core-ui';
|
||||
import { TableOrder } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import { useIntl, useNavigate } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { message } from 'antd';
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { DropdownActions } from '@gpustack/core-ui';
|
||||
import ListMap from '@gpustack/core-ui/lib/components/dynamic-form/components/list-map';
|
||||
import { FieldSchema } from '@gpustack/core-ui/lib/components/dynamic-form/config/types';
|
||||
import { DropdownActions, type FieldSchema, ListMap } from '@gpustack/core-ui';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
|
||||
@@ -14,9 +14,9 @@ import {
|
||||
IconFont,
|
||||
LabelSelector,
|
||||
Select as SealSelect,
|
||||
useAppUtils
|
||||
useAppUtils,
|
||||
type CollapseContainerProps
|
||||
} from '@gpustack/core-ui';
|
||||
import { type CollapseContainerProps } from '@gpustack/core-ui/lib/components/collapse-container';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Button, Form } from 'antd';
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import ListMap from '@gpustack/core-ui/lib/components/dynamic-form/components/list-map';
|
||||
import { statusType } from '@gpustack/core-ui/lib/components/dynamic-form/config/types';
|
||||
import useValidateFields from '@gpustack/core-ui/lib/components/dynamic-form/hooks/use-validate-fields';
|
||||
import { ListMap, type statusType, useValidateFields } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React, { forwardRef, useState } from 'react';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FieldSchema } from '@gpustack/core-ui/lib/components/dynamic-form/config/types';
|
||||
import { type FieldSchema } from '@gpustack/core-ui';
|
||||
|
||||
export const fields = {
|
||||
volumes: {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { StatusMaps } from '@/config';
|
||||
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||
import { StatusType } from '@/config/types';
|
||||
import { GPUsConfigs } from '@/pages/resources/config/gpu-driver';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { icons } from '@gpustack/core-ui';
|
||||
|
||||
export const ClusterStatusValueMap = {
|
||||
Provisioning: 'provisioning',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { icons } from '@gpustack/core-ui';
|
||||
import React from 'react';
|
||||
import { ProviderValueMap } from '.';
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import {
|
||||
AutoTooltip,
|
||||
DropdownButtons,
|
||||
GrafanaIcon,
|
||||
StatusTag
|
||||
StatusTag,
|
||||
icons,
|
||||
type TableColumnProps as SealColumnProps
|
||||
} from '@gpustack/core-ui';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { ColumnProps as SealColumnProps } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Tooltip } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { AutoTooltip, DropdownButtons } from '@gpustack/core-ui';
|
||||
import { ColumnProps } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import {
|
||||
AutoTooltip,
|
||||
DropdownButtons,
|
||||
type TableColumnProps
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
@@ -27,7 +30,7 @@ const actionItems = [
|
||||
const usePoolsColumns = (
|
||||
handleSelect: (val: string, record: ListItem) => void,
|
||||
sortOrder?: string[]
|
||||
): ColumnProps[] => {
|
||||
): TableColumnProps[] => {
|
||||
const intl = useIntl();
|
||||
|
||||
return useMemo(() => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
InfiniteScroller,
|
||||
ResizeContainer,
|
||||
TemplateCardSkeleton
|
||||
TemplateCardSkeleton,
|
||||
useScrollerContext
|
||||
} from '@gpustack/core-ui';
|
||||
import { useScrollerContext } from '@gpustack/core-ui/lib/components/infinite-scroller/use-scroller-context';
|
||||
import { Spin } from 'antd';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
@@ -2,10 +2,10 @@ import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-bench
|
||||
import { DeleteOutlined, DownloadOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
DropdownButtons,
|
||||
type HandlerOptions,
|
||||
IconFont,
|
||||
useDownloadStream
|
||||
} from '@gpustack/core-ui';
|
||||
import { HandlerOptions } from '@gpustack/core-ui/lib/hooks/use-chunk-fetch';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Progress, notification } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||
import { SimpleTable, StatusTag } from '@gpustack/core-ui';
|
||||
import { type ColumnProps } from '@gpustack/core-ui/lib/components/simple-table';
|
||||
import { SimpleTable, StatusTag, type ColumnProps } from '@gpustack/core-ui';
|
||||
import { Progress, Tooltip } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { InstanceStatusMap, status } from '../../config';
|
||||
|
||||
@@ -18,9 +18,9 @@ import {
|
||||
DropdownActions,
|
||||
DropdownButtons,
|
||||
PageTools,
|
||||
Table as SealTable
|
||||
Table as SealTable,
|
||||
TableOrder
|
||||
} from '@gpustack/core-ui';
|
||||
import { TableOrder } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
||||
import { useMemoizedFn, useToggle } from 'ahooks';
|
||||
import { Button, Space, message } from 'antd';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import HotKeys from '@/config/hotkeys';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { icons } from '@gpustack/core-ui';
|
||||
import React from 'react';
|
||||
import { modelCategoriesMap, modelSourceMap } from './index';
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ import useSetChunkRequest from '@/hooks/use-chunk-request';
|
||||
import { usePaginationStatus } from '@/hooks/use-pagination-status';
|
||||
import { useTableMultiSort } from '@/hooks/use-table-sort';
|
||||
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
|
||||
import { TableProvider } from '@gpustack/core-ui';
|
||||
import { TableOrder } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import { TableOrder, TableProvider } from '@gpustack/core-ui';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import _ from 'lodash';
|
||||
import qs from 'query-string';
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { CollapsePanel, IconFont, ScrollSpyTabs } from '@gpustack/core-ui';
|
||||
import { useWrapperContext } from '@gpustack/core-ui/lib/components/column-wrapper/use-wrapper-context';
|
||||
import {
|
||||
CollapsePanel,
|
||||
IconFont,
|
||||
ScrollSpyTabs,
|
||||
useWrapperContext
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -3,9 +3,13 @@ import { systemConfigAtom } from '@/atoms/system';
|
||||
import { OPENAI_COMPATIBLE, tableSorter } from '@/config/settings';
|
||||
import { TargetStatusValueMap } from '@/pages/model-routes/config';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { AutoTooltip, DropdownButtons, GrafanaIcon } from '@gpustack/core-ui';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { ColumnProps } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import {
|
||||
AutoTooltip,
|
||||
DropdownButtons,
|
||||
GrafanaIcon,
|
||||
icons,
|
||||
type TableColumnProps
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Tooltip } from 'antd';
|
||||
@@ -98,7 +102,7 @@ const useModelsColumns = ({
|
||||
clusterList,
|
||||
sortOrder,
|
||||
targetList
|
||||
}: ModelsColumnsHookProps & { targetList: any[] }): ColumnProps[] => {
|
||||
}: ModelsColumnsHookProps & { targetList: any[] }): TableColumnProps[] => {
|
||||
const intl = useIntl();
|
||||
const systemConfig = useAtomValue(systemConfigAtom);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||
import { PageSize } from '@gpustack/core-ui/lib/components/logs-viewer/config';
|
||||
import { PageSize } from '@gpustack/core-ui';
|
||||
import { useState } from 'react';
|
||||
import { MODEL_INSTANCE_API } from '../apis';
|
||||
import { InstanceRealtimeLogStatus } from '../config';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StatusMaps } from '@/config';
|
||||
import { StatusType } from '@/config/types';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { icons } from '@gpustack/core-ui';
|
||||
import { ProviderEnum } from './providers';
|
||||
export { maasProviderLabelMap, maasProviderOptions } from './providers';
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { json2Yaml, yaml2Json } from '@/pages/backends/config';
|
||||
import { CollapsePanel, IconFont, ScrollSpyTabs } from '@gpustack/core-ui';
|
||||
import { useWrapperContext } from '@gpustack/core-ui/lib/components/column-wrapper/use-wrapper-context';
|
||||
import useFinishFailed from '@gpustack/core-ui/lib/components/scroll-spy-tabs/use-finish-failed';
|
||||
import useScrollActiveChange from '@gpustack/core-ui/lib/components/scroll-spy-tabs/use-scroll-active-change';
|
||||
import {
|
||||
CollapsePanel,
|
||||
IconFont,
|
||||
ScrollSpyTabs,
|
||||
useFinishFailed,
|
||||
useScrollActiveChange,
|
||||
useWrapperContext
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StatusMaps } from '@/config';
|
||||
import { StatusType } from '@/config/types';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import { icons } from '@gpustack/core-ui';
|
||||
|
||||
export const TargetStatusValueMap: Record<string, string> = {
|
||||
Active: 'active',
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { modelCategoriesMap } from '@/pages/llmodels/config';
|
||||
import { CollapsePanel, IconFont, ScrollSpyTabs } from '@gpustack/core-ui';
|
||||
import { useWrapperContext } from '@gpustack/core-ui/lib/components/column-wrapper/use-wrapper-context';
|
||||
import useFinishFailed from '@gpustack/core-ui/lib/components/scroll-spy-tabs/use-finish-failed';
|
||||
import useScrollActiveChange from '@gpustack/core-ui/lib/components/scroll-spy-tabs/use-scroll-active-change';
|
||||
import {
|
||||
CollapsePanel,
|
||||
IconFont,
|
||||
ScrollSpyTabs,
|
||||
useFinishFailed,
|
||||
useScrollActiveChange,
|
||||
useWrapperContext
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// columns.ts
|
||||
import { tableSorter } from '@/config/settings';
|
||||
import ModelTag from '@/pages/_components/model-tag';
|
||||
import { AutoTooltip, DropdownButtons } from '@gpustack/core-ui';
|
||||
import { type ColumnProps } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import {
|
||||
AutoTooltip,
|
||||
DropdownButtons,
|
||||
type TableColumnProps
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import dayjs from 'dayjs';
|
||||
import { useMemo } from 'react';
|
||||
@@ -11,7 +14,7 @@ import { RouteItem } from '../config/types';
|
||||
const useAccessColumns = (
|
||||
handleSelect: (val: string, record: RouteItem) => void,
|
||||
onCellClick?: (record: RouteItem, dataIndex: string) => void
|
||||
): ColumnProps[] => {
|
||||
): TableColumnProps[] => {
|
||||
const intl = useIntl();
|
||||
|
||||
const filterActions = (record: RouteItem) => {
|
||||
|
||||
@@ -11,9 +11,9 @@ import {
|
||||
FilterBar,
|
||||
IconFont,
|
||||
Table as SealTable,
|
||||
TableOrder,
|
||||
TableProvider
|
||||
} from '@gpustack/core-ui';
|
||||
import { TableOrder } from '@gpustack/core-ui/lib/components/table/types';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { message } from 'antd';
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
AlertInfo,
|
||||
ImageEditor as CanvasImageEditor,
|
||||
IconFont,
|
||||
processImage,
|
||||
SingleImage
|
||||
} from '@gpustack/core-ui';
|
||||
import { processImage } from '@gpustack/core-ui/lib/components/image-editor/extract-image-colors';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Divider } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { getGPUStackPlugin } from '@/plugins';
|
||||
import { Result } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const SettingsPage: React.FC = () => {
|
||||
const enterprisePlugin = getGPUStackPlugin();
|
||||
const BrandingUI = enterprisePlugin?.components?.BrandingUI;
|
||||
|
||||
if (!BrandingUI) {
|
||||
return (
|
||||
<Result
|
||||
status="404"
|
||||
title="404"
|
||||
subTitle="Enterprise settings page is unavailable."
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <BrandingUI />;
|
||||
};
|
||||
|
||||
export default SettingsPage;
|
||||
@@ -1,7 +1,11 @@
|
||||
// columns.ts
|
||||
import { tableSorter } from '@/config/settings';
|
||||
import { AutoTooltip, DropdownButtons, IconFont } from '@gpustack/core-ui';
|
||||
import icons from '@gpustack/core-ui/lib/components/icon-font/icons';
|
||||
import {
|
||||
AutoTooltip,
|
||||
DropdownButtons,
|
||||
IconFont,
|
||||
icons
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl, useModel } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Tag } from 'antd';
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { GPUStackPluginManager } from './manager';
|
||||
import { AppPlugin } from './types';
|
||||
|
||||
/**
|
||||
* Hook to access the enterprise plugin instance
|
||||
*/
|
||||
export const getGPUStackPlugin = (): AppPlugin | undefined => {
|
||||
return GPUStackPluginManager.get('enterprise');
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to access a plugin by its name
|
||||
*/
|
||||
export const getPlugin = (name: string): AppPlugin | undefined => {
|
||||
return GPUStackPluginManager.get(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to check if a plugin is registered
|
||||
*/
|
||||
export const hasPlugin = (name: string): boolean => {
|
||||
return GPUStackPluginManager.has(name);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { addLocale } from '@umijs/max';
|
||||
|
||||
const mergedLocales = new Set<string>();
|
||||
|
||||
/**
|
||||
* Merge enterprise plugin locales into the main application
|
||||
*/
|
||||
export function mergeEnterpriseLocales(locales: Record<string, any> = {}) {
|
||||
if (!locales) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Merging enterprise plugin locales:', Object.keys(locales));
|
||||
|
||||
// Iterate over all locale configurations of the enterprise plugin
|
||||
Object.entries(locales).forEach(([locale, messages]) => {
|
||||
try {
|
||||
if (mergedLocales.has(locale)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Merge the enterprise locale into the main application
|
||||
addLocale(locale, messages, {
|
||||
momentLocale: '',
|
||||
// @ts-ignore
|
||||
antd: locale
|
||||
});
|
||||
mergedLocales.add(locale);
|
||||
console.log(`✓ Merged enterprise locale: ${locale}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to merge enterprise locale ${locale}:`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { AppPlugin, IPluginManager } from './types';
|
||||
|
||||
class PluginManager implements IPluginManager {
|
||||
private plugins = new Map<string, AppPlugin>();
|
||||
private initialized = false;
|
||||
|
||||
/**
|
||||
* register a plugin
|
||||
*/
|
||||
register(name: string, plugin: AppPlugin) {
|
||||
if (this.plugins.has(name)) {
|
||||
console.warn(`Plugin "${name}" is already registered. Overwriting...`);
|
||||
}
|
||||
this.plugins.set(name, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a plugin
|
||||
*/
|
||||
get(name: string): AppPlugin | undefined {
|
||||
return this.plugins.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a plugin exists
|
||||
*/
|
||||
has(name: string): boolean {
|
||||
return this.plugins.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all plugins
|
||||
*/
|
||||
getAll(): Map<string, AppPlugin> {
|
||||
return this.plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize all plugins
|
||||
*/
|
||||
async initialize(): Promise<Record<string, any>> {
|
||||
if (this.initialized) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const initData: Record<string, any> = {};
|
||||
|
||||
for (const [name, plugin] of this.plugins.entries()) {
|
||||
if (plugin.onAppInit) {
|
||||
try {
|
||||
const data = await plugin.onAppInit();
|
||||
if (data) {
|
||||
initData[name] = data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to initialize plugin "${name}":`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
return initData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知所有插件应用已就绪
|
||||
*/
|
||||
async notifyReady(): Promise<void> {
|
||||
for (const [name, plugin] of this.plugins.entries()) {
|
||||
if (plugin.onAppReady) {
|
||||
try {
|
||||
await plugin.onAppReady();
|
||||
} catch (error) {
|
||||
console.error(`Plugin "${name}" onAppReady failed:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const GPUStackPluginManager = new PluginManager();
|
||||
@@ -0,0 +1,2 @@
|
||||
// If the enterprise plugin is not installed, imports will be redirected to this empty module to prevent errors.
|
||||
export default undefined;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { getGPUStackPlugin } from '@/plugins';
|
||||
import type { RouteConfig } from '@/plugins/types';
|
||||
|
||||
type ClientRoute = RouteConfig & {
|
||||
id?: string;
|
||||
children?: ClientRoute[];
|
||||
routes?: ClientRoute[];
|
||||
};
|
||||
|
||||
function getLayoutChildren(routes: ClientRoute[]): ClientRoute[] | null {
|
||||
const rootRoute = routes.find((route) => route.path === '/');
|
||||
if (!rootRoute) return null;
|
||||
|
||||
const rootChildren = rootRoute.routes || rootRoute.children;
|
||||
if (!Array.isArray(rootChildren)) return null;
|
||||
|
||||
const globalLayout = rootChildren.find((route) =>
|
||||
route.id?.includes('global-layout')
|
||||
);
|
||||
if (!globalLayout) return null;
|
||||
|
||||
const layoutChildren = globalLayout.children || globalLayout.routes;
|
||||
return Array.isArray(layoutChildren) ? layoutChildren : null;
|
||||
}
|
||||
|
||||
function mergeRoute(targetRoutes: ClientRoute[], pluginRoute: ClientRoute) {
|
||||
const existsIndex = targetRoutes.findIndex(
|
||||
(route) =>
|
||||
(pluginRoute.path && route.path === pluginRoute.path) ||
|
||||
(pluginRoute.key && route.key === pluginRoute.key)
|
||||
);
|
||||
|
||||
if (existsIndex === -1) {
|
||||
targetRoutes.push(pluginRoute);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentRoute = targetRoutes[existsIndex];
|
||||
targetRoutes[existsIndex] = {
|
||||
...currentRoute,
|
||||
...pluginRoute,
|
||||
routes: pluginRoute.routes || currentRoute.routes,
|
||||
children: pluginRoute.children || currentRoute.children
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeEnterpriseRoutes(routes: ClientRoute[]) {
|
||||
const enterprisePlugin = getGPUStackPlugin();
|
||||
if (!enterprisePlugin?.routes?.length) return;
|
||||
|
||||
const layoutChildren = getLayoutChildren(routes);
|
||||
if (!layoutChildren) return;
|
||||
|
||||
enterprisePlugin.routes.forEach((route) =>
|
||||
mergeRoute(layoutChildren, route as ClientRoute)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
/**
|
||||
* routes
|
||||
*/
|
||||
export interface RouteConfig {
|
||||
path: string;
|
||||
component?: ComponentType;
|
||||
routes?: RouteConfig[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* login module configuration
|
||||
*/
|
||||
export interface LoginPlugin {
|
||||
shouldUseCustomLogin?: () => boolean;
|
||||
CustomLoginComponent?: ComponentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* branding module configuration
|
||||
*/
|
||||
export interface BrandingPlugin {
|
||||
ConfigPage?: ComponentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* localization configuration
|
||||
*/
|
||||
export interface LocalesConfig {
|
||||
[locale: string]: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* application plugin interface
|
||||
*/
|
||||
export interface AppPlugin {
|
||||
/**
|
||||
* application initialization hook
|
||||
* called when the application starts, can return initialization data
|
||||
*/
|
||||
onAppInit?: () => Promise<Record<string, any>> | Record<string, any>;
|
||||
|
||||
/**
|
||||
* application ready hook
|
||||
* called when the application is fully loaded
|
||||
*/
|
||||
onAppReady?: () => void | Promise<void>;
|
||||
|
||||
/**
|
||||
* login module
|
||||
*/
|
||||
login?: LoginPlugin;
|
||||
|
||||
/**
|
||||
* branding module
|
||||
*/
|
||||
branding?: BrandingPlugin;
|
||||
|
||||
/**
|
||||
* localization configuration
|
||||
*/
|
||||
locales?: LocalesConfig;
|
||||
|
||||
/**
|
||||
* routes extension
|
||||
*/
|
||||
routes?: RouteConfig[];
|
||||
|
||||
/**
|
||||
* components extension
|
||||
*/
|
||||
components?: Record<string, ComponentType>;
|
||||
|
||||
/**
|
||||
* Hooks extension
|
||||
*/
|
||||
hooks?: Record<string, (...args: unknown[]) => unknown>;
|
||||
|
||||
/**
|
||||
* other custom fields for future extensions
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* plugin manager interface
|
||||
*/
|
||||
export interface IPluginManager {
|
||||
register(name: string, plugin: AppPlugin): void;
|
||||
get(name: string): AppPlugin | undefined;
|
||||
has(name: string): boolean;
|
||||
getAll(): Map<string, AppPlugin>;
|
||||
}
|
||||
+2
-16
@@ -17,22 +17,8 @@
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@@/*": ["./src/.umi/*"],
|
||||
"@@test/*": ["./src/.umi-test/*"],
|
||||
"@gpustack/core-ui": ["../core-ui/src/index.ts"],
|
||||
"@gpustack/core-ui/*": ["../core-ui/src/*"],
|
||||
"@gpustack/core-ui/hooks": ["../core-ui/src/lib/hooks"],
|
||||
"@gpustack/core-ui/utils": ["../core-ui/src/lib/utils"],
|
||||
"@gpustack/core-ui/types": ["../core-ui/src/lib/types"],
|
||||
"@gpustack/core-ui/components": ["../core-ui/src/lib/components"],
|
||||
"@gpustack/core-ui/lib/*": ["../core-ui/src/lib/*"]
|
||||
"@@test/*": ["./src/.umi-test/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./**/*.d.ts",
|
||||
"./**/*.ts",
|
||||
"./**/*.tsx",
|
||||
"@gpustack/core-ui/lib/components/logs-viewer/parse-worker.ts",
|
||||
"@gpustack/core-ui/lib/components/image-editor/invert-worker.ts",
|
||||
"@gpustack/core-ui/lib/components/image-editor/offscreen-worker.ts"
|
||||
]
|
||||
"include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user