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); 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) { console.error('Failed to fetch latest host config:', 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) { console.error('Failed to fetch updated host config:', 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) { console.error('Failed to fetch server status:', error); if (!cancelled) { setServerStatus('offline'); toast.error(t('serverStats.failedToFetchStatus')); } } }; const fetchMetrics = async () => { if (!currentHostConfig?.id) return; try { const data = await getServerMetricsById(currentHostConfig.id); if (!cancelled) { setMetrics(data); } } catch (error) { console.error('Failed to fetch server metrics:', error); if (!cancelled) { setMetrics(null); toast.error(t('serverStats.failedToFetchMetrics')); } } }; if (currentHostConfig?.id && isVisible) { fetchStatus(); fetchMetrics(); intervalId = window.setInterval(() => { if (isVisible) { 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 (
{/* Top Header */}

{currentHostConfig?.folder} / {title}

{currentHostConfig?.enableFileManager && ( )}
{/* Stats */}
{/* CPU */}

{(() => { const pct = metrics?.cpu?.percent; const cores = metrics?.cpu?.cores; const la = metrics?.cpu?.load; const pctText = (typeof pct === 'number') ? `${pct}%` : 'N/A'; const coresText = (typeof cores === 'number') ? t('serverStats.cpuCores', {count: cores}) : t('serverStats.naCpus'); const laText = (la && la.length === 3) ? t('serverStats.loadAverage', {avg1: la[0].toFixed(2), avg5: la[1].toFixed(2), avg15: la[2].toFixed(2)}) : t('serverStats.loadAverageNA'); return `${t('serverStats.cpuUsage')} - ${pctText} ${t('serverStats.of')} ${coresText} (${laText})`; })()}

{/* Memory */}

{(() => { 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} GiB` : 'N/A'; const totalText = (typeof total === 'number') ? `${total} GiB` : 'N/A'; return `${t('serverStats.memoryUsage')} - ${pctText} (${usedText} ${t('serverStats.of')} ${totalText})`; })()}

{/* Root Storage */}

{(() => { 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 `${t('serverStats.rootStorageSpace')} - ${pctText} (${usedText} ${t('serverStats.of')} ${totalText})`; })()}

{/* SSH Tunnels */} {(currentHostConfig?.tunnelConnections && currentHostConfig.tunnelConnections.length > 0) && (
)}

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

); }