diff --git a/src/pages/AdminRemnawave.tsx b/src/pages/AdminRemnawave.tsx index d28b578..b34308e 100644 --- a/src/pages/AdminRemnawave.tsx +++ b/src/pages/AdminRemnawave.tsx @@ -50,6 +50,18 @@ const formatBytes = (bytes: number): string => { // сохранён для случая пустого кода (важно для UI-плейсхолдеров). const getCountryFlag = (code: string | null | undefined): string => getFlagEmoji(code) || '🌍'; +// Realtime interface throughput (bytes/s) → network-style speed, matching the +// panel exactly: the unit step is by 1024 bytes, but the value is shown in bits +// (×8 / 1000), e.g. 341 KB/s → "2728 Kb/s", 1.34 MB/s → "10.72 Mb/s". +const formatSpeed = (bytesPerSec: number): string => { + const bps = bytesPerSec || 0; + const bits = bps * 8; + if (bps < 1024) return `${bits.toFixed(0)} b/s`; + if (bps < 1024 ** 2) return `${(bits / 1000).toFixed(2)} Kb/s`; + if (bps < 1024 ** 3) return `${(bits / 1e6).toFixed(2)} Mb/s`; + return `${(bits / 1e9).toFixed(2)} Gb/s`; +}; + interface StatCardProps { label: string; value: string | number; @@ -84,73 +96,75 @@ function StatCard({ label, value, icon, color = 'accent', subValue }: StatCardPr interface NodeCardProps { node: NodeInfo; + providerName?: string; onAction: (uuid: string, action: 'enable' | 'disable' | 'restart') => void; isLoading?: boolean; } -function NodeCard({ node, onAction, isLoading }: NodeCardProps) { +function NodeCard({ node, providerName, onAction, isLoading }: NodeCardProps) { const { t } = useTranslation(); - const statusColor = node.is_disabled - ? 'text-dark-500' - : node.is_connected && node.is_node_online - ? 'text-success-400' - : 'text-error-400'; - + const isUp = node.is_connected && node.is_node_online && !node.is_disabled; + const dotColor = node.is_disabled ? 'bg-dark-500' : isUp ? 'bg-success-400' : 'bg-error-400'; const statusText = node.is_disabled ? t('admin.remnawave.nodes.disabled', 'Disabled') - : node.is_connected && node.is_node_online + : isUp ? t('admin.remnawave.nodes.online', 'Online') : t('admin.remnawave.nodes.offline', 'Offline'); - return ( -
{node.address}
+ const s = node.system?.stats; + const memTotal = s ? s.memoryUsed + s.memoryFree : 0; + const ramPct = memTotal > 0 && s ? Math.round((s.memoryUsed / memTotal) * 100) : null; + const loadAvg = s?.loadAvg?.length + ? s.loadAvg + .slice(0, 3) + .map((n) => n.toFixed(2)) + .join(' ') + : null; + const rx = s?.interface?.rxBytesPerSec ?? 0; + const tx = s?.interface?.txBytesPerSec ?? 0; -