diff --git a/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx b/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx index ef750f0c..0e606bad 100644 --- a/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx +++ b/src/ui/desktop/apps/host-manager/HostManagerViewer.tsx @@ -25,10 +25,12 @@ import { getSSHFolders, updateFolderMetadata, deleteAllHostsInFolder, + getServerStatusById, } from "@/ui/main-axios.ts"; import { toast } from "sonner"; import { useTranslation } from "react-i18next"; import { useConfirmation } from "@/hooks/use-confirmation.ts"; +import { Status, StatusIndicator } from "@/components/ui/shadcn-io/status"; import { Edit, Trash2, @@ -58,6 +60,7 @@ import { Archive, HardDrive, Globe, + FolderOpen, } from "lucide-react"; import type { SSHHost, @@ -66,10 +69,12 @@ import type { } from "../../../../types/index.js"; import { DEFAULT_STATS_CONFIG } from "@/types/stats-widgets"; import { FolderEditDialog } from "./components/FolderEditDialog"; +import { useTabs } from "@/ui/desktop/navigation/tabs/TabContext"; export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) { const { t } = useTranslation(); const { confirmWithToast } = useConfirmation(); + const { addTab } = useTabs(); const [hosts, setHosts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -82,6 +87,7 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) { const [operationLoading, setOperationLoading] = useState(false); const [folderMetadata, setFolderMetadata] = useState>(new Map()); const [editingFolderAppearance, setEditingFolderAppearance] = useState(null); + const [serverStatuses, setServerStatuses] = useState>(new Map()); const dragCounter = useRef(0); useEffect(() => { @@ -191,6 +197,71 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) { ); }; + useEffect(() => { + if (hosts.length === 0) return; + + const statusIntervals: NodeJS.Timeout[] = []; + const statusCancelled: boolean[] = []; + + hosts.forEach((host, index) => { + const statsConfig = (() => { + try { + return host.statsConfig + ? JSON.parse(host.statsConfig) + : DEFAULT_STATS_CONFIG; + } catch { + return DEFAULT_STATS_CONFIG; + } + })(); + + const shouldShowStatus = statsConfig.statusCheckEnabled !== false; + + if (!shouldShowStatus) { + setServerStatuses((prev) => { + const next = new Map(prev); + next.set(host.id, "offline"); + return next; + }); + return; + } + + const fetchStatus = async () => { + try { + const res = await getServerStatusById(host.id); + if (!statusCancelled[index]) { + setServerStatuses((prev) => { + const next = new Map(prev); + next.set(host.id, res?.status === "online" ? "online" : "offline"); + return next; + }); + } + } catch (error: unknown) { + if (!statusCancelled[index]) { + const err = error as { response?: { status?: number } }; + let status: "online" | "offline" | "degraded" = "offline"; + if (err?.response?.status === 504) { + status = "degraded"; + } + setServerStatuses((prev) => { + const next = new Map(prev); + next.set(host.id, status); + return next; + }); + } + } + }; + + fetchStatus(); + const intervalId = setInterval(fetchStatus, 10000); + statusIntervals.push(intervalId); + }); + + return () => { + statusCancelled.fill(true); + statusIntervals.forEach((interval) => clearInterval(interval)); + }; + }, [hosts]); + const getFolderIcon = (folderName: string) => { const metadata = folderMetadata.get(folderName); if (!metadata?.icon) return Folder; @@ -1110,6 +1181,28 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
+ {(() => { + const statsConfig = (() => { + try { + return host.statsConfig + ? JSON.parse(host.statsConfig) + : DEFAULT_STATS_CONFIG; + } catch { + return DEFAULT_STATS_CONFIG; + } + })(); + const shouldShowStatus = statsConfig.statusCheckEnabled !== false; + const serverStatus = serverStatuses.get(host.id) || "degraded"; + + return shouldShowStatus ? ( + + + + ) : null; + })()} {host.pin && ( )} @@ -1332,6 +1425,76 @@ export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) { })()}
+ +
+ {host.enableTerminal && ( + + + + + +

Open Terminal

+
+
+ )} + {host.enableFileManager && ( + + + + + +

Open File Manager

+
+
+ )} + + + + + +

Open Server Details

+
+
+