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, }, }, },