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) {