diff --git a/web/src/assets/svg/es.svg b/web/src/assets/svg/es.svg
new file mode 100644
index 0000000000..b3274ca5c9
--- /dev/null
+++ b/web/src/assets/svg/es.svg
@@ -0,0 +1,24 @@
+
\ No newline at end of file
diff --git a/web/src/assets/svg/minio.svg b/web/src/assets/svg/minio.svg
new file mode 100644
index 0000000000..23dd1072e7
--- /dev/null
+++ b/web/src/assets/svg/minio.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/web/src/assets/svg/mysql.svg b/web/src/assets/svg/mysql.svg
new file mode 100644
index 0000000000..05dfb70d89
--- /dev/null
+++ b/web/src/assets/svg/mysql.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/web/src/assets/svg/redis.svg b/web/src/assets/svg/redis.svg
new file mode 100644
index 0000000000..821e5bd9b1
--- /dev/null
+++ b/web/src/assets/svg/redis.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/web/src/constants/setting.ts b/web/src/constants/setting.ts
index 7a60477128..e27244ff08 100644
--- a/web/src/constants/setting.ts
+++ b/web/src/constants/setting.ts
@@ -4,6 +4,7 @@ export enum UserSettingRouteKey {
Profile = 'profile',
Password = 'password',
Model = 'model',
+ System = 'system',
Team = 'team',
Logout = 'logout',
}
@@ -12,6 +13,7 @@ export const UserSettingRouteMap = {
[UserSettingRouteKey.Profile]: 'Profile',
[UserSettingRouteKey.Password]: 'Password',
[UserSettingRouteKey.Model]: 'Model Providers',
+ [UserSettingRouteKey.System]: 'System Version',
[UserSettingRouteKey.Team]: 'Team',
[UserSettingRouteKey.Logout]: 'Log out',
};
diff --git a/web/src/hooks/userSettingHook.ts b/web/src/hooks/userSettingHook.ts
index 79f138121d..d2499828db 100644
--- a/web/src/hooks/userSettingHook.ts
+++ b/web/src/hooks/userSettingHook.ts
@@ -1,7 +1,8 @@
import { ITenantInfo } from '@/interfaces/database/knowledge';
-import { IUserInfo } from '@/interfaces/database/userSetting';
+import { ISystemStatus, IUserInfo } from '@/interfaces/database/userSetting';
+import userService from '@/services/userService';
import authorizationUtil from '@/utils/authorizationUtil';
-import { useCallback, useEffect, useMemo } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import { history, useDispatch, useSelector } from 'umi';
export const useFetchUserInfo = () => {
@@ -92,3 +93,41 @@ export const useSaveSetting = () => {
return saveSetting;
};
+
+export const useFetchSystemVersion = () => {
+ const [version, setVersion] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ const fetchSystemVersion = useCallback(async () => {
+ setLoading(true);
+ const { data } = await userService.getSystemVersion();
+ if (data.retcode === 0) {
+ setVersion(data.data);
+ setLoading(false);
+ }
+ }, []);
+
+ return { fetchSystemVersion, version, loading };
+};
+
+export const useFetchSystemStatus = () => {
+ const [systemStatus, setSystemStatus] = useState(
+ {} as ISystemStatus,
+ );
+ const [loading, setLoading] = useState(false);
+
+ const fetchSystemStatus = useCallback(async () => {
+ setLoading(true);
+ const { data } = await userService.getSystemStatus();
+ if (data.retcode === 0) {
+ setSystemStatus(data.data);
+ setLoading(false);
+ }
+ }, []);
+
+ return {
+ systemStatus,
+ fetchSystemStatus,
+ loading,
+ };
+};
diff --git a/web/src/interfaces/database/userSetting.ts b/web/src/interfaces/database/userSetting.ts
index 76a61fee4d..c99ed5d9e7 100644
--- a/web/src/interfaces/database/userSetting.ts
+++ b/web/src/interfaces/database/userSetting.ts
@@ -19,3 +19,31 @@ export interface IUserInfo {
update_date: string;
update_time: number;
}
+
+export interface ISystemStatus {
+ es: Es;
+ minio: Minio;
+ mysql: Minio;
+ redis: Redis;
+}
+
+interface Redis {
+ status: string;
+ elapsed: number;
+ error: string;
+ pending: number;
+}
+
+export interface Minio {
+ status: string;
+ elapsed: number;
+ error: string;
+}
+
+interface Es {
+ status: string;
+ elapsed: number;
+ error: string;
+ number_of_nodes: number;
+ active_shards: number;
+}
diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts
index c3089bca56..a941b96634 100644
--- a/web/src/locales/en.ts
+++ b/web/src/locales/en.ts
@@ -393,6 +393,7 @@ export default {
model: 'Model Providers',
modelDescription: 'Set the model parameter and API Key here.',
team: 'Team',
+ system: 'System',
logout: 'Log out',
username: 'Username',
usernameMessage: 'Please input your username!',
diff --git a/web/src/locales/zh-traditional.ts b/web/src/locales/zh-traditional.ts
index 7b06ffaf3b..8b7d7708ca 100644
--- a/web/src/locales/zh-traditional.ts
+++ b/web/src/locales/zh-traditional.ts
@@ -364,6 +364,7 @@ export default {
modelDescription: '在此設置模型參數和 API Key。',
team: '團隊',
logout: '登出',
+ system: '系統',
username: '使用者名稱',
usernameMessage: '請輸入用戶名',
photo: '頭像',
diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts
index bdf035b7fb..b85d4b031f 100644
--- a/web/src/locales/zh.ts
+++ b/web/src/locales/zh.ts
@@ -380,6 +380,7 @@ export default {
model: '模型提供商',
modelDescription: '在此设置模型参数和 API Key。',
team: '团队',
+ system: '系统',
logout: '登出',
username: '用户名',
usernameMessage: '请输入用户名',
diff --git a/web/src/pages/user-setting/constants.tsx b/web/src/pages/user-setting/constants.tsx
index d91843c2a7..e812ebaf57 100644
--- a/web/src/pages/user-setting/constants.tsx
+++ b/web/src/pages/user-setting/constants.tsx
@@ -4,11 +4,13 @@ import { ReactComponent as PasswordIcon } from '@/assets/svg/password.svg';
import { ReactComponent as ProfileIcon } from '@/assets/svg/profile.svg';
import { ReactComponent as TeamIcon } from '@/assets/svg/team.svg';
import { UserSettingRouteKey } from '@/constants/setting';
+import { MonitorOutlined } from '@ant-design/icons';
export const UserSettingIconMap = {
[UserSettingRouteKey.Profile]: ,
[UserSettingRouteKey.Password]: ,
[UserSettingRouteKey.Model]: ,
+ [UserSettingRouteKey.System]: ,
[UserSettingRouteKey.Team]: ,
[UserSettingRouteKey.Logout]: ,
};
diff --git a/web/src/pages/user-setting/index.less b/web/src/pages/user-setting/index.less
index 2332db195e..baae0eebbd 100644
--- a/web/src/pages/user-setting/index.less
+++ b/web/src/pages/user-setting/index.less
@@ -3,6 +3,8 @@
.outletWrapper {
padding: 32px 32px 0;
+ height: 100%;
+ overflow-y: auto;
}
.itemDescription {
diff --git a/web/src/pages/user-setting/setting-system/index.less b/web/src/pages/user-setting/setting-system/index.less
new file mode 100644
index 0000000000..365a8ed54b
--- /dev/null
+++ b/web/src/pages/user-setting/setting-system/index.less
@@ -0,0 +1,20 @@
+.systemInfo {
+ width: 100%;
+ .title {
+ font-size: 20px;
+ font-weight: 600;
+ }
+ .text {
+ height: 26px;
+ line-height: 26px;
+ }
+ .badge {
+ :global(.ant-badge-status-dot) {
+ width: 10px;
+ height: 10px;
+ }
+ }
+ .error {
+ color: red;
+ }
+}
diff --git a/web/src/pages/user-setting/setting-system/index.tsx b/web/src/pages/user-setting/setting-system/index.tsx
new file mode 100644
index 0000000000..cee68d07bd
--- /dev/null
+++ b/web/src/pages/user-setting/setting-system/index.tsx
@@ -0,0 +1,94 @@
+import SvgIcon from '@/components/svg-icon';
+import { useFetchSystemStatus } from '@/hooks/userSettingHook';
+import { ISystemStatus, Minio } from '@/interfaces/database/userSetting';
+import { Badge, Card, Flex, Spin, Typography } from 'antd';
+import classNames from 'classnames';
+import lowerCase from 'lodash/lowerCase';
+import upperFirst from 'lodash/upperFirst';
+import { useEffect } from 'react';
+
+import { toFixed } from '@/utils/commonUtil';
+import styles from './index.less';
+
+const { Text } = Typography;
+
+enum Status {
+ 'green' = 'success',
+ 'red' = 'error',
+ 'yellow' = 'warning',
+}
+
+const TitleMap = {
+ es: 'Elasticsearch',
+ minio: 'MinIO Object Storage',
+ redis: 'Redis',
+ mysql: 'Mysql',
+};
+
+const SystemInfo = () => {
+ const {
+ systemStatus,
+ fetchSystemStatus,
+ loading: statusLoading,
+ } = useFetchSystemStatus();
+
+ useEffect(() => {
+ fetchSystemStatus();
+ }, [fetchSystemStatus]);
+
+ return (
+
+
+
+ {Object.keys(systemStatus).map((key) => {
+ const info = systemStatus[key as keyof ISystemStatus];
+
+ return (
+
+
+
+ {TitleMap[key as keyof typeof TitleMap]}
+
+
+
+ }
+ key={key}
+ >
+ {Object.keys(info)
+ .filter((x) => x !== 'status')
+ .map((x) => {
+ return (
+
+ {upperFirst(lowerCase(x))}:
+
+ {toFixed(info[x as keyof Minio]) as any}
+ {x === 'elapsed' && ' ms'}
+
+
+ );
+ })}
+
+ );
+ })}
+
+
+
+ );
+};
+
+export default SystemInfo;
diff --git a/web/src/pages/user-setting/sidebar/index.less b/web/src/pages/user-setting/sidebar/index.less
index a72d58c3be..42f3204d09 100644
--- a/web/src/pages/user-setting/sidebar/index.less
+++ b/web/src/pages/user-setting/sidebar/index.less
@@ -1,3 +1,6 @@
.sideBarWrapper {
padding-top: 32px;
+ .version {
+ color: rgb(17, 206, 17);
+ }
}
diff --git a/web/src/pages/user-setting/sidebar/index.tsx b/web/src/pages/user-setting/sidebar/index.tsx
index 0945e31e11..03764450ea 100644
--- a/web/src/pages/user-setting/sidebar/index.tsx
+++ b/web/src/pages/user-setting/sidebar/index.tsx
@@ -1,7 +1,7 @@
import { useSecondPathName } from '@/hooks/routeHook';
import type { MenuProps } from 'antd';
-import { Menu } from 'antd';
-import React, { useMemo } from 'react';
+import { Flex, Menu } from 'antd';
+import React, { useEffect, useMemo } from 'react';
import { useNavigate } from 'umi';
import {
UserSettingBaseKey,
@@ -10,7 +10,7 @@ import {
} from '../constants';
import { useTranslate } from '@/hooks/commonHooks';
-import { useLogout } from '@/hooks/userSettingHook';
+import { useFetchSystemVersion, useLogout } from '@/hooks/userSettingHook';
import styles from './index.less';
type MenuItem = Required['items'][number];
@@ -20,6 +20,11 @@ const SideBar = () => {
const pathName = useSecondPathName();
const logout = useLogout();
const { t } = useTranslate('setting');
+ const { version, fetchSystemVersion } = useFetchSystemVersion();
+
+ useEffect(() => {
+ fetchSystemVersion();
+ }, [fetchSystemVersion]);
function getItem(
label: string,
@@ -32,7 +37,14 @@ const SideBar = () => {
key,
icon,
children,
- label: t(label),
+ label: (
+
+ {t(label)}
+
+ {label === 'system' && version}
+
+
+ ),
type,
} as MenuItem;
}
diff --git a/web/src/routes.ts b/web/src/routes.ts
index 4ab6134bde..965ddf6320 100644
--- a/web/src/routes.ts
+++ b/web/src/routes.ts
@@ -78,6 +78,10 @@ const routes = [
path: '/user-setting/team',
component: '@/pages/user-setting/setting-team',
},
+ {
+ path: '/user-setting/system',
+ component: '@/pages/user-setting/setting-system',
+ },
],
},
{
diff --git a/web/src/services/userService.ts b/web/src/services/userService.ts
index d7ca37f172..a4931942c6 100644
--- a/web/src/services/userService.ts
+++ b/web/src/services/userService.ts
@@ -16,6 +16,8 @@ const {
set_tenant_info,
add_llm,
delete_llm,
+ getSystemStatus,
+ getSystemVersion,
} = api;
const methods = {
@@ -71,6 +73,14 @@ const methods = {
url: delete_llm,
method: 'post',
},
+ getSystemStatus: {
+ url: getSystemStatus,
+ method: 'get',
+ },
+ getSystemVersion: {
+ url: getSystemVersion,
+ method: 'get',
+ },
} as const;
const userService = registerServer(methods, request);
diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts
index 1346181eb4..fe083d6751 100644
--- a/web/src/utils/api.ts
+++ b/web/src/utils/api.ts
@@ -77,4 +77,8 @@ export default {
createFolder: `${api_host}/file/create`,
connectFileToKnowledge: `${api_host}/file2document/convert`,
getFile: `${api_host}/file/get`,
+
+ // system
+ getSystemVersion: `${api_host}/system/version`,
+ getSystemStatus: `${api_host}/system/status`,
};
diff --git a/web/src/utils/commonUtil.ts b/web/src/utils/commonUtil.ts
index fa95559f53..f3d0aef634 100644
--- a/web/src/utils/commonUtil.ts
+++ b/web/src/utils/commonUtil.ts
@@ -65,3 +65,10 @@ export const filterOptionsByInput = (
input: string,
option: { label: string; value: string } | undefined,
) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
+
+export const toFixed = (value: unknown, fixed = 2) => {
+ if (typeof value === 'number') {
+ return value.toFixed(fixed);
+ }
+ return value;
+};
diff --git a/web/src/utils/registerServer.ts b/web/src/utils/registerServer.ts
index ade4eb69f0..02ae2b1f58 100644
--- a/web/src/utils/registerServer.ts
+++ b/web/src/utils/registerServer.ts
@@ -9,7 +9,7 @@ const registerServer = (
) => {
const server: Service = {} as Service;
for (let key in opt) {
- server[key] = (params: any, urlAppendix?: string) => {
+ server[key] = (params?: any, urlAppendix?: string) => {
let url = opt[key].url;
const requestOptions = opt[key];
if (urlAppendix) {