mirvmon/templates/dashboard.twig

276 lines
13 KiB
Twig
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% 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><span id='cpu-val-{{ server.id }}'>{{ server.latest_metrics['cpu_load'].value }}{{ server.latest_metrics['cpu_load'].unit }}</span></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" id="cpu-bar-{{ server.id }}"
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><span id='ram-val-{{ server.id }}'>{{ server.latest_metrics['ram_used'].value }}{{ server.latest_metrics['ram_used'].unit }}</span></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" id="ram-bar-{{ server.id }}"
style="width: {{ server.latest_metrics['ram_used'].value }}%"></div>
</div>
</div>
{% endif %}
{% set diskMetric = server.latest_metrics['disk_used_root'] is defined and server.latest_metrics['disk_used_root'] ? server.latest_metrics['disk_used_root'] : (server.latest_metrics['disk_used'] is defined and server.latest_metrics['disk_used'] ? server.latest_metrics['disk_used'] : null) %}
{% if diskMetric and diskMetric.value %}
<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><span id='disk-val-{{ server.id }}'>{{ diskMetric.value }}{{ diskMetric.unit|default('%') }}</span></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" id="disk-bar-{{ server.id }}"
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 %}
Обновлено: <span id="updated-at-{{ server.id }}">{{ server.last_metrics_at|date('d.m.Y H:i:s') }}</span>
{% 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 %}