diff --git a/src/Controllers/ServerDetailController.php b/src/Controllers/ServerDetailController.php index 4c43241..75c6e7a 100755 --- a/src/Controllers/ServerDetailController.php +++ b/src/Controllers/ServerDetailController.php @@ -86,7 +86,7 @@ class ServerDetailController extends Model } // Получаем все типы метрик - $stmt = $this->pdo->query("SELECT id, name, unit FROM metric_names WHERE name NOT LIKE '%_proc' ORDER BY name"); + $stmt = $this->pdo->query("SELECT id, name, unit FROM metric_names WHERE name NOT LIKE '%\_proc' ORDER BY name"); $allMetricTypes = $stmt->fetchAll(); // Получаем список сервисов @@ -132,7 +132,7 @@ class ServerDetailController extends Model $params = $request->getParsedBody(); // Получаем все типы метрик - $stmt = $this->pdo->query("SELECT id, name FROM metric_names WHERE name NOT LIKE '%_proc' ORDER BY name"); + $stmt = $this->pdo->query("SELECT id, name FROM metric_names WHERE name NOT LIKE '%\_proc' ORDER BY name"); $metricTypes = $stmt->fetchAll(); // Удаляем старые пороги для этого сервера diff --git a/templates/servers/detail.twig b/templates/servers/detail.twig index 8862ff9..e1a8d3f 100755 --- a/templates/servers/detail.twig +++ b/templates/servers/detail.twig @@ -500,12 +500,61 @@ new Chart(ctx{{ metricName|replace({'-': '_', '.': '_'}) }}, { enabled: true, mode: 'index', intersect: false, - callbacks: { - afterBody: function(context) { - var dataIndex = context[0].dataIndex; - var time = labels{{ metricName }}[dataIndex]; - return fetchProcesses({{ server.id }}, time); + external: function(context) { + // Tooltip element + var tooltipEl = document.getElementById('chartjs-tooltip-' + {{ server.id }}); + + if (!tooltipEl) { + tooltipEl = document.createElement('div'); + tooltipEl.id = 'chartjs-tooltip-' + {{ server.id }}; + tooltipEl.style.opacity = 0; + tooltipEl.style.position = 'absolute'; + tooltipEl.style.background = 'rgba(0,0,0,0.7)'; + tooltipEl.style.color = 'white'; + tooltipEl.style.borderRadius = '3px'; + tooltipEl.style.padding = '10px'; + tooltipEl.style.pointerEvents = 'none'; + document.body.appendChild(tooltipEl); } + + var dataIndex = context[0].dataIndex; + var time = labels{{ metricName }}[dataIndex]; + + // Fetch processes + fetch('/api/v1/agent/' + {{ server.id }} + '/processes?time=' + encodeURIComponent(time)) + .then(response => response.json()) + .then(data => { + var lines = []; + lines.push('{{ metricName|replace({'_': ' ', 'load': 'загрузка', 'used': 'использование'})|title }}: ' + data{{ metricName }}[dataIndex]); + + if (data.top_cpu && data.top_cpu.length > 0) { + lines.push(''); + lines.push('TOP CPU:'); + data.top_cpu.forEach(function(proc) { + lines.push(' ' + proc.name + ': ' + proc.value + '%'); + }); + } + + if (data.top_ram && data.top_ram.length > 0) { + lines.push(''); + lines.push('TOP RAM:'); + data.top_ram.forEach(function(proc) { + lines.push(' ' + proc.name + ': ' + proc.value + '%'); + }); + } + + // Show tooltip + var position = context.chart.canvas.getBoundingClientRect(); + tooltipEl.innerHTML = lines.join('
'); + tooltipEl.style.opacity = 1; + tooltipEl.style.left = position.left + window.pageXOffset + context.tooltip.caretX + 10 + 'px'; + tooltipEl.style.top = position.top + window.pageYOffset + context.tooltip.caretY + 'px'; + + // Hide after 3 seconds + setTimeout(function() { + tooltipEl.style.opacity = 0; + }, 3000); + }); } } }