127 lines
5.9 KiB
HTML
127 lines
5.9 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Дашборд — InfoUser Monitor{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2>Компьютеры</h2>
|
|
<div class="d-flex gap-2">
|
|
<a href="/api/computers/export/csv?{{ request.query_params }}" class="btn btn-outline-success btn-sm" target="_blank">
|
|
<i class="bi bi-download"></i> Экспорт CSV
|
|
</a>
|
|
<a href="/notifications" class="btn btn-outline-info btn-sm">
|
|
<i class="bi bi-bell"></i> Уведомления {% if unread_count %}<span class="badge bg-danger">{{ unread_count }}</span>{% endif %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<form id="filter-form" class="row g-2 mb-4 p-3 bg-white rounded shadow-sm" method="get" action="/">
|
|
<div class="col-md-2">
|
|
<select name="status" class="form-select form-select-sm">
|
|
<option value="">Все статусы</option>
|
|
<option value="online" {% if filters.status == 'online' %}selected{% endif %}>Online</option>
|
|
<option value="offline" {% if filters.status == 'offline' %}selected{% endif %}>Offline</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<input type="text" name="user" class="form-control form-control-sm" placeholder="Пользователь" value="{{ filters.user }}">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<input type="text" name="search" class="form-control form-control-sm" placeholder="Поиск по хосту/IP/OS" value="{{ filters.search }}">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select name="sort" class="form-select form-select-sm">
|
|
<option value="hostname" {% if filters.sort == 'hostname' %}selected{% endif %}>Сортировка: хост</option>
|
|
<option value="last_seen" {% if filters.sort == 'last_seen' %}selected{% endif %}>Последний чек</option>
|
|
<option value="current_user" {% if filters.sort == 'current_user' %}selected{% endif %}>Пользователь</option>
|
|
<option value="status" {% if filters.sort == 'status' %}selected{% endif %}>Статус</option>
|
|
<option value="current_cpu_percent" {% if filters.sort == 'current_cpu_percent' %}selected{% endif %}>CPU</option>
|
|
<option value="current_ram_percent" {% if filters.sort == 'current_ram_percent' %}selected{% endif %}>RAM</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<select name="order" class="form-select form-select-sm">
|
|
<option value="asc" {% if filters.order == 'asc' %}selected{% endif %}>Asc</option>
|
|
<option value="desc" {% if filters.order == 'desc' %}selected{% endif %}>Desc</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="submit" class="btn btn-primary btn-sm w-100">Применить</button>
|
|
</div>
|
|
</form>
|
|
|
|
<table class="table table-hover align-middle" id="computers-table">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Хост</th>
|
|
<th>Пользователь</th>
|
|
<th>IP</th>
|
|
<th>Статус</th>
|
|
<th>CPU</th>
|
|
<th>RAM</th>
|
|
<th>OS</th>
|
|
<th>Последний чек</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for c in computers %}
|
|
<tr data-id="{{ c.id }}">
|
|
<td><strong>{{ c.hostname }}</strong></td>
|
|
<td>{{ c.current_user or '-' }}</td>
|
|
<td>{{ c.current_ip or '-' }}</td>
|
|
<td>
|
|
{% if c.status == 'online' %}
|
|
<span class="badge bg-success">Online</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">Offline</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ c.current_cpu_percent | round(1) if c.current_cpu_percent is not none else '-' }}%</td>
|
|
<td>{{ c.current_ram_percent | round(1) if c.current_ram_percent is not none else '-' }}%</td>
|
|
<td>{{ c.os_info or '-' }}</td>
|
|
<td class="last-seen">{% if c.last_seen %}{{ c.last_seen.strftime('%Y-%m-%d %H:%M') }}{% else %}-{% endif %}</td>
|
|
<td><a href="/computers/{{ c.id }}" class="btn btn-sm btn-outline-primary">Детали</a></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
{% if not computers %}
|
|
<div class="alert alert-info">Пока нет данных. Установите и запустите агент на клиентских ПК.</div>
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
function currentQuery() {
|
|
return new URLSearchParams(window.location.search).toString();
|
|
}
|
|
|
|
async function loadDashboard() {
|
|
try {
|
|
const res = await fetch('/api/computers?' + currentQuery());
|
|
if (!res.ok) return location.reload();
|
|
const data = await res.json();
|
|
const tbody = document.querySelector('#computers-table tbody');
|
|
tbody.innerHTML = data.map(c => `
|
|
<tr data-id="${c.id}">
|
|
<td><strong>${escapeHtml(c.hostname)}</strong></td>
|
|
<td>${escapeHtml(c.current_user || '-')}</td>
|
|
<td>${escapeHtml(c.current_ip || '-')}</td>
|
|
<td>${c.status === 'online' ? '<span class="badge bg-success">Online</span>' : '<span class="badge bg-secondary">Offline</span>'}</td>
|
|
<td>${c.current_cpu_percent != null ? c.current_cpu_percent.toFixed(1) : '-'}%</td>
|
|
<td>${c.current_ram_percent != null ? c.current_ram_percent.toFixed(1) : '-'}%</td>
|
|
<td>${escapeHtml(c.os_info || '-')}</td>
|
|
<td class="last-seen">${c.last_seen ? c.last_seen.replace('T', ' ').slice(0, 16) : '-'}</td>
|
|
<td><a href="/computers/${c.id}" class="btn btn-sm btn-outline-primary">Детали</a></td>
|
|
</tr>
|
|
`).join('');
|
|
} catch (e) {
|
|
console.error('Dashboard refresh failed', e);
|
|
}
|
|
}
|
|
setInterval(loadDashboard, 60000);
|
|
</script>
|
|
{% endblock %}
|