diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cc715bfb..46605b4d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -907,6 +907,8 @@ importers:
'@types/morgan': ^1.9.4
'@types/react': ^18.0.25
'@types/react-dom': ^18.0.8
+ ahooks: ^3.7.4
+ axios: ^1.2.2
body-parser: ^1.20.1
compression: ^1.7.4
cross-env: ^7.0.3
@@ -937,6 +939,8 @@ importers:
'@remix-run/react': 1.9.0_biqbaboplfbrettd7655fr4n2y
'@typegoose/typegoose': 9.3.1
'@types/md5': 2.3.2
+ ahooks: 3.7.4_react@18.2.0
+ axios: 1.2.2
body-parser: 1.20.1
compression: 1.7.4
express: 4.18.2
@@ -15384,6 +15388,16 @@ packages:
- debug
dev: false
+ /axios/1.2.2:
+ resolution: {integrity: sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==}
+ dependencies:
+ follow-redirects: 1.15.2
+ form-data: 4.0.0
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
/b-tween/0.3.3:
resolution: {integrity: sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==}
dev: false
@@ -30899,7 +30913,6 @@ packages:
/proxy-from-env/1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
- dev: true
/prr/1.0.1:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
diff --git a/server/admin/app/ra/App.tsx b/server/admin/app/ra/App.tsx
index 24192c66..835095a5 100644
--- a/server/admin/app/ra/App.tsx
+++ b/server/admin/app/ra/App.tsx
@@ -1,10 +1,9 @@
import {
Admin,
Resource,
- ListGuesser,
fetchUtils,
- EditGuesser,
ShowGuesser,
+ CustomRoutes,
} from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';
import { authProvider, authStorageKey } from './authProvider';
@@ -19,6 +18,9 @@ import GroupIcon from '@mui/icons-material/Group';
import AttachFileIcon from '@mui/icons-material/AttachFile';
import { theme } from './theme';
import { Dashboard } from './dashboard';
+import { Route } from 'react-router-dom';
+import { TailchatNetwork } from './network';
+import { TailchatLayout } from './layout';
const httpClient: typeof fetchUtils.fetchJson = (url, options = {}) => {
try {
@@ -47,6 +49,7 @@ export const App = () => (
basename="/admin"
theme={theme}
dashboard={Dashboard}
+ layout={TailchatLayout}
disableTelemetry={true}
authProvider={authProvider}
dataProvider={dataProvider}
@@ -80,5 +83,9 @@ export const App = () => (
list={FileList}
show={ShowGuesser}
/>
+
+
+ } />
+
);
diff --git a/server/admin/app/ra/components/ChipItems.tsx b/server/admin/app/ra/components/ChipItems.tsx
new file mode 100644
index 00000000..47076979
--- /dev/null
+++ b/server/admin/app/ra/components/ChipItems.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { Chip, Grid } from '@mui/material';
+
+export const ChipItems: React.FC<{
+ items: string[];
+}> = React.memo((props) => {
+ return (
+
+ {props.items.map((item) => (
+
+
+
+ ))}
+
+ );
+});
+ChipItems.displayName = 'ChipItems';
diff --git a/server/admin/app/ra/layout/Menu.tsx b/server/admin/app/ra/layout/Menu.tsx
new file mode 100644
index 00000000..c7a7b8f1
--- /dev/null
+++ b/server/admin/app/ra/layout/Menu.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import {
+ Menu,
+ MenuProps,
+ ResourceMenuItem,
+ useResourceDefinitions,
+} from 'react-admin';
+import FilterDramaIcon from '@mui/icons-material/FilterDrama';
+
+export const TailchatMenu: React.FC = React.memo((props) => {
+ const resources = useResourceDefinitions();
+
+ return (
+
+ );
+});
+TailchatMenu.displayName = 'TailchatMenu';
diff --git a/server/admin/app/ra/layout/index.tsx b/server/admin/app/ra/layout/index.tsx
new file mode 100644
index 00000000..90a72584
--- /dev/null
+++ b/server/admin/app/ra/layout/index.tsx
@@ -0,0 +1,8 @@
+import React from 'react';
+import { LayoutComponent } from 'react-admin';
+import { Layout } from 'react-admin';
+import { TailchatMenu } from './Menu';
+
+export const TailchatLayout: LayoutComponent = (props) => (
+
+);
diff --git a/server/admin/app/ra/network/index.tsx b/server/admin/app/ra/network/index.tsx
new file mode 100644
index 00000000..6461fc20
--- /dev/null
+++ b/server/admin/app/ra/network/index.tsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import { request } from '../request';
+import { useRequest } from 'ahooks';
+import {
+ CircularProgress,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ Typography,
+ Box,
+} from '@mui/material';
+import _uniq from 'lodash/uniq';
+import { ChipItems } from '../components/ChipItems';
+
+/**
+ * Tailchat 网络状态
+ */
+export const TailchatNetwork: React.FC = React.memo(() => {
+ const { data, loading } = useRequest(async () => {
+ const { data } = await request('/network/all');
+
+ return data;
+ });
+
+ if (loading) {
+ return ;
+ }
+
+ return (
+
+
+ 节点列表
+
+
+
+
+ ID
+ 主机名
+ CPU占用
+ IP地址列表
+ SDK版本
+
+
+
+ {(data.nodes ?? []).map((row) => (
+
+
+ {row.id}
+ {row.local && (*)}
+
+ {row.hostname}
+ {row.cpu}%
+
+
+
+ {row.client.version}
+
+ ))}
+
+
+
+
+ 服务列表
+
+
+ (data.services ?? [])} />
+
+
+
+ 操作列表
+
+
+ (data.actions ?? [])} />
+
+
+
+ 事件列表
+
+
+ (data.events ?? [])} />
+
+
+ );
+});
+TailchatNetwork.displayName = 'TailchatNetwork';
diff --git a/server/admin/app/ra/request.ts b/server/admin/app/ra/request.ts
new file mode 100644
index 00000000..74e1104e
--- /dev/null
+++ b/server/admin/app/ra/request.ts
@@ -0,0 +1,29 @@
+import axios from 'axios';
+import { authStorageKey } from './authProvider';
+import _set from 'lodash/set';
+
+/**
+ * 创建请求实例
+ */
+function createRequest() {
+ const ins = axios.create({
+ baseURL: '/admin/api',
+ });
+
+ ins.interceptors.request.use(async (val) => {
+ try {
+ const { token } = JSON.parse(
+ window.localStorage.getItem(authStorageKey) ?? '{}'
+ );
+ _set(val, ['headers', 'Authorization'], `Bearer ${token}`);
+
+ return val;
+ } catch (err) {
+ throw err;
+ }
+ });
+
+ return ins;
+}
+
+export const request = createRequest();
diff --git a/server/admin/app/ra/resources/user.tsx b/server/admin/app/ra/resources/user.tsx
index 951ea8b1..edec9f3b 100644
--- a/server/admin/app/ra/resources/user.tsx
+++ b/server/admin/app/ra/resources/user.tsx
@@ -39,7 +39,6 @@ export const UserList: React.FC = () => (
-
diff --git a/server/admin/package.json b/server/admin/package.json
index 35750546..7369dfb0 100644
--- a/server/admin/package.json
+++ b/server/admin/package.json
@@ -18,6 +18,8 @@
"@remix-run/react": "^1.9.0",
"@typegoose/typegoose": "9.3.1",
"@types/md5": "^2.3.2",
+ "ahooks": "^3.7.4",
+ "axios": "^1.2.2",
"body-parser": "^1.20.1",
"compression": "^1.7.4",
"express": "^4.18.2",