240 lines
10 KiB
HTML
240 lines
10 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Инфраструктура - Домовой Бот{% endblock %}
|
||
{% block nav_infra %}active{% endblock %}
|
||
|
||
{% block content %}
|
||
<h2 class="mb-4"><i class="bi bi-cpu"></i> Мониторинг Инфраструктуры</h2>
|
||
|
||
<div class="row mb-4">
|
||
<div class="col-md-12">
|
||
<div class="card bg-dark border-success">
|
||
<div class="card-header border-success text-success">
|
||
<i class="bi bi-speedometer2"></i> ТЕСТ СКОРОСТИ ИНТЕРНЕТА (150 Mbps План)
|
||
</div>
|
||
<div class="card-body">
|
||
<table class="table table-dark table-hover small">
|
||
<thead>
|
||
<tr>
|
||
<th>Дата и Время</th>
|
||
<th>Download (Mbps)</th>
|
||
<th>Upload (Mbps)</th>
|
||
<th>Ping (ms)</th>
|
||
<th>Сервер</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for test in speedtests %}
|
||
<tr>
|
||
<td>{{ test.timestamp }}</td>
|
||
<td class="text-info">{{ test.download }}</td>
|
||
<td class="text-warning">{{ test.upload }}</td>
|
||
<td>{{ test.ping }}</td>
|
||
<td class="opacity-50">{{ test.server_name }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="card bg-dark border-info">
|
||
<div class="card-header text-info">УЗЛЫ СЕТИ</div>
|
||
<div class="card-body">
|
||
<ul class="list-group list-group-flush bg-transparent">
|
||
<li class="list-group-item bg-transparent text-white border-secondary">
|
||
<span class="badge bg-success">ONLINE</span> NanoPi R3S (192.168.10.1)
|
||
</li>
|
||
<li class="list-group-item bg-transparent text-white border-secondary">
|
||
<span class="badge bg-success">ONLINE</span> Synology NAS (192.168.10.105)
|
||
</li>
|
||
<li class="list-group-item bg-transparent text-white border-secondary">
|
||
<span class="badge bg-success">ONLINE</span> DockerServer (192.168.10.116)
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="card bg-dark border-primary">
|
||
<div class="card-header text-primary">ГИС ЖКХ & ЮРИДИЧЕСКИЙ АРХИВ</div>
|
||
<div class="card-body">
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<button id="runGisHarvestBtn" class="btn btn-outline-primary flex-grow-1 me-2">
|
||
<i class="bi bi-cloud-download"></i> ЗАПУСТИТЬ ЖАТВУ И PDF-СБОРКУ
|
||
</button>
|
||
<div id="gisStatusBadge" class="badge bg-secondary p-2">СТАТУС: НЕИЗВЕСТНО</div>
|
||
</div>
|
||
<div class="mb-2 d-flex justify-content-between">
|
||
<span class="small text-muted">ПОСЛЕДНИЕ ЛОГИ ЖНЕЦА:</span>
|
||
<button id="refreshGisStatusBtn" class="btn btn-sm btn-link p-0 text-primary">ОБНОВИТЬ СТАТУС</button>
|
||
</div>
|
||
<pre id="gisLogs" class="bg-black text-success p-2 small border border-secondary" style="height: 200px; overflow-y: scroll; font-size: 0.7rem;"></pre>
|
||
<div class="small opacity-50 mt-2">Выполняет: Выкачка (Overwrite) -> Теггирование -> Склейка -> Google Drive</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="card bg-dark border-danger">
|
||
<div class="card-header text-danger">ГАС ПРАВОСУДИЕ (СУДЕБНЫЙ АРХИВ)</div>
|
||
<div class="card-body">
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<button id="runSudrfParserBtn" class="btn btn-outline-danger flex-grow-1 me-2">
|
||
<i class="bi bi-play-circle"></i> ЗАПУСТИТЬ ЖАТВУ СУДОВ
|
||
</button>
|
||
<div id="sudrfStatusBadge" class="badge bg-secondary p-2">СТАТУС: НЕИЗВЕСТНО</div>
|
||
</div>
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<a href="http://192.168.10.116:6080/vnc.html" target="_blank" class="btn btn-outline-info w-100">
|
||
<i class="bi bi-window"></i> ОТКРЫТЬ VNC БРАУЗЕР (ДЛЯ ЕСИА)
|
||
</a>
|
||
</div>
|
||
<div class="mb-2 d-flex justify-content-between">
|
||
<span class="small text-muted">ПОСЛЕДНИЕ ЛОГИ:</span>
|
||
<button id="refreshSudrfStatusBtn" class="btn btn-sm btn-link p-0 text-danger">ОБНОВИТЬ СТАТУС</button>
|
||
</div>
|
||
<pre id="sudrfLogs" class="bg-black text-danger p-2 small border border-secondary" style="height: 200px; overflow-y: scroll; font-size: 0.7rem;"></pre>
|
||
<div class="small opacity-50 mt-2">Выполняет: Открытие сессии -> Ввод 2FA пользователем -> Парсинг ej.sudrf.ru -> Сборка PDF -> Google Drive</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="card bg-dark border-warning">
|
||
<div class="card-header text-warning">ДЕЙСТВИЯ</div>
|
||
<div class="card-body">
|
||
<button id="runSpeedtestBtn" class="btn btn-outline-warning w-100 mb-2">
|
||
<i class="bi bi-play-fill"></i> ЗАПУСТИТЬ ТЕСТ СКОРОСТИ СЕЙЧАС
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_script %}
|
||
<script>
|
||
document.getElementById('runSpeedtestBtn').addEventListener('click', async function() {
|
||
const btn = this;
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> ТЕСТИРУЮ (60с)...';
|
||
|
||
try {
|
||
const response = await fetch('/api/infra/run_speedtest', { method: 'POST' });
|
||
const result = await response.json();
|
||
alert(result.message);
|
||
location.reload();
|
||
} catch (e) {
|
||
alert('Ошибка: ' + e);
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.innerHTML = '<i class="bi bi-play-fill"></i> ЗАПУСТИТЬ ТЕСТ СКОРОСТИ СЕЙЧАС';
|
||
}
|
||
});
|
||
|
||
document.getElementById('runGisHarvestBtn').addEventListener('click', async function() {
|
||
const btn = this;
|
||
if (!confirm('Запустить тотальную перекачку ГИС ЖКХ и сборку PDF? Это займет значительное время.')) return;
|
||
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> ЖАТВА В ПРОЦЕССЕ...';
|
||
|
||
try {
|
||
const response = await fetch('/api/infra/run_gis_harvest', { method: 'POST' });
|
||
const result = await response.json();
|
||
alert(result.message);
|
||
} catch (e) {
|
||
alert('Ошибка: ' + e);
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.innerHTML = '<i class="bi bi-cloud-download"></i> ЗАПУСТИТЬ ЖАТВУ И PDF-СБОРКУ';
|
||
refreshGisStatus();
|
||
}
|
||
});
|
||
|
||
async function refreshGisStatus() {
|
||
const badge = document.getElementById('gisStatusBadge');
|
||
const logs = document.getElementById('gisLogs');
|
||
|
||
try {
|
||
const response = await fetch('/api/infra/gis_status');
|
||
const data = await response.json();
|
||
|
||
if (data.alive) {
|
||
badge.className = 'badge bg-success p-2';
|
||
badge.innerText = 'СТАТУС: РАБОТАЕТ';
|
||
} else {
|
||
badge.className = 'badge bg-danger p-2';
|
||
badge.innerText = 'СТАТУС: ОСТАНОВЛЕН';
|
||
}
|
||
|
||
logs.innerText = data.log;
|
||
logs.scrollTop = logs.scrollHeight;
|
||
} catch (e) {
|
||
badge.className = 'badge bg-warning p-2';
|
||
badge.innerText = 'ОШИБКА API';
|
||
}
|
||
}
|
||
|
||
document.getElementById('runSudrfParserBtn').addEventListener('click', async function() {
|
||
const btn = this;
|
||
if (!confirm('Запустить сборку судебного архива ГАС Правосудие? Потребуется войти через Госуслуги во VNC.')) return;
|
||
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> СЕССИЯ СУДОВ...';
|
||
|
||
try {
|
||
const response = await fetch('/api/infra/run_sudrf_parser', { method: 'POST' });
|
||
const result = await response.json();
|
||
alert(result.message);
|
||
} catch (e) {
|
||
alert('Ошибка: ' + e);
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.innerHTML = '<i class="bi bi-play-circle"></i> ЗАПУСТИТЬ ЖАТВУ СУДОВ';
|
||
refreshSudrfStatus();
|
||
}
|
||
});
|
||
|
||
async function refreshSudrfStatus() {
|
||
const badge = document.getElementById('sudrfStatusBadge');
|
||
const logs = document.getElementById('sudrfLogs');
|
||
|
||
try {
|
||
const response = await fetch('/api/infra/sudrf_status');
|
||
const data = await response.json();
|
||
|
||
if (data.alive) {
|
||
badge.className = 'badge bg-success p-2';
|
||
badge.innerText = 'СТАТУС: РАБОТАЕТ';
|
||
} else {
|
||
badge.className = 'badge bg-danger p-2';
|
||
badge.innerText = 'СТАТУС: ОСТАНОВЛЕН';
|
||
}
|
||
|
||
logs.innerText = data.log;
|
||
logs.scrollTop = logs.scrollHeight;
|
||
} catch (e) {
|
||
badge.className = 'badge bg-warning p-2';
|
||
badge.innerText = 'ОШИБКА API';
|
||
}
|
||
}
|
||
|
||
document.getElementById('refreshGisStatusBtn').addEventListener('click', refreshGisStatus);
|
||
document.getElementById('refreshSudrfStatusBtn').addEventListener('click', refreshSudrfStatus);
|
||
|
||
window.addEventListener('load', () => {
|
||
refreshGisStatus();
|
||
refreshSudrfStatus();
|
||
});
|
||
|
||
// Авто-обновление раз в 30 секунд
|
||
setInterval(() => {
|
||
refreshGisStatus();
|
||
refreshSudrfStatus();
|
||
}, 30000);
|
||
</script>
|
||
{% endblock %}
|