Skip to content

Commit

Permalink
feat: refactor web ui
Browse files Browse the repository at this point in the history
  • Loading branch information
zilliz authored and zilliz committed Nov 15, 2024
1 parent 1d06d43 commit 26ad709
Show file tree
Hide file tree
Showing 78 changed files with 7,310 additions and 2,814 deletions.
24 changes: 24 additions & 0 deletions internal/http/internal/web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
66 changes: 66 additions & 0 deletions internal/http/internal/web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## Milvus Management Web UI

### Prepare

Install [Node.js](https://nodejs.org/en/download/package-manager)

Or you can install Node.js using nvm

```bash
# installs nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
# download and install Node.js (you may need to restart the terminal)
nvm install 22
# verifies the right Node.js version is in the environment
node -v # should print `v22.11.0`
# verifies the right npm version is in the environment
npm -v # should print `10.9.0`
```

After you installed Node.js, check the Node.js version and NPM version in your command line interface.

```bash
node -v # get the Node.js version

npm -v # get the NPM version
```

### Development

Index to the `internal/http/internal/web` in Milvus project.

Run:

```bash
npm install && npm run dev
```

This web ui will automatically open the browser page.

Alternatively, you can open it manually via this address: http://localhost:5173.

#### Debug Mode

You can add `?debug=true` after the URL in the browser to turn on the debug mode. Under the debug mode, all api requests data will be mocked.


#### Others

If you have a deployed environment, you can specify the `API_URL` environment variable to allow your web page to access its data. Using:

```bash
API_URL="http://107.xxx.xxx.08/api/v1" npm run dev
```


### Build & Integration

Index to the `internal/http/internal/web`.

Run:

```bash
npm run build
```

All static files will be generated under the `internal/http/webui` directory.
29 changes: 29 additions & 0 deletions internal/http/internal/web/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript/no-require-imports': false,
},
},
)
13 changes: 13 additions & 0 deletions internal/http/internal/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
49 changes: 49 additions & 0 deletions internal/http/internal/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "milvus-management",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@mui/lab": "^5.0.0-alpha.144",
"@mui/material": "^6.0.2",
"@zilliz/zui": "^1.0.30",
"axios": "^1.7.7",
"classnames": "^2.5.1",
"qs": "^6.13.0",
"react": "^18.2.1",
"react-dom": "^18.2.1",
"react-router-dom": "^6.28.0",
"swr": "^2.2.5"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/postcss-advanced-variables": "^3.0.0",
"@types/postcss-import": "^14.0.3",
"@types/postcss-url": "^10.0.4",
"@types/qs": "^6.9.17",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"autoprefixer": "^10.4.15",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"postcss": "^8.4.7",
"postcss-advanced-variables": "^3.0.1",
"postcss-custom-media": "^8.0.0",
"postcss-import": "^14.0.2",
"postcss-nested": "^5.0.6",
"postcss-url": "^10.1.3",
"sass-embedded": "^1.80.6",
"typescript": "~5.6.2",
"typescript-eslint": "^8.11.0",
"vite": "^5.4.10"
}
}
47 changes: 47 additions & 0 deletions internal/http/internal/web/src/apis/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios from 'axios'
import { ZNotification } from '@zilliz/zui'
import { checkDebugMode } from './mock'

const MILVUS_URI = __API_URL__

const axiosInstance = axios.create({
timeout: 5000,
baseURL: MILVUS_URI,
})

axiosInstance.interceptors.response.use((v) => {
if (v.status >= 400) {
ZNotification({
title: 'Error',
message: v.data.message || 'Request Error',
showClose: false,
handleClose: () => {},
})
return v
}
return v
})

const requestWithMock = <T>(mockData: T | undefined, fetcher: () => Promise<{ data: T }>) => {
if (checkDebugMode() && mockData) {
return Promise.resolve(mockData)
}
return fetcher().then(res => res.data)
}

const $http = {
get: <T>(url: string, options?: any, mockData?: string) => {
return requestWithMock<T>(
JSON.parse(mockData || 'null'),
() => axiosInstance.get(url, options),
)
},
post: <T>(url: string, params?: any, options?: any, mockData?: any) => {
return requestWithMock<T>(
JSON.parse(mockData || 'null'),
() => axiosInstance.post(url, params, options),
)
}
}

export default $http
114 changes: 114 additions & 0 deletions internal/http/internal/web/src/apis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import $http from './http'
import * as mock from './mock'
import { Client, Collection, Database, Dependency, Node } from '../typings'
import { convertCollectionData, convertDatabaseData } from '../utils'

export { checkDebugMode } from './mock'

export const fetchCluster = async () => {
const res = await $http.get<{ nodes_info: Node[] }>('/_cluster/info', {}, mock.sysmetrics)
return res.nodes_info
}

export const fetchClusterClients = async () => {
return $http.get<Client[]>('/_cluster/clients', {}, mock.clientInfos)
}

export const fetchClusterDeps = async () => {
return $http.get<Dependency>('/_cluster/dependencies', {}, mock.dependencies)
}

export const fetchDatabases = async () => {
const res = await $http.get<any>('/_db/list', {}, mock.listDatabaseData)
try {

return convertDatabaseData(res) as Database[]
} catch (e) {
console.warn(e)
}
}

export const fetchDatabase = async (name: string) => {
return $http.get<any>('/_db/desc', { params: { db_name: name } }, mock.describeDatabaseData)
}

export const fetchCollections = async (dbName: string = 'default') => {
const res = await $http.get<any>('/_collection/list', { params: { db_name: dbName } }, mock.listCollectionData)
return convertCollectionData(res) as Collection[]
}

export const fetchCollection = async (name: string, dbName: string = 'default') => {
return $http.get<any>(
'/_collection/desc',
{ params: { db_name: dbName, collection_name: name } },
mock.describeCollectionData,
)
}

export const fetchQCDist = async () => {
return $http.get<any>('/_qc/dist', {}, mock.qcDist)
}

export const fetchQCCurrTargets = async () => {
return $http.get<any>('/_qc/target', {}, mock.qcCurrentTargets)
}

export const fetchQCNextTargets = async () => {
return $http.get<any>('/_qc/target', { params: { target_scope: 2 } }, mock.qcNextTargets)
}

export const fetchSegments = async () => {
return $http.get<any>('/_qn/segments', {}, mock.qnSegments)
}

export const fetchChannels = async () => {
return $http.get<any>('/_qn/channels', {}, mock.qnChannels)
}

export const fetchReplicas = async () => {
return $http.get<any>('/_qc/replica', {}, mock.qcReplica)
}

export const fetchResourceGroups = async () => {
return $http.get<any>('/_qc/resource_group', {}, mock.qcResourceGroup)
}

export const fetchDCDist = async () => {
return $http.get<any>('/_dc/dist', {}, mock.dc_dist)
}

export const fetchDataChannels = async () => {
return $http.get<any>('/_dn/channels', {}, mock.dn_channels)
}

export const fetchDataSegments = async () => {
return $http.get<any>('/_dn/segments', {}, mock.dn_segments)
}

export const fetchTasks = async () => {
return $http.get<any>('/_qc/tasks', {}, mock.qcTasks)
}

export const fetchTasksCompaction = async () => {
return $http.get<any>('/_dc/tasks/compaction', {}, mock.dc_compaction_task)
}

export const fetchTasksBuildIndex = async () => {
return $http.get<any>('/_dc/tasks/build_index', {}, mock.dc_build_index_task)
}

export const fetchTasksImport = async () => {
return $http.get<any>('/_dc/tasks/import', {}, mock.dc_import_task)
}

export const fetchTasksSync = async () => {
return $http.get<any>('/_dn/tasks/sync', {}, mock.dn_sync_task)
}

export const fetchSlowRequests = async () => {
return $http.get<any>('/_cluster/slow_query', {}, mock.slowQueries)
}

export const fetchConfigs = async () => {
return $http.get<any>('/_cluster/configs', {}, mock.mconfigs)
}
Loading

0 comments on commit 26ad709

Please sign in to comment.