Пункт 18 ревью 2026-09-28 21:32 (docs/changes/027): после отката ROS
плата остаётся на более новой прошивке, а приложение сравнивало версии
на неравенство и показывало откат как обновление.
- devices.fw_state: unknown/update/downgrade/current; has_fw_update и
фильтры не считают откат обновлением; метка «в ROS: X».
- upgrade_firmware не понижает прошивку; общая запись — _flash_firmware.
- Задача fw_downgrade: проверка → обязательный бэкап → запись →
перезагрузка; API POST /devices/{id}/firmware/downgrade и
/batch/fw_downgrade (target_version обязателен).
- UI: «Откатить прошивку…» (строка и группа), общее окно отката с kind.
Тесты: 39 из 39. Стенд: временное недоступное устройство — 422/202,
задачи failed на проверке, бэкапов нет. Реальная запись прошивки при
откате и ручная проверка UI не выполнялись.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
После смены канала на long-term колонка показывала «актуально», хотя
версия канала (7.23.7) старше установленной (7.24.4) — docs/changes/023:
- состояния колонки: «↑ X», «актуально», «канал: X» (версия канала старше
установленной), «проверка не удалась» (ошибка check-for-updates больше
не маскируется под «актуально»), «—»;
- откат ROS до версии канала как отдельная задача ros_downgrade: проверка
версии на устройстве → обязательный бэкап (сбой прерывает) → install;
- UI: «Откатить ROS…» в меню устройства и групповой пункт в «Обновление»,
окно с вводом целевой версии, список не затрагиваемых устройств;
- API: POST /devices/{id}/update/downgrade и /batch/ros_downgrade с
обязательным target_version. Штатное обновление откат не выполняет.
Тесты: 33 из 33. Стенд: 422 без версии, задачи отката на недоступном
устройстве завершаются на проверке без бэкапа. Реальный откат и ручная
проверка UI пользователем на момент коммита не выполнены.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Повторное ревью кодовой базы: docs/reviews/2026-09-28-1243-codebase-review.md
(статус 12 замечаний, новые замечания 13–16).
Остаток п. 9 (docs/changes/022):
- 14 async-функций API, UI и сервисов больше не обращаются к SQLite
напрямую — через asyncio.to_thread; jobs.start_jobs стал async (БД в
потоке, create_task в event loop); ops._conn для подключения к устройству;
- тест-линтер по AST: в async def нет прямых вызовов функций с
session_scope — защита от регресса.
Тесты: 29 из 29. Стенд: задачи и актор событий в порядке, параллельные
запросы не ждут медленного устройства, боевые данные не изменены.
Ручная проверка UI пользователем на момент коммита не подтверждена.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Производительность и масштабирование, пункты 8–10 ревью (docs/changes/019):
- кэш списка бакета и связей файлов с копиями (BACKUPS_CACHE_TTL, 60 с):
sync_rows только при реальном чтении бакета; сброс после бэкапа и
удаления, счётчик поколений против гонки; refresh=1 и «Обновить список»;
- обработчики API/UI без await — обычные функции (пул потоков FastAPI),
запуск задач остаётся async; запись статуса, задачи и sync_rows —
через asyncio.to_thread; SQLite: WAL, synchronous=NORMAL, busy_timeout;
- файловая блокировка <файл БД>.lock: второй процесс на той же БД не
стартует; раздел «Ограничения» в README.
Тесты: 24 из 24. Стенд (порт 8001): кэш 0,014 с против 0,138 с, второй
uvicorn на боевой БД отклонён блокировкой, боевые данные не изменены.
Ручная проверка UI пользователем на момент коммита не подтверждена.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ревью кодовой базы: docs/reviews/2026-09-27-codebase-review.md.
Корректность и согласованность, пункты 5–7 ревью (docs/changes/018):
- одиночное удаление бэкапа в UI идёт через общий delete_many: пометка
deleted_at и событие backup.deleted, как у группового удаления и API;
- единая система миграций: ручные ALTER из db._migrate перенесены в
migrations.run (при user_version < 1, до замены ID);
- групповая смена канала выполняется фоновыми задачами set_channel;
PUT /api/v1/batch/channel → 202 {"job_ids": [...]} (ломающее изменение
API), меню «Канал» в UI выводит задачи в панель «Задачи».
Тесты: 22 из 22. Стенд проверен на порту 8001 (8000 занят посторонним
процессом), боевые данные не изменены. Ручная проверка UI пользователем
на момент коммита не подтверждена.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Глобально уникальные 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>
Групповое удаление бэкапов (docs/changes/013):
- страница «Резервные копии»: чекбоксы, «выбрать все», панель «Выбрано: N /
Удалить / Снять выбор» вместо фильтров, подтверждение, итог удаления;
- backups.delete_many: все ключи проверяются до удаления, удаление параллельное;
UI POST /backups/delete-many, API POST /api/v1/backups/delete;
- выбор строк в app.js обобщён (data-select) для устройств и бэкапов.
Поддержка CHR (docs/changes/014):
- у CHR нет /system/routerboard (HTTP 400): устройство больше не считается
недоступным, версия FW не показывается, обновление FW пропускается;
- «есть обновление ROS» определяется строгим сравнением версий (на канале
long-term последняя версия может быть старше установленной);
- в таблице указана причина недоступности: авторизация / ошибка ответа /
нет соединения.
Тесты: 11 из 11.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Control Server (FastAPI) с WEB UI (Jinja2 + HTMX) и JSON API для группового
администрирования устройств RouterOS 7 через REST.
- Устройства: список, статус (модель, канал, версии ROS/FW, uptime, доступные
обновления), примечания, пароли шифруются (Fernet); фоновый опрос каждые 30 с
и быстрое обнаружение недоступности (таймаут соединения 4 с).
- Группы устройств и фильтры (устройства, резервные копии).
- Резервные копии: .backup и .rsc (show-sensitive) создаются через REST,
скачиваются сервером и загружаются в S3 (Yandex Object Storage).
- Обновление ROS/FW и выбор канала, групповые операции задачами.
- Интерфейс по утверждённому макету: светлая/тёмная темы, окна, шрифты IBM Plex
локально. Docker Compose, SQLite в томе, минимальный набор тестов.
- Планы и итоги каждого изменения — в docs/changes/.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>