balibabu commited on
Commit
bf1e3ff
·
1 Parent(s): d219eb3

feat: display the version and backend service status on the page (#848)

Browse files

### What problem does this PR solve?

#643 feat: display the version and backend service status on the page

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/assets/svg/es.svg ADDED
web/src/assets/svg/minio.svg ADDED
web/src/assets/svg/mysql.svg ADDED
web/src/assets/svg/redis.svg ADDED
web/src/constants/setting.ts CHANGED
@@ -4,6 +4,7 @@ export enum UserSettingRouteKey {
4
  Profile = 'profile',
5
  Password = 'password',
6
  Model = 'model',
 
7
  Team = 'team',
8
  Logout = 'logout',
9
  }
@@ -12,6 +13,7 @@ export const UserSettingRouteMap = {
12
  [UserSettingRouteKey.Profile]: 'Profile',
13
  [UserSettingRouteKey.Password]: 'Password',
14
  [UserSettingRouteKey.Model]: 'Model Providers',
 
15
  [UserSettingRouteKey.Team]: 'Team',
16
  [UserSettingRouteKey.Logout]: 'Log out',
17
  };
 
4
  Profile = 'profile',
5
  Password = 'password',
6
  Model = 'model',
7
+ System = 'system',
8
  Team = 'team',
9
  Logout = 'logout',
10
  }
 
13
  [UserSettingRouteKey.Profile]: 'Profile',
14
  [UserSettingRouteKey.Password]: 'Password',
15
  [UserSettingRouteKey.Model]: 'Model Providers',
16
+ [UserSettingRouteKey.System]: 'System Version',
17
  [UserSettingRouteKey.Team]: 'Team',
18
  [UserSettingRouteKey.Logout]: 'Log out',
19
  };
web/src/hooks/userSettingHook.ts CHANGED
@@ -1,7 +1,8 @@
1
  import { ITenantInfo } from '@/interfaces/database/knowledge';
2
- import { IUserInfo } from '@/interfaces/database/userSetting';
 
3
  import authorizationUtil from '@/utils/authorizationUtil';
4
- import { useCallback, useEffect, useMemo } from 'react';
5
  import { history, useDispatch, useSelector } from 'umi';
6
 
7
  export const useFetchUserInfo = () => {
@@ -92,3 +93,41 @@ export const useSaveSetting = () => {
92
 
93
  return saveSetting;
94
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { ITenantInfo } from '@/interfaces/database/knowledge';
2
+ import { ISystemStatus, IUserInfo } from '@/interfaces/database/userSetting';
3
+ import userService from '@/services/userService';
4
  import authorizationUtil from '@/utils/authorizationUtil';
5
+ import { useCallback, useEffect, useMemo, useState } from 'react';
6
  import { history, useDispatch, useSelector } from 'umi';
7
 
8
  export const useFetchUserInfo = () => {
 
93
 
94
  return saveSetting;
95
  };
96
+
97
+ export const useFetchSystemVersion = () => {
98
+ const [version, setVersion] = useState('');
99
+ const [loading, setLoading] = useState(false);
100
+
101
+ const fetchSystemVersion = useCallback(async () => {
102
+ setLoading(true);
103
+ const { data } = await userService.getSystemVersion();
104
+ if (data.retcode === 0) {
105
+ setVersion(data.data);
106
+ setLoading(false);
107
+ }
108
+ }, []);
109
+
110
+ return { fetchSystemVersion, version, loading };
111
+ };
112
+
113
+ export const useFetchSystemStatus = () => {
114
+ const [systemStatus, setSystemStatus] = useState<ISystemStatus>(
115
+ {} as ISystemStatus,
116
+ );
117
+ const [loading, setLoading] = useState(false);
118
+
119
+ const fetchSystemStatus = useCallback(async () => {
120
+ setLoading(true);
121
+ const { data } = await userService.getSystemStatus();
122
+ if (data.retcode === 0) {
123
+ setSystemStatus(data.data);
124
+ setLoading(false);
125
+ }
126
+ }, []);
127
+
128
+ return {
129
+ systemStatus,
130
+ fetchSystemStatus,
131
+ loading,
132
+ };
133
+ };
web/src/interfaces/database/userSetting.ts CHANGED
@@ -19,3 +19,31 @@ export interface IUserInfo {
19
  update_date: string;
20
  update_time: number;
21
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  update_date: string;
20
  update_time: number;
21
  }
22
+
23
+ export interface ISystemStatus {
24
+ es: Es;
25
+ minio: Minio;
26
+ mysql: Minio;
27
+ redis: Redis;
28
+ }
29
+
30
+ interface Redis {
31
+ status: string;
32
+ elapsed: number;
33
+ error: string;
34
+ pending: number;
35
+ }
36
+
37
+ export interface Minio {
38
+ status: string;
39
+ elapsed: number;
40
+ error: string;
41
+ }
42
+
43
+ interface Es {
44
+ status: string;
45
+ elapsed: number;
46
+ error: string;
47
+ number_of_nodes: number;
48
+ active_shards: number;
49
+ }
web/src/locales/en.ts CHANGED
@@ -393,6 +393,7 @@ export default {
393
  model: 'Model Providers',
394
  modelDescription: 'Set the model parameter and API Key here.',
395
  team: 'Team',
 
396
  logout: 'Log out',
397
  username: 'Username',
398
  usernameMessage: 'Please input your username!',
 
393
  model: 'Model Providers',
394
  modelDescription: 'Set the model parameter and API Key here.',
395
  team: 'Team',
396
+ system: 'System',
397
  logout: 'Log out',
398
  username: 'Username',
399
  usernameMessage: 'Please input your username!',
web/src/locales/zh-traditional.ts CHANGED
@@ -364,6 +364,7 @@ export default {
364
  modelDescription: '在此設置模型參數和 API Key。',
365
  team: '團隊',
366
  logout: '登出',
 
367
  username: '使用者名稱',
368
  usernameMessage: '請輸入用戶名',
369
  photo: '頭像',
 
364
  modelDescription: '在此設置模型參數和 API Key。',
365
  team: '團隊',
366
  logout: '登出',
367
+ system: '系統',
368
  username: '使用者名稱',
369
  usernameMessage: '請輸入用戶名',
370
  photo: '頭像',
web/src/locales/zh.ts CHANGED
@@ -380,6 +380,7 @@ export default {
380
  model: '模型提供商',
381
  modelDescription: '在此设置模型参数和 API Key。',
382
  team: '团队',
 
383
  logout: '登出',
384
  username: '用户名',
385
  usernameMessage: '请输入用户名',
 
380
  model: '模型提供商',
381
  modelDescription: '在此设置模型参数和 API Key。',
382
  team: '团队',
383
+ system: '系统',
384
  logout: '登出',
385
  username: '用户名',
386
  usernameMessage: '请输入用户名',
web/src/pages/user-setting/constants.tsx CHANGED
@@ -4,11 +4,13 @@ import { ReactComponent as PasswordIcon } from '@/assets/svg/password.svg';
4
  import { ReactComponent as ProfileIcon } from '@/assets/svg/profile.svg';
5
  import { ReactComponent as TeamIcon } from '@/assets/svg/team.svg';
6
  import { UserSettingRouteKey } from '@/constants/setting';
 
7
 
8
  export const UserSettingIconMap = {
9
  [UserSettingRouteKey.Profile]: <ProfileIcon />,
10
  [UserSettingRouteKey.Password]: <PasswordIcon />,
11
  [UserSettingRouteKey.Model]: <ModelIcon />,
 
12
  [UserSettingRouteKey.Team]: <TeamIcon />,
13
  [UserSettingRouteKey.Logout]: <LogoutIcon />,
14
  };
 
4
  import { ReactComponent as ProfileIcon } from '@/assets/svg/profile.svg';
5
  import { ReactComponent as TeamIcon } from '@/assets/svg/team.svg';
6
  import { UserSettingRouteKey } from '@/constants/setting';
7
+ import { MonitorOutlined } from '@ant-design/icons';
8
 
9
  export const UserSettingIconMap = {
10
  [UserSettingRouteKey.Profile]: <ProfileIcon />,
11
  [UserSettingRouteKey.Password]: <PasswordIcon />,
12
  [UserSettingRouteKey.Model]: <ModelIcon />,
13
+ [UserSettingRouteKey.System]: <MonitorOutlined style={{ fontSize: 24 }} />,
14
  [UserSettingRouteKey.Team]: <TeamIcon />,
15
  [UserSettingRouteKey.Logout]: <LogoutIcon />,
16
  };
web/src/pages/user-setting/index.less CHANGED
@@ -3,6 +3,8 @@
3
 
4
  .outletWrapper {
5
  padding: 32px 32px 0;
 
 
6
  }
7
 
8
  .itemDescription {
 
3
 
4
  .outletWrapper {
5
  padding: 32px 32px 0;
6
+ height: 100%;
7
+ overflow-y: auto;
8
  }
9
 
10
  .itemDescription {
web/src/pages/user-setting/setting-system/index.less ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .systemInfo {
2
+ width: 100%;
3
+ .title {
4
+ font-size: 20px;
5
+ font-weight: 600;
6
+ }
7
+ .text {
8
+ height: 26px;
9
+ line-height: 26px;
10
+ }
11
+ .badge {
12
+ :global(.ant-badge-status-dot) {
13
+ width: 10px;
14
+ height: 10px;
15
+ }
16
+ }
17
+ .error {
18
+ color: red;
19
+ }
20
+ }
web/src/pages/user-setting/setting-system/index.tsx ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import SvgIcon from '@/components/svg-icon';
2
+ import { useFetchSystemStatus } from '@/hooks/userSettingHook';
3
+ import { ISystemStatus, Minio } from '@/interfaces/database/userSetting';
4
+ import { Badge, Card, Flex, Spin, Typography } from 'antd';
5
+ import classNames from 'classnames';
6
+ import lowerCase from 'lodash/lowerCase';
7
+ import upperFirst from 'lodash/upperFirst';
8
+ import { useEffect } from 'react';
9
+
10
+ import { toFixed } from '@/utils/commonUtil';
11
+ import styles from './index.less';
12
+
13
+ const { Text } = Typography;
14
+
15
+ enum Status {
16
+ 'green' = 'success',
17
+ 'red' = 'error',
18
+ 'yellow' = 'warning',
19
+ }
20
+
21
+ const TitleMap = {
22
+ es: 'Elasticsearch',
23
+ minio: 'MinIO Object Storage',
24
+ redis: 'Redis',
25
+ mysql: 'Mysql',
26
+ };
27
+
28
+ const SystemInfo = () => {
29
+ const {
30
+ systemStatus,
31
+ fetchSystemStatus,
32
+ loading: statusLoading,
33
+ } = useFetchSystemStatus();
34
+
35
+ useEffect(() => {
36
+ fetchSystemStatus();
37
+ }, [fetchSystemStatus]);
38
+
39
+ return (
40
+ <section className={styles.systemInfo}>
41
+ <Spin spinning={statusLoading}>
42
+ <Flex gap={16} vertical>
43
+ {Object.keys(systemStatus).map((key) => {
44
+ const info = systemStatus[key as keyof ISystemStatus];
45
+
46
+ return (
47
+ <Card
48
+ type="inner"
49
+ title={
50
+ <Flex align="center" gap={10}>
51
+ <SvgIcon name={key} width={26}></SvgIcon>
52
+ <span className={styles.title}>
53
+ {TitleMap[key as keyof typeof TitleMap]}
54
+ </span>
55
+ <Badge
56
+ className={styles.badge}
57
+ status={Status[info.status as keyof typeof Status]}
58
+ />
59
+ </Flex>
60
+ }
61
+ key={key}
62
+ >
63
+ {Object.keys(info)
64
+ .filter((x) => x !== 'status')
65
+ .map((x) => {
66
+ return (
67
+ <Flex
68
+ key={x}
69
+ align="center"
70
+ gap={16}
71
+ className={styles.text}
72
+ >
73
+ <b>{upperFirst(lowerCase(x))}:</b>
74
+ <Text
75
+ className={classNames({
76
+ [styles.error]: x === 'error',
77
+ })}
78
+ >
79
+ {toFixed(info[x as keyof Minio]) as any}
80
+ {x === 'elapsed' && ' ms'}
81
+ </Text>
82
+ </Flex>
83
+ );
84
+ })}
85
+ </Card>
86
+ );
87
+ })}
88
+ </Flex>
89
+ </Spin>
90
+ </section>
91
+ );
92
+ };
93
+
94
+ export default SystemInfo;
web/src/pages/user-setting/sidebar/index.less CHANGED
@@ -1,3 +1,6 @@
1
  .sideBarWrapper {
2
  padding-top: 32px;
 
 
 
3
  }
 
1
  .sideBarWrapper {
2
  padding-top: 32px;
3
+ .version {
4
+ color: rgb(17, 206, 17);
5
+ }
6
  }
web/src/pages/user-setting/sidebar/index.tsx CHANGED
@@ -1,7 +1,7 @@
1
  import { useSecondPathName } from '@/hooks/routeHook';
2
  import type { MenuProps } from 'antd';
3
- import { Menu } from 'antd';
4
- import React, { useMemo } from 'react';
5
  import { useNavigate } from 'umi';
6
  import {
7
  UserSettingBaseKey,
@@ -10,7 +10,7 @@ import {
10
  } from '../constants';
11
 
12
  import { useTranslate } from '@/hooks/commonHooks';
13
- import { useLogout } from '@/hooks/userSettingHook';
14
  import styles from './index.less';
15
 
16
  type MenuItem = Required<MenuProps>['items'][number];
@@ -20,6 +20,11 @@ const SideBar = () => {
20
  const pathName = useSecondPathName();
21
  const logout = useLogout();
22
  const { t } = useTranslate('setting');
 
 
 
 
 
23
 
24
  function getItem(
25
  label: string,
@@ -32,7 +37,14 @@ const SideBar = () => {
32
  key,
33
  icon,
34
  children,
35
- label: t(label),
 
 
 
 
 
 
 
36
  type,
37
  } as MenuItem;
38
  }
 
1
  import { useSecondPathName } from '@/hooks/routeHook';
2
  import type { MenuProps } from 'antd';
3
+ import { Flex, Menu } from 'antd';
4
+ import React, { useEffect, useMemo } from 'react';
5
  import { useNavigate } from 'umi';
6
  import {
7
  UserSettingBaseKey,
 
10
  } from '../constants';
11
 
12
  import { useTranslate } from '@/hooks/commonHooks';
13
+ import { useFetchSystemVersion, useLogout } from '@/hooks/userSettingHook';
14
  import styles from './index.less';
15
 
16
  type MenuItem = Required<MenuProps>['items'][number];
 
20
  const pathName = useSecondPathName();
21
  const logout = useLogout();
22
  const { t } = useTranslate('setting');
23
+ const { version, fetchSystemVersion } = useFetchSystemVersion();
24
+
25
+ useEffect(() => {
26
+ fetchSystemVersion();
27
+ }, [fetchSystemVersion]);
28
 
29
  function getItem(
30
  label: string,
 
37
  key,
38
  icon,
39
  children,
40
+ label: (
41
+ <Flex justify={'space-between'}>
42
+ {t(label)}
43
+ <span className={styles.version}>
44
+ {label === 'system' && version}
45
+ </span>
46
+ </Flex>
47
+ ),
48
  type,
49
  } as MenuItem;
50
  }
web/src/routes.ts CHANGED
@@ -78,6 +78,10 @@ const routes = [
78
  path: '/user-setting/team',
79
  component: '@/pages/user-setting/setting-team',
80
  },
 
 
 
 
81
  ],
82
  },
83
  {
 
78
  path: '/user-setting/team',
79
  component: '@/pages/user-setting/setting-team',
80
  },
81
+ {
82
+ path: '/user-setting/system',
83
+ component: '@/pages/user-setting/setting-system',
84
+ },
85
  ],
86
  },
87
  {
web/src/services/userService.ts CHANGED
@@ -16,6 +16,8 @@ const {
16
  set_tenant_info,
17
  add_llm,
18
  delete_llm,
 
 
19
  } = api;
20
 
21
  const methods = {
@@ -71,6 +73,14 @@ const methods = {
71
  url: delete_llm,
72
  method: 'post',
73
  },
 
 
 
 
 
 
 
 
74
  } as const;
75
 
76
  const userService = registerServer<keyof typeof methods>(methods, request);
 
16
  set_tenant_info,
17
  add_llm,
18
  delete_llm,
19
+ getSystemStatus,
20
+ getSystemVersion,
21
  } = api;
22
 
23
  const methods = {
 
73
  url: delete_llm,
74
  method: 'post',
75
  },
76
+ getSystemStatus: {
77
+ url: getSystemStatus,
78
+ method: 'get',
79
+ },
80
+ getSystemVersion: {
81
+ url: getSystemVersion,
82
+ method: 'get',
83
+ },
84
  } as const;
85
 
86
  const userService = registerServer<keyof typeof methods>(methods, request);
web/src/utils/api.ts CHANGED
@@ -77,4 +77,8 @@ export default {
77
  createFolder: `${api_host}/file/create`,
78
  connectFileToKnowledge: `${api_host}/file2document/convert`,
79
  getFile: `${api_host}/file/get`,
 
 
 
 
80
  };
 
77
  createFolder: `${api_host}/file/create`,
78
  connectFileToKnowledge: `${api_host}/file2document/convert`,
79
  getFile: `${api_host}/file/get`,
80
+
81
+ // system
82
+ getSystemVersion: `${api_host}/system/version`,
83
+ getSystemStatus: `${api_host}/system/status`,
84
  };
web/src/utils/commonUtil.ts CHANGED
@@ -65,3 +65,10 @@ export const filterOptionsByInput = (
65
  input: string,
66
  option: { label: string; value: string } | undefined,
67
  ) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
 
 
 
 
 
 
 
 
65
  input: string,
66
  option: { label: string; value: string } | undefined,
67
  ) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
68
+
69
+ export const toFixed = (value: unknown, fixed = 2) => {
70
+ if (typeof value === 'number') {
71
+ return value.toFixed(fixed);
72
+ }
73
+ return value;
74
+ };
web/src/utils/registerServer.ts CHANGED
@@ -9,7 +9,7 @@ const registerServer = <T extends string>(
9
  ) => {
10
  const server: Service<T> = {} as Service<T>;
11
  for (let key in opt) {
12
- server[key] = (params: any, urlAppendix?: string) => {
13
  let url = opt[key].url;
14
  const requestOptions = opt[key];
15
  if (urlAppendix) {
 
9
  ) => {
10
  const server: Service<T> = {} as Service<T>;
11
  for (let key in opt) {
12
+ server[key] = (params?: any, urlAppendix?: string) => {
13
  let url = opt[key].url;
14
  const requestOptions = opt[key];
15
  if (urlAppendix) {