207 lines
8.2 KiB
JavaScript
207 lines
8.2 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
||
|
||
const ServerMonitoring = () => {
|
||
const [servers, setServers] = useState([]);
|
||
const [monitoringData, setMonitoringData] = useState({});
|
||
const [autoRefresh, setAutoRefresh] = useState(true);
|
||
|
||
useEffect(() => {
|
||
loadServers();
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
if (autoRefresh) {
|
||
const interval = setInterval(() => {
|
||
servers.forEach(server => {
|
||
checkServerStatus(server);
|
||
});
|
||
}, 30000);
|
||
return () => clearInterval(interval);
|
||
}
|
||
}, [autoRefresh, servers]);
|
||
|
||
const loadServers = async () => {
|
||
if (window.electronAPI) {
|
||
const result = await window.electronAPI.loadServers();
|
||
if (result.success) {
|
||
setServers(result.servers);
|
||
result.servers.forEach(server => checkServerStatus(server));
|
||
}
|
||
}
|
||
};
|
||
|
||
const checkServerStatus = async (server) => {
|
||
if (!window.electronAPI) return;
|
||
|
||
// Ping проверка
|
||
const pingResult = await window.electronAPI.pingServer(server);
|
||
|
||
// SNMP мониторинг (если доступен)
|
||
let snmpData = null;
|
||
try {
|
||
const snmpResult = await window.electronAPI.getSnmpData(server, 'public');
|
||
if (snmpResult.success) snmpData = snmpResult.data;
|
||
} catch (error) {}
|
||
|
||
// SSH мониторинг (если есть учетные данные)
|
||
let sshStats = null;
|
||
if (server.username && server.password) {
|
||
try {
|
||
const sshResult = await window.electronAPI.getSshStats(server);
|
||
if (sshResult.success) sshStats = sshResult.stats;
|
||
} catch (error) {}
|
||
}
|
||
|
||
setMonitoringData(prev => ({
|
||
...prev,
|
||
[server.id]: {
|
||
ping: pingResult,
|
||
snmp: snmpData,
|
||
ssh: sshStats,
|
||
lastUpdate: new Date()
|
||
}
|
||
}));
|
||
};
|
||
|
||
const getStatusColor = (online) => {
|
||
return online ? 'bg-green-500' : 'bg-red-500';
|
||
};
|
||
|
||
const getUsageColor = (percent) => {
|
||
if (percent < 70) return 'bg-green-500';
|
||
if (percent < 90) return 'bg-yellow-500';
|
||
return 'bg-red-500';
|
||
};
|
||
|
||
return (
|
||
<div className="p-6">
|
||
<div className="flex justify-between items-center mb-6">
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-gray-800 dark:text-white">Мониторинг серверов</h1>
|
||
<p className="text-gray-600 dark:text-gray-400">Реальное время состояние серверов</p>
|
||
</div>
|
||
<div className="flex items-center space-x-4">
|
||
<label className="flex items-center space-x-2">
|
||
<input
|
||
type="checkbox"
|
||
checked={autoRefresh}
|
||
onChange={(e) => setAutoRefresh(e.target.checked)}
|
||
className="rounded border-gray-300"
|
||
/>
|
||
<span className="text-sm text-gray-700 dark:text-gray-300">Автообновление (30с)</span>
|
||
</label>
|
||
<button
|
||
onClick={loadServers}
|
||
className="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700"
|
||
>
|
||
Обновить
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
||
{servers.map(server => {
|
||
const data = monitoringData[server.id];
|
||
const isOnline = data?.ping?.online;
|
||
|
||
return (
|
||
<div key={server.id} className="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 border border-gray-200 dark:border-gray-700">
|
||
<div className="flex justify-between items-start mb-4">
|
||
<div>
|
||
<h3 className="font-semibold text-lg text-gray-800 dark:text-white">{server.name}</h3>
|
||
<p className="text-sm text-gray-600 dark:text-gray-400">{server.ip}</p>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<div className={`w-3 h-3 rounded-full ${getStatusColor(isOnline)}`}></div>
|
||
<span className={`text-sm font-medium ${isOnline ? 'text-green-600' : 'text-red-600'}`}>
|
||
{isOnline ? 'Онлайн' : 'Офлайн'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{data && (
|
||
<div className="space-y-4">
|
||
{/* Ping информация */}
|
||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||
<div>
|
||
<span className="text-gray-600 dark:text-gray-400">Ping:</span>
|
||
<div className="font-semibold text-gray-800 dark:text-white">
|
||
{data.ping?.responseTime ? `${data.ping.responseTime}ms` : 'N/A'}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span className="text-gray-600 dark:text-gray-400">Потери:</span>
|
||
<div className="font-semibold text-gray-800 dark:text-white">
|
||
{data.ping?.packetLoss ? `${data.ping.packetLoss}%` : 'N/A'}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* CPU Usage */}
|
||
{(data.snmp?.cpuLoad || data.ssh?.cpuLoad) && (
|
||
<div>
|
||
<div className="flex justify-between text-sm mb-1">
|
||
<span className="text-gray-600 dark:text-gray-400">Загрузка CPU</span>
|
||
<span className="font-semibold text-gray-800 dark:text-white">
|
||
{Math.round(data.ssh?.cpuLoad || data.snmp?.cpuLoad)}%
|
||
</span>
|
||
</div>
|
||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||
<div
|
||
className={`h-2 rounded-full ${getUsageColor(data.ssh?.cpuLoad || data.snmp?.cpuLoad)}`}
|
||
style={{ width: `${Math.min(100, data.ssh?.cpuLoad || data.snmp?.cpuLoad)}%` }}
|
||
></div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Memory Usage */}
|
||
{(data.ssh?.memoryUsed && data.ssh?.memoryTotal) && (
|
||
<div>
|
||
<div className="flex justify-between text-sm mb-1">
|
||
<span className="text-gray-600 dark:text-gray-400">Память</span>
|
||
<span className="font-semibold text-gray-800 dark:text-white">
|
||
{Math.round((data.ssh.memoryUsed / data.ssh.memoryTotal) * 100)}%
|
||
</span>
|
||
</div>
|
||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||
<div
|
||
className={`h-2 rounded-full ${getUsageColor((data.ssh.memoryUsed / data.ssh.memoryTotal) * 100)}`}
|
||
style={{ width: `${Math.min(100, (data.ssh.memoryUsed / data.ssh.memoryTotal) * 100)}%` }}
|
||
></div>
|
||
</div>
|
||
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||
{data.ssh.memoryUsed}MB / {data.ssh.memoryTotal}MB
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Last update */}
|
||
<div className="text-xs text-gray-500 dark:text-gray-400 border-t pt-2">
|
||
Обновлено: {data.lastUpdate?.toLocaleTimeString()}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{!data && (
|
||
<div className="text-center py-4 text-gray-500 dark:text-gray-400">
|
||
Загрузка данных...
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{servers.length === 0 && (
|
||
<div className="text-center py-12">
|
||
<div className="text-gray-400 text-6xl mb-4">📊</div>
|
||
<h3 className="text-lg font-medium text-gray-900 dark:text-white">Нет серверов для мониторинга</h3>
|
||
<p className="text-gray-500 dark:text-gray-400 mt-1">Добавьте серверы в разделе "Серверы"</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default ServerMonitoring; |