import React from "react"; import { useSidebar } from "@/components/ui/sidebar.tsx"; import { Status, StatusIndicator } from "@/components/ui/shadcn-io/status"; import { Separator } from "@/components/ui/separator.tsx"; import { Button } from "@/components/ui/button.tsx"; import { Progress } from "@/components/ui/progress.tsx"; import { Cpu, HardDrive, MemoryStick } from "lucide-react"; import { Tunnel } from "@/ui/Desktop/Apps/Tunnel/Tunnel.tsx"; import { getServerStatusById, getServerMetricsById, type ServerMetrics, } from "@/ui/main-axios.ts"; import { useTabs } from "@/ui/Desktop/Navigation/Tabs/TabContext.tsx"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; interface ServerProps { hostConfig?: any; title?: string; isVisible?: boolean; isTopbarOpen?: boolean; embedded?: boolean; } export function Server({ hostConfig, title, isVisible = true, isTopbarOpen = true, embedded = false, }: ServerProps): React.ReactElement { const { t } = useTranslation(); const { state: sidebarState } = useSidebar(); const { addTab, tabs } = useTabs() as any; const [serverStatus, setServerStatus] = React.useState<"online" | "offline">( "offline", ); const [metrics, setMetrics] = React.useState(null); const [currentHostConfig, setCurrentHostConfig] = React.useState(hostConfig); const [isLoadingMetrics, setIsLoadingMetrics] = React.useState(false); const [isRefreshing, setIsRefreshing] = React.useState(false); const [showStatsUI, setShowStatsUI] = React.useState(true); React.useEffect(() => { setCurrentHostConfig(hostConfig); }, [hostConfig]); React.useEffect(() => { const fetchLatestHostConfig = async () => { if (hostConfig?.id) { try { const { getSSHHosts } = await import("@/ui/main-axios.ts"); const hosts = await getSSHHosts(); const updatedHost = hosts.find((h) => h.id === hostConfig.id); if (updatedHost) { setCurrentHostConfig(updatedHost); } } catch (error) { toast.error(t("serverStats.failedToFetchHostConfig")); } } }; fetchLatestHostConfig(); const handleHostsChanged = async () => { if (hostConfig?.id) { try { const { getSSHHosts } = await import("@/ui/main-axios.ts"); const hosts = await getSSHHosts(); const updatedHost = hosts.find((h) => h.id === hostConfig.id); if (updatedHost) { setCurrentHostConfig(updatedHost); } } catch (error) { toast.error(t("serverStats.failedToFetchHostConfig")); } } }; window.addEventListener("ssh-hosts:changed", handleHostsChanged); return () => window.removeEventListener("ssh-hosts:changed", handleHostsChanged); }, [hostConfig?.id]); React.useEffect(() => { let cancelled = false; let intervalId: number | undefined; const fetchStatus = async () => { try { const res = await getServerStatusById(currentHostConfig?.id); if (!cancelled) { setServerStatus(res?.status === "online" ? "online" : "offline"); } } catch (error: any) { if (!cancelled) { if (error?.response?.status === 503) { setServerStatus("offline"); } else if (error?.response?.status === 504) { setServerStatus("offline"); } else if (error?.response?.status === 404) { setServerStatus("offline"); } else { setServerStatus("offline"); } toast.error(t("serverStats.failedToFetchStatus")); } } }; const fetchMetrics = async () => { if (!currentHostConfig?.id) return; try { setIsLoadingMetrics(true); const data = await getServerMetricsById(currentHostConfig.id); if (!cancelled) { setMetrics(data); setShowStatsUI(true); } } catch (error) { if (!cancelled) { setMetrics(null); setShowStatsUI(false); toast.error(t("serverStats.failedToFetchMetrics")); } } finally { if (!cancelled) { setIsLoadingMetrics(false); } } }; if (currentHostConfig?.id && isVisible) { fetchStatus(); fetchMetrics(); intervalId = window.setInterval(() => { fetchStatus(); fetchMetrics(); }, 30000); } return () => { cancelled = true; if (intervalId) window.clearInterval(intervalId); }; }, [currentHostConfig?.id, isVisible]); const topMarginPx = isTopbarOpen ? 74 : 16; const leftMarginPx = sidebarState === "collapsed" ? 16 : 8; const bottomMarginPx = 8; const isFileManagerAlreadyOpen = React.useMemo(() => { if (!currentHostConfig) return false; return tabs.some( (tab: any) => tab.type === "file_manager" && tab.hostConfig?.id === currentHostConfig.id, ); }, [tabs, currentHostConfig]); const wrapperStyle: React.CSSProperties = embedded ? { opacity: isVisible ? 1 : 0, height: "100%", width: "100%" } : { opacity: isVisible ? 1 : 0, marginLeft: leftMarginPx, marginRight: 17, marginTop: topMarginPx, marginBottom: bottomMarginPx, height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`, }; const containerClass = embedded ? "h-full w-full text-white overflow-hidden bg-transparent" : "bg-dark-bg text-white rounded-lg border-2 border-dark-border overflow-hidden"; return (

{currentHostConfig?.folder} / {title}

{currentHostConfig?.enableFileManager && ( )}
{showStatsUI && (
{isLoadingMetrics && !metrics ? (
{t("serverStats.loadingMetrics")}
) : !metrics && serverStatus === "offline" ? (

{t("serverStats.serverOffline")}

{t("serverStats.cannotFetchMetrics")}

) : (
{/* CPU Stats */}

{t("serverStats.cpuUsage")}

{(() => { const pct = metrics?.cpu?.percent; const cores = metrics?.cpu?.cores; const pctText = typeof pct === "number" ? `${pct}%` : "N/A"; const coresText = typeof cores === "number" ? t("serverStats.cpuCores", { count: cores }) : t("serverStats.naCpus"); return `${pctText} ${t("serverStats.of")} ${coresText}`; })()}
{metrics?.cpu?.load ? `Load: ${metrics.cpu.load[0].toFixed(2)}, ${metrics.cpu.load[1].toFixed(2)}, ${metrics.cpu.load[2].toFixed(2)}` : "Load: N/A"}
{/* Memory Stats */}

{t("serverStats.memoryUsage")}

{(() => { const pct = metrics?.memory?.percent; const used = metrics?.memory?.usedGiB; const total = metrics?.memory?.totalGiB; const pctText = typeof pct === "number" ? `${pct}%` : "N/A"; const usedText = typeof used === "number" ? `${used.toFixed(1)} GiB` : "N/A"; const totalText = typeof total === "number" ? `${total.toFixed(1)} GiB` : "N/A"; return `${pctText} (${usedText} ${t("serverStats.of")} ${totalText})`; })()}
{(() => { const used = metrics?.memory?.usedGiB; const total = metrics?.memory?.totalGiB; const free = typeof used === "number" && typeof total === "number" ? (total - used).toFixed(1) : "N/A"; return `Free: ${free} GiB`; })()}
{/* Disk Stats */}

{t("serverStats.rootStorageSpace")}

{(() => { const pct = metrics?.disk?.percent; const used = metrics?.disk?.usedHuman; const total = metrics?.disk?.totalHuman; const pctText = typeof pct === "number" ? `${pct}%` : "N/A"; const usedText = used ?? "N/A"; const totalText = total ?? "N/A"; return `${pctText} (${usedText} ${t("serverStats.of")} ${totalText})`; })()}
{(() => { const available = metrics?.disk?.availableHuman; return available ? `Available: ${available}` : "Available: N/A"; })()}
)}
)} {/* SSH Tunnels */} {currentHostConfig?.tunnelConnections && currentHostConfig.tunnelConnections.length > 0 && (
)}

{t("serverStats.feedbackMessage")}{" "} GitHub !

); }