From 7c830be7213c25f9717197c7f3b22b3b5d27ab59 Mon Sep 17 00:00:00 2001 From: ayurishchev Date: Mon, 28 Sep 2026 17:32:37 +0300 Subject: [PATCH] =?UTF-8?q?README=20=D0=BF=D0=BE=20=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D1=86=D1=83=20ipam=5Fcontrol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README переверстан (docs/changes/024): быстрый старт, конфигурация одной таблицей, архитектура с деревом каталогов, функциональность и правила по разделам (таблица состояний «Upgrade ROS»), API по областям с соглашениями, безопасность, эксплуатация (в т.ч. копия БД в режиме WAL, стенд на порту 8001), интерфейс, тесты, история изменений таблицей со ссылками на план и итог, отчёты ревью. Повторы убраны. Все 51 ссылка на документы проверены; переменные, эндпоинты и число тестов сверены с кодом. Код не менялся. Co-Authored-By: Claude Opus 5.5 --- README.md | 322 ++++++++++-------- docs/changes/024-readme-restructure/plan.md | 32 ++ .../changes/024-readme-restructure/summary.md | 18 + 3 files changed, 227 insertions(+), 145 deletions(-) create mode 100644 docs/changes/024-readme-restructure/plan.md create mode 100644 docs/changes/024-readme-restructure/summary.md diff --git a/README.md b/README.md index 708fe15..d819909 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # ros_control -Централизованное управление парком MikroTik RouterOS: WEB UI и JSON API. -Дизайн и требования — `ros_control.svg`; макет интерфейса — утверждён и перенесён в приложение (см. `docs/changes/006-ui-redesign`). +Централизованное управление парком MikroTik RouterOS: статус и мониторинг устройств, бэкапы в S3, обновление и откат ROS, +обновление прошивки, группы устройств и журнал событий. Backend на FastAPI и SQLite, WEB UI (Jinja2 + HTMX) и JSON API используют +один сервисный слой. Дизайн и требования — `ros_control.svg`, утверждённый макет интерфейса — [изменение 006](docs/changes/006-ui-redesign/plan.md). ``` Admin Dashboard ⇄ Control Server ⇄ RouterOS REST (на каждом устройстве) @@ -9,166 +10,197 @@ Admin Dashboard ⇄ Control Server ⇄ RouterOS REST (на каждом устр Metadata DB S3 (Yandex Object Storage) ``` -## Возможности - -**Устройства** -- Список устройств: добавление, изменение, удаление; пароли хранятся зашифрованно (Fernet). Имя устройства в таблице кликабельно — открывает окно изменения; **само имя задаётся только при создании и не меняется** (оно входит в ключи бэкапов). -- К устройству можно добавить **примечание** (до 500 символов): видно подсказкой при наведении на имя. -- Статус: online/offline, модель, канал обновлений, версии ROS и FW, uptime, время запроса бэкапа. Колонки **Upgrade ROS** и **Upgrade FW** показывают версию, до которой можно обновиться (устройство проверяет обновления на серверах MikroTik; без интернета на устройстве — «—»). -- Колонка **Upgrade ROS** — пять состояний: обновление доступно («↑ версия»), актуально, **«канал: версия»** — версия канала старше установленной (например, после переключения на `long-term`: это не обновление, а осознанный откат), «проверка не удалась» — последняя проверка обновлений завершилась ошибкой, «—» — проверка ещё не выполнялась. -- **Мониторинг доступности**: сервер опрашивает устройства каждые 30 с (`POLL_INTERVAL`), недоступность определяется за ~4 с на устройство (`ROS_CONNECT_TIMEOUT`); страница обновляет статусы сама, без перезагрузки. - -**Группы и фильтры** -- Устройство относится к одной группе: вкладки групп со счётчиками, страница «Группы» (создание, переименование, удаление), перенос отмеченных устройств в группу. При добавлении устройства группу можно создать «на месте» (пункт «+ Новая группа…»). -- Фильтры устройств: поиск (имя, адрес, модель), статус, наличие обновлений, канал. Фильтры бэкапов: группа, устройство, тип файла, период, имя файла. Фильтры попадают в URL. - -**Операции** -- Действия по устройству — в меню «⋯» строки; действия над выбранными устройствами (бэкап, статус, обновление ROS/FW, канал, перенос в группу) появляются в полосе инструментов при выборе строк; через API их можно запускать и над всей группой. -- **Бэкап**: `.backup` (без шифрования) и `.rsc` (с `show-sensitive`, т.е. с паролями и ключами — из него можно восстановить все сущности, использующие пароль). Сервер создаёт файлы через REST, скачивает во временную папку, загружает в S3 и удаляет файлы с устройства. -- Список бэкапов в бакете, скачивание (временная ссылка), удаление одного файла или **группы выбранных** (чекбоксы, кнопка «Удалить» с подтверждением). -- Канал обновлений; обновление ROS (устройство перезагружается после скачивания); обновление FW (перезагрузка сразу после появления в журнале устройства записи «Firmware upgraded successfully…»). -- **Откат ROS до версии канала** (состояние «канал: X»): окно с вводом целевой версии (кнопка активна только после ввода, сервер сверяет версию с кэшем статуса) — для одного устройства (пункт «Откатить ROS…» в меню «⋯», виден только в состоянии отката) и для группы (пункт «Откатить ROS до версии канала…» в меню «Обновление» с выбранными устройствами; устройства, которые откатывать нельзя, показаны отдельным списком «Не будут затронуты»). Перед откатом — обязательный бэкап: не удался — откат не запускается. Реальный откат на устройстве запускает пользователь в UI. -- Долгие и групповые операции выполняются задачами (карточка «Задачи», состояние — в UI и через API). - -## Идентификаторы и журнал событий - -У каждой сущности глобально уникальный ID вида `<префикс>_`: `dev_…` устройство, `grp_…` группа, `job_…` задача, `bkp_…` резервная копия, `evt_…` запись журнала. Префикс исключает пересечение типов, UUIDv7 не повторяется и сортируется по времени создания; ID не переиспользуются после удаления. Числовых ID нет: `GET /api/v1/devices/dev_…`, ID чужого типа или неверного формата — 404. -Резервная копия — пара файлов (`.backup` + `.rsc`) с одним `bkp_` ID; файлы в бакете имеют метаданные `backup-id`/`device-id` (Yandex возвращает их как `Backup-Id`/`Device-Id`), в списке файлов API есть `backup_id`; копия, у которой пропали файлы, помечается удалённой, но остаётся в истории. -**Журнал событий** пишется в БД: создание, изменение и удаление устройств и групп, перенос устройств, жизненный цикл задач и бэкапов, смена online/offline, вход в UI, ротация и очистка журнала. Каждая запись содержит `evt_` ID, ID сущности, актора (`ui:<пользователь>`, `api`, `poller`, `system`) и данные. -В UI — страница «Журнал» (фильтры по типу, актору, устройству, периоду и поиску по сообщению/ID; подгрузка «Показать ещё 100»; клик по строке открывает запись с полными ID и данными). -- **Ротация**: «Журнал → Настройки» — срок хранения (дней) и максимум записей, `0` — без ограничения. Настройки хранятся в БД (значения по умолчанию — `EVENTS_RETENTION_DAYS=90`, `EVENTS_MAX_ROWS=100000` в `.env`); ротация выполняется при старте, раз в час и сразу после сохранения настроек и оставляет запись `journal.rotated`. -- **Очистка**: «Журнал → Очистить журнал» открывает окно с вводом пароля пользователя. Неверный пароль журнал не трогает (`journal.clear_denied`), 5 неверных попыток за 10 минут блокируют очистку на 10 минут. После успешной очистки остаётся одна запись `journal.cleared` (кто и сколько удалил). Очистки через API нет — только через это окно; API даёт чтение журнала и настроек. - -## Интерфейс - -Экран строится сверху вниз: приложение (навигация) → страница (заголовок, счётчики, главное действие) → вкладки групп → полоса инструментов таблицы → данные; «Задачи» — отдельная карточка. Пока ничего не выбрано, полоса показывает фильтры; при выборе строк — действия над выбранными. -Добавление и изменение устройств, создание и переименование групп, откат ROS — в окнах поверх страницы (запасные страницы `/devices/new`, `/devices/{id}/edit` работают без JavaScript). Выпадающие меню не обрезаются таблицей и раскрываются вверх, если снизу нет места. Выпадающие списки (фильтры устройств/бэкапов/журнала, поле «Группа») оформлены как меню действий «⋯»: список строит JS поверх обычного ``: без JavaScript работает стандартный. +- Светлая и тёмная темы — по настройке системы. + +## Тесты +33 теста, фоновый опрос выключен; стенд не нужен (временная SQLite, RouterOS и S3 — заглушки). Тест-линтер не допускает синхронных обращений к БД в `async`-коде. +```bash +python3 -m venv venv && venv/bin/pip install -r requirements.txt +venv/bin/python -m pytest -q +``` +Локальный запуск без Docker: `set -a; . ./.env; set +a; venv/bin/uvicorn app.main:app --reload`. ## История изменений +Каждая доработка описана в `docs/changes/<номер>/`: `plan.md` — план, `summary.md` — итог. -Планы и итоги — в `docs/changes/`: -- `001-initial-implementation` — первая версия. -- `002-http-support-and-fixes` — HTTP-подключение, исправления по итогам теста на реальном устройстве. -- `003-download-via-rest-upload-from-server` — бэкап: скачивание файлов через REST, загрузка в S3 сервером. -- `004-upgrade-columns-and-actions-menu` — колонки Upgrade ROS/FW, меню действий «⋯». -- `005-groups-and-filters` — группы устройств, фильтры устройств и бэкапов. -- `006-ui-redesign` — новый интерфейс по утверждённому макету; исправлены перезагрузка после обновления FW и привязка группы при добавлении устройства. -- `007-fast-offline-detection` — фоновый опрос устройств (30 с), быстрый таймаут соединения, автообновление страницы. -- `008-device-note` — примечание к устройству. -- `009-dialog-label-alignment` — выравнивание полей в окне устройства. -- `010-export-show-sensitive` — `.rsc` создаётся с `show-sensitive` (секреты в файле). -- `011-dropdown-clipping` — выпадающие меню не обрезаются в узком окне. -- `012-clickable-device-name` — клик по имени устройства открывает окно изменения. -- `013-bulk-delete-backups` — выбор файлов чекбоксами и групповое удаление бэкапов. -- `014-chr-support` — поддержка CHR (нет `/system/routerboard`), строгое сравнение версий ROS, причина недоступности в таблице. -- `015-immutable-device-name` — имя устройства задаётся только при создании. -- `016-unique-ids-and-event-log` — глобально уникальные ID (префикс + UUIDv7) для всех сущностей, журнал событий в БД, миграция числовых ID. -- `017-events-ui-rotation` — страница «Журнал» в UI, настройки ротации, очистка журнала с подтверждением паролем. -- `018-correctness-consistency` — одиночное удаление бэкапа в UI через общий сервис `delete_many`, единая система миграций (колонки старой схемы — в `migrations.run` до миграции ID), групповая смена канала фоновыми задачами (`set_channel`). -- `019-performance-scaling` — кэш списка бакета (`BACKUPS_CACHE_TTL`, «Обновить список»); обработчики без обращений к event loop — обычные функции (пул потоков FastAPI), запись статуса и тяжёлые операции с БД в фоне — через `asyncio.to_thread`; SQLite — WAL и `busy_timeout`; файловая блокировка БД — один процесс на БД. -- `020-custom-select-menus` — выпадающие списки (фильтры, «Группа») в стиле меню действий «⋯»: прогрессивное улучшение в JS, нативный `select` остаётся в разметке и работает без JavaScript. -- `021-security-hardening` — отказ старта при небезопасных секретах (`API_TOKEN`/`SESSION_SECRET`/`ADMIN_PASSWORD`/`SECRET_KEY`), блокировка входа в UI по IP клиента, `SESSION_COOKIE_SECURE`, безопасный `next` в редиректах (`/ui/move`, `/backups/delete-many`). -- `022-async-db-remainder` — остаток п. 9 ревью: оставшиеся синхронные обращения к БД в async-обработчиках (API, UI, `ops`, `backups.search`) — через `asyncio.to_thread`; `jobs.start_jobs` разделён на синхронную `_create_jobs` (в потоке) и `async start_jobs` (создаёт задачи и планирует их в event loop); регрессионный тест-линтер (AST-обход `app/`) не даёт синхронным обращениям к БД вернуться в async-код. -- `023-ros-downgrade` — пять состояний колонки «Upgrade ROS» (`update`/`current`/`downgrade`/`check_error`/`unknown`; строгое сравнение версий больше не путает откат канала с «актуально»); осознанный откат ROS до версии канала с обязательным бэкапом — для одного устройства и группы, в UI (окно с подтверждением версии) и API (`/update/downgrade`, `/batch/ros_downgrade`). +| № | Изменение | Документы | +|---|---|---| +| 001 | Первая версия | [план](docs/changes/001-initial-implementation/plan.md) · [итог](docs/changes/001-initial-implementation/summary.md) | +| 002 | HTTP-подключение, исправления по тесту на устройстве | [план](docs/changes/002-http-support-and-fixes/plan.md) · [итог](docs/changes/002-http-support-and-fixes/summary.md) | +| 003 | Бэкап: скачивание через REST, загрузка в S3 сервером | [план](docs/changes/003-download-via-rest-upload-from-server/plan.md) · [итог](docs/changes/003-download-via-rest-upload-from-server/summary.md) | +| 004 | Колонки Upgrade ROS/FW, меню действий «⋯» | [план](docs/changes/004-upgrade-columns-and-actions-menu/plan.md) · [итог](docs/changes/004-upgrade-columns-and-actions-menu/summary.md) | +| 005 | Группы устройств, фильтры устройств и бэкапов | [план](docs/changes/005-groups-and-filters/plan.md) · [итог](docs/changes/005-groups-and-filters/summary.md) | +| 006 | Новый интерфейс по утверждённому макету | [план](docs/changes/006-ui-redesign/plan.md) · [итог](docs/changes/006-ui-redesign/summary.md) | +| 007 | Фоновый опрос, быстрое обнаружение недоступности | [план](docs/changes/007-fast-offline-detection/plan.md) · [итог](docs/changes/007-fast-offline-detection/summary.md) | +| 008 | Примечание к устройству | [план](docs/changes/008-device-note/plan.md) · [итог](docs/changes/008-device-note/summary.md) | +| 009 | Выравнивание полей в окне устройства | [план](docs/changes/009-dialog-label-alignment/plan.md) · [итог](docs/changes/009-dialog-label-alignment/summary.md) | +| 010 | `.rsc` с `show-sensitive` | [план](docs/changes/010-export-show-sensitive/plan.md) · [итог](docs/changes/010-export-show-sensitive/summary.md) | +| 011 | Выпадающие меню не обрезаются | [план](docs/changes/011-dropdown-clipping/plan.md) · [итог](docs/changes/011-dropdown-clipping/summary.md) | +| 012 | Клик по имени устройства открывает окно изменения | [план](docs/changes/012-clickable-device-name/plan.md) · [итог](docs/changes/012-clickable-device-name/summary.md) | +| 013 | Групповое удаление бэкапов | [план](docs/changes/013-bulk-delete-backups/plan.md) · [итог](docs/changes/013-bulk-delete-backups/summary.md) | +| 014 | Поддержка CHR, строгое сравнение версий ROS | [план](docs/changes/014-chr-support/plan.md) · [итог](docs/changes/014-chr-support/summary.md) | +| 015 | Имя устройства задаётся только при создании | [план](docs/changes/015-immutable-device-name/plan.md) · [итог](docs/changes/015-immutable-device-name/summary.md) | +| 016 | Уникальные ID сущностей, журнал событий | [план](docs/changes/016-unique-ids-and-event-log/plan.md) · [итог](docs/changes/016-unique-ids-and-event-log/summary.md) | +| 017 | Журнал в UI: ротация, очистка с паролем | [план](docs/changes/017-events-ui-rotation/plan.md) · [итог](docs/changes/017-events-ui-rotation/summary.md) | +| 018 | Корректность: удаление бэкапа, миграции, групповая смена канала задачами | [план](docs/changes/018-correctness-consistency/plan.md) · [итог](docs/changes/018-correctness-consistency/summary.md) | +| 019 | Производительность: кэш бакета, БД вне event loop, один процесс на БД | [план](docs/changes/019-performance-scaling/plan.md) · [итог](docs/changes/019-performance-scaling/summary.md) | +| 020 | Выпадающие списки в стиле меню «⋯» | [план](docs/changes/020-custom-select-menus/plan.md) · [итог](docs/changes/020-custom-select-menus/summary.md) | +| 021 | Безопасность: секреты, блокировка входа, Secure-cookie, редиректы | [план](docs/changes/021-security-hardening/plan.md) · [итог](docs/changes/021-security-hardening/summary.md) | +| 022 | Остаток синхронной БД в async-коде, тест-линтер | [план](docs/changes/022-async-db-remainder/plan.md) · [итог](docs/changes/022-async-db-remainder/summary.md) | +| 023 | Состояния «Upgrade ROS», откат ROS до версии канала | [план](docs/changes/023-ros-downgrade/plan.md) · [итог](docs/changes/023-ros-downgrade/summary.md) | +| 024 | Оптимизация README | [план](docs/changes/024-readme-restructure/plan.md) · [итог](docs/changes/024-readme-restructure/summary.md) | + +## Отчёты ревью +- [Ревью кодовой базы 2026-09-27](docs/reviews/2026-09-27-codebase-review.md) (→ 018–021) +- [Повторное ревью 2026-09-28](docs/reviews/2026-09-28-1243-codebase-review.md) (→ 022; открыты п. 11, 12, 14–16) diff --git a/docs/changes/024-readme-restructure/plan.md b/docs/changes/024-readme-restructure/plan.md new file mode 100644 index 0000000..7f23524 --- /dev/null +++ b/docs/changes/024-readme-restructure/plan.md @@ -0,0 +1,32 @@ +# План: 024 — оптимизация README по образцу ipam_control + +## Context +README вырос за 23 изменения: длинные абзацы, повторы (требования к секретам — в трёх разделах, генерация ключей — в двух), +история изменений — список с длинными описаниями без ссылок на документы, нет раздела об отчётах ревью, эксплуатационные сведения +(том, миграции, один процесс, WAL) разбросаны. Пользователь просит переверстать README по образцу `/opt/lvraid/claude/ipam_control/README.md`. + +## Изменения (только `README.md`) +Структура образца: +1. Заголовок и абзац о проекте (+ схема компонентов). +2. **Быстрый старт** — команды и адреса UI/OpenAPI, вход. +3. **Конфигурация (`.env`)** — одна таблица «Переменная | По умолчанию | Назначение», требования к секретам — только здесь и в «Безопасности». +4. **Архитектура** — таблица слоёв и дерево каталогов. +5. **Функциональность и правила** — полужирные подзаголовки: устройства, состояния «Upgrade ROS», группы и фильтры, операции и задачи, + бэкапы, откат ROS, идентификаторы, журнал событий. +6. **Требования к устройствам**. +7. **API (`/api/v1`)** — таблица по областям + «Соглашения». +8. **Безопасность** — секреты, вход, cookie, шифрование, секреты в бэкапах. +9. **Эксплуатация** — контейнер и том, миграции, один процесс, кэш бакета, копирование БД в режиме WAL (замечание 13 повторного ревью — только документация), текущий стенд на порту 8001. +10. **Интерфейс** — короткий список. +11. **Тесты** — команда и число тестов. +12. **История изменений** — таблица «№ | Изменение | Документы» со ссылками на `plan.md`/`summary.md` (001–024). +13. **Отчёты ревью** — ссылки на `docs/reviews/` с указанием, в какие изменения они вылились. + +Содержание не теряется: каждое утверждение текущего README переносится в свой раздел (сверка списком фактов при ревью). + +## Исполнение +Только документация — оркестратор сам (навык change-flow: документация без исполнителя). Код не меняется, стенд не пересобирается. + +## Проверка +- Все ссылки на `docs/changes/*/plan.md|summary.md` и `docs/reviews/*.md` указывают на существующие файлы (скрипт). +- Сверка фактов: переменные `.env` из `app/config.py`, эндпоинты из `app/api/v1.py`, число тестов из `pytest --collect-only`. diff --git a/docs/changes/024-readme-restructure/summary.md b/docs/changes/024-readme-restructure/summary.md new file mode 100644 index 0000000..cd7dcb6 --- /dev/null +++ b/docs/changes/024-readme-restructure/summary.md @@ -0,0 +1,18 @@ +# Итоги: 024 — оптимизация README по образцу ipam_control + +## Сделано +- README переверстан по структуре `/opt/lvraid/claude/ipam_control/README.md`: «Быстрый старт», «Конфигурация» (одна таблица), + «Архитектура» (таблица слоёв и дерево каталогов), «Функциональность и правила» (полужирные подзаголовки, таблица состояний «Upgrade ROS»), + «Требования к устройствам», «API» (таблица по областям и «Соглашения»), «Безопасность», «Эксплуатация», «Интерфейс», «Тесты», + «История изменений» (таблица со ссылками на `plan.md`/`summary.md` 001–024), «Отчёты ревью». +- Убраны повторы: требования к секретам и команды генерации — по одному месту; длинные описания истории изменений — в документах изменений. +- Добавлено: раздел «Отчёты ревью», копирование БД в режиме WAL (замечание 13 повторного ревью, только документация), текущий стенд на порту 8001, + коды ошибок API (включая 401), синхронность смены канала одного устройства и `refresh`. + +## Проверено +- Все 51 ссылка (49 на `docs/changes/*`, 2 на `docs/reviews/*`) указывают на существующие файлы. +- Переменные `.env` сверены с `app/config.py`, эндпоинты — с `app/api/v1.py`, число тестов (33) — с `pytest --collect-only`. +- Код не менялся, стенд не пересобирался. + +## Оговорки +- Документы изменений в этом проекте названы `plan.md`/`summary.md` (в образце — `PLAN.md`/`SUMMARY.md`); регистр сохранён как есть.