feat: initial commit - ServerManager Pro v2.0.0

This commit is contained in:
2025-11-26 23:21:44 +03:00
commit af51c68d7f
39 changed files with 23191 additions and 0 deletions
+207
View File
@@ -0,0 +1,207 @@
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;