Глобально уникальные ID (docs/changes/016): - у устройств, групп, задач, резервных копий и записей журнала ID вида <префикс>_<uuid7> (dev_, grp_, job_, bkp_, evt_): типы не пересекаются, внутри типа ID не повторяются и сортируются по времени; - миграция БД v1 заменяет числовые ID с пересчётом ссылок (копия файла БД перед миграцией, сверка числа строк, одна транзакция); - резервная копия = пара файлов с одним bkp_ ID, файлы в S3 получают метаданные backup-id/device-id; синхронизация метаданных с бакетом; - журнал событий в БД (events): создание/изменение/удаление, задачи, бэкапы, смена online/offline, вход в UI; API чтения GET /api/v1/events. Журнал в UI, ротация и очистка (docs/changes/017): - страница «Журнал»: фильтры, подгрузка «Показать ещё», окно записи; - настройки ротации (срок и максимум записей) хранятся в БД (схема v2), ротация при старте, раз в час и после сохранения настроек; - очистка журнала только через окно с паролем пользователя, блокировка после 5 неверных попыток, остаётся запись о факте очистки; через API очистки нет. Тесты: 20 из 20. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
32 lines
1.8 KiB
HTML
32 lines
1.8 KiB
HTML
{% import "_ui.html" as ui %}
|
||
{% set running = jobs|selectattr("status", "in", ["pending", "running"])|list|length %}
|
||
{% set done = jobs|selectattr("status", "equalto", "done")|list|length %}
|
||
{% set failed = jobs|selectattr("status", "equalto", "failed")|list|length %}
|
||
<div class="section-head">
|
||
<h2>Задачи</h2>
|
||
{% if running %}<span class="badge run">{{ running }} выполняется</span>{% endif %}
|
||
{% if done %}<span class="badge">{{ done }} завершено</span>{% endif %}
|
||
{% if failed %}<span class="badge bad">{{ failed }} с ошибкой</span>{% endif %}
|
||
</div>
|
||
{% if flash %}<p class="err">{{ flash }}</p>{% endif %}
|
||
<div class="card">
|
||
<table class="grid jobs">
|
||
<colgroup><col style="width:96px"><col style="width:16%"><col style="width:16%"><col style="width:130px"><col><col style="width:150px"></colgroup>
|
||
<thead><tr><th>ID</th><th>Устройство</th><th>Операция</th><th>Статус</th><th>Сообщение</th><th>Создана (UTC)</th></tr></thead>
|
||
<tbody>
|
||
{% for j in jobs %}
|
||
<tr>
|
||
<td class="c-mono muted" title="{{ j.id }}">{{ j.id|short_id }}</td>
|
||
<td style="font-weight:500">{{ j.device_name }}</td>
|
||
<td>{{ {"backup": "Бэкап", "ros_update": "Обновление ROS", "fw_update": "Обновление FW"}.get(j.type, j.type) }}</td>
|
||
<td><span class="badge {{ {'running': 'run', 'done': 'ok', 'failed': 'bad'}.get(j.status, '') }}">{{ {"pending": "В очереди", "running": "Выполняется", "done": "Готово", "failed": "Ошибка"}.get(j.status, j.status) }}</span></td>
|
||
<td class="msg">{{ j.message }}</td>
|
||
<td class="c-small">{{ j.created_at|dt }}</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="6" class="empty">Задач пока нет.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|