diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index cba4a7c..f0df314 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -24,7 +24,7 @@ jobs: - name: Build static export run: | - bun run build --base=/dashboard + bun run build - name: Compress dist folder run: zip -r dist.zip dist diff --git a/src/App.tsx b/src/App.tsx index 68dfb28..9dbda1b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,15 +8,10 @@ const App: React.FC = () => { return (
-
+
- - } - /> + } />
diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 457581e..388f2c2 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -8,13 +8,10 @@ import { DateTime } from "luxon"; import { useEffect, useRef, useState } from "react"; function Header() { - return (
-
+
void, delay: number | null) => { - const savedCallback = useRef<() => void>(() => { }); + const savedCallback = useRef<() => void>(() => {}); useEffect(() => { savedCallback.current = callback; }); @@ -74,9 +71,7 @@ function Overview() {

👋 Overview

-

- where the time is -

+

where the time is

{mouted ? (

{timeString}

) : ( diff --git a/src/components/ThemeSwitcher.tsx b/src/components/ThemeSwitcher.tsx index 86a7dab..fa248b0 100644 --- a/src/components/ThemeSwitcher.tsx +++ b/src/components/ThemeSwitcher.tsx @@ -8,7 +8,6 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { cn } from "@/lib/utils"; -import { CheckCircleIcon } from "@heroicons/react/20/solid"; import { Moon, Sun } from "lucide-react"; import { Theme } from "@/components/ThemeProvider"; import { useTheme } from "../hooks/use-theme"; @@ -40,21 +39,18 @@ export function ModeToggle() { onSelect={(e) => handleSelect(e, "light")} > Light - {theme === "light" && } handleSelect(e, "dark")} > Dark - {theme === "dark" && } handleSelect(e, "system")} > System - {theme === "system" && } diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx index 6d7f122..4407ae5 100644 --- a/src/components/ui/separator.tsx +++ b/src/components/ui/separator.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import * as SeparatorPrimitive from "@radix-ui/react-separator" +import * as React from "react"; +import * as SeparatorPrimitive from "@radix-ui/react-separator"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Separator = React.forwardRef< React.ElementRef, @@ -9,7 +9,7 @@ const Separator = React.forwardRef< >( ( { className, orientation = "horizontal", decorative = true, ...props }, - ref + ref, ) => ( - ) -) -Separator.displayName = SeparatorPrimitive.Root.displayName + ), +); +Separator.displayName = SeparatorPrimitive.Root.displayName; -export { Separator } +export { Separator }; diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx index 01b8b6d..2cdf440 100644 --- a/src/components/ui/skeleton.tsx +++ b/src/components/ui/skeleton.tsx @@ -1,4 +1,4 @@ -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Skeleton({ className, @@ -9,7 +9,7 @@ function Skeleton({ className={cn("animate-pulse rounded-md bg-muted", className)} {...props} /> - ) + ); } -export { Skeleton } +export { Skeleton }; diff --git a/src/lib/useWebsocket.tsx b/src/lib/useWebsocket.tsx index 0ae0f6d..1ebe770 100644 --- a/src/lib/useWebsocket.tsx +++ b/src/lib/useWebsocket.tsx @@ -1,89 +1,89 @@ -import { useState, useEffect, useRef, useCallback } from 'react' +import { useState, useEffect, useRef, useCallback } from "react"; export interface WebSocketHook { - socket: WebSocket | null - connected: boolean - onlineCount: number - message: string | null - sendMessage: (msg: string) => void + socket: WebSocket | null; + connected: boolean; + onlineCount: number; + message: string | null; + sendMessage: (msg: string) => void; } export default function useWebSocket(url: string): WebSocketHook { - const [socket, setSocket] = useState(null) - const [message, setMessage] = useState(null) - const [connected, setConnected] = useState(false) - const [onlineCount, setOnlineCount] = useState(0) - const socketRef = useRef(null) - const reconnectAttempts = useRef(0) - const reconnectTimeout = useRef(null) - const isUnmounted = useRef(false) + const [socket, setSocket] = useState(null); + const [message, setMessage] = useState(null); + const [connected, setConnected] = useState(false); + const [onlineCount, setOnlineCount] = useState(0); + const socketRef = useRef(null); + const reconnectAttempts = useRef(0); + const reconnectTimeout = useRef(null); + const isUnmounted = useRef(false); const connect = useCallback(() => { - if (isUnmounted.current) return + if (isUnmounted.current) return; - const ws = new WebSocket(url) - setSocket(ws) - socketRef.current = ws + const ws = new WebSocket(url); + setSocket(ws); + socketRef.current = ws; ws.onopen = () => { - setConnected(true) - reconnectAttempts.current = 0 - } + setConnected(true); + reconnectAttempts.current = 0; + }; ws.onmessage = (event: MessageEvent) => { - setMessage(event.data) - const msgJson = JSON.parse(event.data) - if (msgJson.type === 'live') { - setOnlineCount(msgJson.data.count) + setMessage(event.data); + const msgJson = JSON.parse(event.data); + if (msgJson.type === "live") { + setOnlineCount(msgJson.data.count); } - } + }; ws.onerror = (error) => { - console.error('WebSocket Error:', error) - } + console.error("WebSocket Error:", error); + }; ws.onclose = () => { - setConnected(false) + setConnected(false); if (!isUnmounted.current) { // Attempt to reconnect if (reconnectAttempts.current < 5) { - const timeout = Math.pow(2, reconnectAttempts.current) * 1000 // Exponential backoff - reconnectAttempts.current += 1 + const timeout = Math.pow(2, reconnectAttempts.current) * 1000; // Exponential backoff + reconnectAttempts.current += 1; reconnectTimeout.current = setTimeout(() => { - connect() - }, timeout) + connect(); + }, timeout); } else { - console.warn('Max reconnect attempts reached.') + console.warn("Max reconnect attempts reached."); } } - } - }, [url]) + }; + }, [url]); useEffect(() => { - connect() + connect(); return () => { - isUnmounted.current = true + isUnmounted.current = true; if (socketRef.current) { - socketRef.current.close() + socketRef.current.close(); } if (reconnectTimeout.current) { - clearTimeout(reconnectTimeout.current) + clearTimeout(reconnectTimeout.current); } - } - }, [connect]) + }; + }, [connect]); // Function to send messages const sendMessage = useCallback((msg: string) => { if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) { - socketRef.current.send(msg) + socketRef.current.send(msg); } else { console.warn( - 'WebSocket is not open. Ready state:', - socketRef.current?.readyState - ) + "WebSocket is not open. Ready state:", + socketRef.current?.readyState, + ); } - }, []) + }, []); - return { socket, message, sendMessage, connected, onlineCount } + return { socket, message, sendMessage, connected, onlineCount }; } diff --git a/src/lib/websocketProvider.tsx b/src/lib/websocketProvider.tsx index 81ae95f..1cf6688 100644 --- a/src/lib/websocketProvider.tsx +++ b/src/lib/websocketProvider.tsx @@ -1,26 +1,25 @@ -import { createContext, useContext, ReactNode } from 'react' -import useWebSocket, { WebSocketHook } from './useWebsocket' - +import { createContext, useContext, ReactNode } from "react"; +import useWebSocket, { WebSocketHook } from "./useWebsocket"; interface WebSocketProviderProps { - children: ReactNode + children: ReactNode; } -const WebSocketContext = createContext(undefined) +const WebSocketContext = createContext(undefined); export const WebSocketProvider = ({ children }: WebSocketProviderProps) => { - const ws = useWebSocket('wss://dev-next.buycoffee.top:4433/api/v1/ws/server') + const ws = useWebSocket("wss://dev-next.buycoffee.top:4433/api/v1/ws/server"); return ( {children} - ) -} + ); +}; export const useWebSocketContext = (): WebSocketHook => { - const context = useContext(WebSocketContext) + const context = useContext(WebSocketContext); if (!context) { throw new Error( - 'useWebSocketContext must be used within a WebSocketProvider' - ) + "useWebSocketContext must be used within a WebSocketProvider", + ); } - return context -} + return context; +}; diff --git a/src/main.tsx b/src/main.tsx index 293e182..daa8280 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -12,14 +12,14 @@ const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById("root")!).render( - - + + - - + + , ); diff --git a/src/pages/Server.tsx b/src/pages/Server.tsx index 147044f..534dd79 100644 --- a/src/pages/Server.tsx +++ b/src/pages/Server.tsx @@ -19,4 +19,4 @@ export default function Servers() {
); -} \ No newline at end of file +} diff --git a/src/types/nezha-api.ts b/src/types/nezha-api.ts new file mode 100644 index 0000000..f46aa77 --- /dev/null +++ b/src/types/nezha-api.ts @@ -0,0 +1,39 @@ +export interface NezhaAPI { + id: number; + name: string; + host: NezhaAPIHost; + status: NezhaAPIStatus; +} + +export interface NezhaAPIHost { + Platform: string; + PlatformVersion: string; + CPU: string[]; + MemTotal: number; + DiskTotal: number; + SwapTotal: number; + Arch: string; + BootTime: number; + CountryCode: string; + Version: string; +} + +export interface NezhaAPIStatus { + CPU: number; + MemUsed: number; + SwapUsed: number; + DiskUsed: number; + NetInTransfer: number; + NetOutTransfer: number; + NetInSpeed: number; + NetOutSpeed: number; + Uptime: number; + Load1: number; + Load5: number; + Load15: number; + TcpConnCount: number; + UdpConnCount: number; + ProcessCount: number; + Temperatures: number; + GPU: number; +}