diff --git a/src/components/ServerDetailChart.tsx b/src/components/ServerDetailChart.tsx
index 19ab087..9359b1c 100644
--- a/src/components/ServerDetailChart.tsx
+++ b/src/components/ServerDetailChart.tsx
@@ -56,9 +56,9 @@ export default function ServerDetailChart({
}: {
server_id: string;
}) {
- const { lastMessage, readyState } = useWebSocketContext();
+ const { lastMessage, connected } = useWebSocketContext();
- if (readyState !== 1) {
+ if (!connected) {
return ;
}
diff --git a/src/components/ServerDetailOverview.tsx b/src/components/ServerDetailOverview.tsx
index 910e2fd..7316cdc 100644
--- a/src/components/ServerDetailOverview.tsx
+++ b/src/components/ServerDetailOverview.tsx
@@ -18,9 +18,9 @@ export default function ServerDetailOverview({
const { t } = useTranslation();
const navigate = useNavigate();
- const { lastMessage, readyState } = useWebSocketContext();
+ const { lastMessage, connected } = useWebSocketContext();
- if (readyState !== 1) {
+ if (!connected) {
return ;
}
@@ -195,8 +195,9 @@ export default function ServerDetailOverview({
{"Load"}
{server.state.load_1 ? (
- {server.state.load_1} / {server.state.load_5} /{" "}
- {server.state.load_15}
+ {server.state.load_1.toFixed(2)} /{" "}
+ {server.state.load_5.toFixed(2)} /{" "}
+ {server.state.load_15.toFixed(2)}
) : (
{t("serverDetail.unknown")}
diff --git a/src/context/websocket-context.ts b/src/context/websocket-context.ts
index 28badc5..f6a40fd 100644
--- a/src/context/websocket-context.ts
+++ b/src/context/websocket-context.ts
@@ -1,11 +1,11 @@
import { createContext } from "react";
export interface WebSocketContextType {
- sendMessage: (message: string) => void;
- lastMessage: MessageEvent | null;
- readyState: number;
+ lastMessage: { data: string } | null;
+ connected: boolean;
}
-export const WebSocketContext = createContext(
- undefined,
-);
+export const WebSocketContext = createContext({
+ lastMessage: null,
+ connected: false,
+});
diff --git a/src/context/websocket-provider.tsx b/src/context/websocket-provider.tsx
index 3ae59cd..008ee92 100644
--- a/src/context/websocket-provider.tsx
+++ b/src/context/websocket-provider.tsx
@@ -1,9 +1,5 @@
-import React from "react";
-import {
- WebSocketContext,
- type WebSocketContextType,
-} from "./websocket-context";
-import useWebSocket from "react-use-websocket";
+import React, { useEffect, useRef, useState } from "react";
+import { WebSocketContext, WebSocketContextType } from "./websocket-context";
interface WebSocketProviderProps {
url: string;
@@ -14,16 +10,67 @@ export const WebSocketProvider: React.FC = ({
url,
children,
}) => {
- const { sendMessage, lastMessage, readyState } = useWebSocket(url, {
- reconnectAttempts: 10,
- reconnectInterval: 3000,
- shouldReconnect: () => true,
- });
+ const [lastMessage, setLastMessage] = useState<{ data: string } | null>(null);
+ const [connected, setConnected] = useState(false);
+ const ws = useRef(null);
+ const reconnectTimeout = useRef();
+ const maxReconnectAttempts = 30;
+ const reconnectAttempts = useRef(0);
+
+ const connect = () => {
+ try {
+ const wsUrl = new URL(url, window.location.origin);
+ wsUrl.protocol = wsUrl.protocol.replace("http", "ws");
+
+ ws.current = new WebSocket(wsUrl.toString());
+
+ ws.current.onopen = () => {
+ console.log("WebSocket connected");
+ setConnected(true);
+ reconnectAttempts.current = 0;
+ };
+
+ ws.current.onclose = () => {
+ console.log("WebSocket disconnected");
+ setConnected(false);
+
+ // 重连逻辑
+ if (reconnectAttempts.current < maxReconnectAttempts) {
+ reconnectTimeout.current = setTimeout(() => {
+ reconnectAttempts.current++;
+ connect();
+ }, 3000);
+ }
+ };
+
+ ws.current.onmessage = (event) => {
+ setLastMessage({ data: event.data });
+ };
+
+ ws.current.onerror = (error) => {
+ console.error("WebSocket error:", error);
+ };
+ } catch (error) {
+ console.error("WebSocket connection error:", error);
+ }
+ };
+
+ useEffect(() => {
+ connect();
+
+ return () => {
+ if (ws.current) {
+ ws.current.close();
+ }
+ if (reconnectTimeout.current) {
+ clearTimeout(reconnectTimeout.current);
+ }
+ };
+ }, [url]);
const contextValue: WebSocketContextType = {
- sendMessage,
lastMessage,
- readyState,
+ connected,
};
return (
diff --git a/src/pages/Server.tsx b/src/pages/Server.tsx
index 840e1aa..55fe1ac 100644
--- a/src/pages/Server.tsx
+++ b/src/pages/Server.tsx
@@ -13,6 +13,7 @@ import { useTranslation } from "react-i18next";
import { ChartBarSquareIcon, ViewColumnsIcon } from "@heroicons/react/20/solid";
import { ServiceTracker } from "@/components/ServiceTracker";
import ServerCardInline from "@/components/ServerCardInline";
+import { Loader } from "@/components/loading/Loader";
export default function Servers() {
const { t } = useTranslation();
@@ -20,7 +21,7 @@ export default function Servers() {
queryKey: ["server-group"],
queryFn: () => fetchServerGroup(),
});
- const { lastMessage, readyState } = useWebSocketContext();
+ const { lastMessage, connected } = useWebSocketContext();
const [showServices, setShowServices] = useState("0");
const [inline, setInline] = useState("0");
@@ -47,16 +48,19 @@ export default function Servers() {
useEffect(() => {
const hasShownToast = sessionStorage.getItem("websocket-connected-toast");
- if (readyState == 1 && !hasShownToast) {
+ if (connected && !hasShownToast) {
toast.success(t("info.websocketConnected"));
sessionStorage.setItem("websocket-connected-toast", "true");
}
- }, [readyState]);
+ }, [connected]);
- if (readyState !== 1) {
+ if (!connected) {
return (
-
-
{t("info.websocketConnecting")}
+
+
+
+ {t("info.websocketConnecting")}
+
);
}
diff --git a/vite.config.ts b/vite.config.ts
index 9a2c4e9..67d9050 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -20,7 +20,6 @@ export default defineConfig({
"/api/v1/": {
target: "http://localhost:8008",
changeOrigin: true,
- ws: true,
},
},
},