276 lines
13 KiB
Twig
Executable File
276 lines
13 KiB
Twig
Executable File
{% extends "layout.twig" %}
|
||
|
||
{% block content %}
|
||
<div class="row mb-4">
|
||
|
||
<div class="col-12 d-flex justify-content-between align-items-center">
|
||
<h2><i class="fas fa-tachometer-alt"></i> Дашборд мониторинга</h2>
|
||
<div>
|
||
<a href="/servers/create" class="btn btn-primary">
|
||
<i class="fas fa-plus"></i> Добавить сервер
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Статистика -->
|
||
<div class="row mb-4">
|
||
<div class="col-md-3 col-sm-6 mb-3">
|
||
<div class="card text-white bg-primary">
|
||
<div class="card-body text-center">
|
||
<i class="fas fa-server fa-2x mb-2"></i>
|
||
<h3>{{ stats.total_servers }}</h3>
|
||
<p class="mb-0">Всего серверов</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3 col-sm-6 mb-3">
|
||
<div class="card text-white bg-success">
|
||
<div class="card-body text-center">
|
||
<i class="fas fa-check-circle fa-2x mb-2"></i>
|
||
<h3>{{ stats.servers_with_metrics }}</h3>
|
||
<p class="mb-0">С метриками</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3 col-sm-6 mb-3">
|
||
<div class="card text-white bg-warning">
|
||
<div class="card-body text-center">
|
||
<i class="fas fa-exclamation-triangle fa-2x mb-2"></i>
|
||
<h3>{{ stats.warnings }}</h3>
|
||
<p class="mb-0">Предупреждения</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-3 col-sm-6 mb-3">
|
||
<div class="card text-white bg-danger">
|
||
<div class="card-body text-center">
|
||
<i class="fas fa-radiation fa-2x mb-2"></i>
|
||
<h3>{{ stats.criticals }}</h3>
|
||
<p class="mb-0">Критические</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Карточки серверов -->
|
||
<!-- DEBUG: {{ servers|json_encode }} -->
|
||
{% for s in servers %}<!-- DBG: id={{ s.id }} t={{ s.thresholds|json_encode }} -->{% endfor %}
|
||
<div class="row">
|
||
{% for server in servers %}
|
||
<!-- DEBUG: server_id={{ server.id }} thresholds={{ server.thresholds|json_encode }} ram_metric={{ server.latest_metrics["ram_used"].value }} -->
|
||
|
||
<div class="col-xl-4 col-lg-6 col-md-6 mb-4">
|
||
<div class="card h-100 server-card" data-server-id="{{ server.id }}" data-status="{{ server.status }}">
|
||
<div class="card-header d-flex justify-content-between align-items-center
|
||
{% if server.status == 'online' %}bg-success text-white
|
||
{% elseif server.status == 'warning' %}bg-warning text-dark
|
||
{% else %}bg-danger text-white{% endif %}">
|
||
<h5 class="mb-0">
|
||
<i class="fas fa-server"></i> {{ server.name }}
|
||
</h5>
|
||
<div>
|
||
{% if server.status == 'online' %}
|
||
<span class="badge bg-light text-dark">
|
||
<i class="fas fa-check-circle"></i> Онлайн
|
||
</span>
|
||
{% elseif server.status == 'warning' %}
|
||
<span class="badge bg-dark">
|
||
<i class="fas fa-exclamation-triangle"></i> Внимание
|
||
</span>
|
||
{% else %}
|
||
<span class="badge bg-light text-dark">
|
||
<i class="fas fa-times-circle"></i> Оффлайн
|
||
</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
{% if server.group_name %}
|
||
<div class="mb-2">
|
||
<span class="badge" style="background-color: {{ server.group_color|default('#6c757d') }}">
|
||
<i class="fas {{ server.group_icon|default('fa-box') }}"></i> {{ server.group_name }}
|
||
</span>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if server.description %}
|
||
<p class="text-muted small mb-3">{{ server.description }}</p>
|
||
{% endif %}
|
||
|
||
<div class="mb-2"><span class="badge bg-info">Статус: {{ server.status }}</span></div>
|
||
|
||
<!-- Метрики -->
|
||
{% for s in servers %}<!-- DBG: id={{ s.id }} t={{ s.thresholds|json_encode }} -->{% endfor %}
|
||
<div class="row">
|
||
{% if server.latest_metrics['cpu_load'] is defined %}
|
||
<div class="col-6 mb-2">
|
||
<div class="d-flex justify-content-between">
|
||
<small class="text-muted"><i class="fas fa-microchip"></i> CPU</small>
|
||
<strong>{{ server.latest_metrics['cpu_load'].value }}{{ server.latest_metrics['cpu_load'].unit }}</strong>
|
||
</div>
|
||
{% set cpu_t = server.thresholds['cpu_load']|default(null) %}
|
||
{% if cpu_t and server.latest_metrics['cpu_load'].value >= cpu_t.critical %}
|
||
{% set cpu_color = 'bg-danger' %}
|
||
{% elseif cpu_t and server.latest_metrics['cpu_load'].value >= cpu_t.warning %}
|
||
{% set cpu_color = 'bg-warning' %}
|
||
{% elseif server.latest_metrics['cpu_load'].value > 80 %}
|
||
{% set cpu_color = 'bg-danger' %}
|
||
{% elseif server.latest_metrics['cpu_load'].value > 60 %}
|
||
{% set cpu_color = 'bg-warning' %}
|
||
{% else %}
|
||
{% set cpu_color = 'bg-success' %}
|
||
{% endif %}
|
||
<div class="progress" style="height: 6px;">
|
||
<div class="progress-bar {{ cpu_color }}"
|
||
role="progressbar"
|
||
style="width: {{ server.latest_metrics['cpu_load'].value }}%"></div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if server.latest_metrics['ram_used'] is defined %}
|
||
<div class="col-6 mb-2">
|
||
<div class="d-flex justify-content-between">
|
||
<small class="text-muted"><i class="fas fa-memory"></i> RAM</small>
|
||
<strong>{{ server.latest_metrics['ram_used'].value }}{{ server.latest_metrics['ram_used'].unit }}</strong>
|
||
</div>
|
||
{% set ram_t = server.thresholds['ram_used']|default(null) %}
|
||
{% if ram_t and server.latest_metrics['ram_used'].value >= ram_t.critical %}
|
||
{% set ram_color = 'bg-danger' %}
|
||
{% elseif ram_t and server.latest_metrics['ram_used'].value >= ram_t.warning %}
|
||
{% set ram_color = 'bg-warning' %}
|
||
{% elseif server.latest_metrics['ram_used'].value > 80 %}
|
||
{% set ram_color = 'bg-danger' %}
|
||
{% elseif server.latest_metrics['ram_used'].value > 60 %}
|
||
{% set ram_color = 'bg-warning' %}
|
||
{% else %}
|
||
{% set ram_color = 'bg-success' %}
|
||
{% endif %}
|
||
<div class="progress" style="height: 6px;">
|
||
<div class="progress-bar {{ ram_color }}"
|
||
role="progressbar"
|
||
style="width: {{ server.latest_metrics['ram_used'].value }}%"></div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% set diskMetric = server.latest_metrics['disk_used_root'] is defined ? server.latest_metrics['disk_used_root'] : server.latest_metrics['disk_used'] %}
|
||
{% if diskMetric is defined %}
|
||
<div class="col-6 mb-2">
|
||
<div class="d-flex justify-content-between">
|
||
<small class="text-muted"><i class="fas fa-hdd"></i> Диск (/)</small>
|
||
<strong>{{ diskMetric.value }}{{ diskMetric.unit|default('%') }}</strong>
|
||
</div>
|
||
{% set disk_t = server.thresholds['disk_used_root'] is defined ? server.thresholds['disk_used_root'] : null %}
|
||
{% if disk_t and diskMetric.value >= disk_t.critical %}
|
||
{% set disk_color = 'bg-danger' %}
|
||
{% elseif disk_t and diskMetric.value >= disk_t.warning %}
|
||
{% set disk_color = 'bg-warning' %}
|
||
{% elseif diskMetric.value > 80 %}
|
||
{% set disk_color = 'bg-danger' %}
|
||
{% elseif diskMetric.value > 60 %}
|
||
{% set disk_color = 'bg-warning' %}
|
||
{% else %}
|
||
{% set disk_color = 'bg-success' %}
|
||
{% endif %}
|
||
<div class="progress" style="height: 6px;">
|
||
<div class="progress-bar {{ disk_color }}"
|
||
role="progressbar"
|
||
style="width: {{ diskMetric.value }}%"></div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if server.active_alerts > 0 %}
|
||
<div class="alert alert-danger py-2 mb-2">
|
||
<small>
|
||
<i class="fas fa-bell"></i>
|
||
Активных алертов: <strong>{{ server.active_alerts }}</strong>
|
||
</small>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Время обновления -->
|
||
<div class="text-muted small mt-2">
|
||
<i class="fas fa-clock"></i>
|
||
{% if server.last_metrics_at %}
|
||
Обновлено: {{ server.last_metrics_at|date('d.m.Y H:i:s') }}
|
||
{% else %}
|
||
Метрики не получены
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="card-footer bg-light">
|
||
<div class="d-flex justify-content-between">
|
||
<a href="/servers/{{ server.id }}" class="btn btn-sm btn-outline-primary">
|
||
<i class="fas fa-chart-line"></i> Подробнее
|
||
</a>
|
||
<a href="/servers/{{ server.id }}/edit" class="btn btn-sm btn-outline-secondary">
|
||
<i class="fas fa-edit"></i> Изменить
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-body text-center py-5">
|
||
<i class="fas fa-server fa-4x text-muted mb-3"></i>
|
||
<h4>Серверы пока не добавлены</h4>
|
||
<p class="lead text-muted">Добавьте первый сервер, чтобы начать мониторинг</p>
|
||
<a href="/servers/create" class="btn btn-primary btn-lg">
|
||
<i class="fas fa-plus"></i> Добавить первый сервер
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
<!-- Автообновление каждые 30 секунд -->
|
||
<script>
|
||
// Автообновление через 30 секунд
|
||
setTimeout(function() {
|
||
location.reload();
|
||
}, 30000);
|
||
|
||
// Визуальное обновление карточек с анимацией
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Добавляем плавную анимацию при наведении
|
||
document.querySelectorAll('.server-card').forEach(function(card) {
|
||
card.style.transition = 'transform 0.2s ease, box-shadow 0.2s ease';
|
||
card.addEventListener('mouseenter', function() {
|
||
this.style.transform = 'translateY(-4px)';
|
||
this.style.boxShadow = '0 8px 25px rgba(0,0,0,0.15)';
|
||
});
|
||
card.addEventListener('mouseleave', function() {
|
||
this.style.transform = 'translateY(0)';
|
||
this.style.boxShadow = '';
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<style>
|
||
.server-card {
|
||
border: none;
|
||
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||
}
|
||
.server-card:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||
}
|
||
.server-card .card-header {
|
||
border-radius: 0.5rem 0.5rem 0 0;
|
||
}
|
||
.progress {
|
||
border-radius: 3px;
|
||
}
|
||
</style>
|
||
|
||
{% endblock %}
|