# ros_control Централизованное управление парком MikroTik RouterOS: WEB UI и JSON API. Дизайн и требования — `ros_control.svg`; макет интерфейса — утверждён и перенесён в приложение (см. `docs/changes/006-ui-redesign`). ``` Admin Dashboard ⇄ Control Server ⇄ RouterOS REST (на каждом устройстве) ⇅ ⇅ Metadata DB S3 (Yandex Object Storage) ``` ## Возможности **Устройства** - Список устройств: добавление, изменение, удаление; пароли хранятся зашифрованно (Fernet). Имя устройства в таблице кликабельно — открывает окно изменения. - К устройству можно добавить **примечание** (до 500 символов): видно подсказкой при наведении на имя. - Статус: online/offline, модель, канал обновлений, версии ROS и FW, uptime, время запроса бэкапа. Колонки **Upgrade ROS** и **Upgrade FW** показывают версию, до которой можно обновиться (устройство проверяет обновления на серверах MikroTik; без интернета на устройстве — «—»). - **Мониторинг доступности**: сервер опрашивает устройства каждые 30 с (`POLL_INTERVAL`), недоступность определяется за ~4 с на устройство (`ROS_CONNECT_TIMEOUT`); страница обновляет статусы сама, без перезагрузки. **Группы и фильтры** - Устройство относится к одной группе: вкладки групп со счётчиками, страница «Группы» (создание, переименование, удаление), перенос отмеченных устройств в группу. При добавлении устройства группу можно создать «на месте» (пункт «+ Новая группа…»). - Фильтры устройств: поиск (имя, адрес, модель), статус, наличие обновлений, канал. Фильтры бэкапов: группа, устройство, тип файла, период, имя файла. Фильтры попадают в URL. **Операции** - Действия по устройству — в меню «⋯» строки; действия над выбранными устройствами (бэкап, статус, обновление ROS/FW, канал, перенос в группу) появляются в полосе инструментов при выборе строк; через API их можно запускать и над всей группой. - **Бэкап**: `.backup` (без шифрования) и `.rsc` (с `show-sensitive`, т.е. с паролями и ключами — из него можно восстановить все сущности, использующие пароль). Сервер создаёт файлы через REST, скачивает во временную папку, загружает в S3 и удаляет файлы с устройства. - Список бэкапов в бакете, скачивание (временная ссылка), удаление. - Канал обновлений; обновление ROS (устройство перезагружается после скачивания); обновление FW (перезагрузка сразу после появления в журнале устройства записи «Firmware upgraded successfully…»). - Долгие и групповые операции выполняются задачами (карточка «Задачи», состояние — в UI и через API). ## Интерфейс Экран строится сверху вниз: приложение (навигация) → страница (заголовок, счётчики, главное действие) → вкладки групп → полоса инструментов таблицы → данные; «Задачи» — отдельная карточка. Пока ничего не выбрано, полоса показывает фильтры; при выборе строк — действия над выбранными. Добавление и изменение устройств, создание и переименование групп — в окнах поверх страницы (запасные страницы `/devices/new`, `/devices/{id}/edit` работают без JavaScript). Выпадающие меню не обрезаются таблицей и раскрываются вверх, если снизу нет места. Светлая и тёмная темы переключаются по настройке системы. Шрифты IBM Plex лежат в `app/ui/static/fonts` (лицензия OFL), внешние ресурсы не загружаются. ## Требования к устройствам - RouterOS **7.1+**, включённый сервис `www-ssl` (REST API, порт 443; **не** `api-ssl` 8729 и не `api` 8728). Если `www-ssl` недоступен, для доверенной сети можно указать сервис `www` (порт 80) и снять флаг «HTTPS» у устройства — логин и пароль пойдут открытым текстом. - Пользователь с политиками `read`, `write`, `policy`, `test`, `ftp`, `reboot`, `sensitive` (проще всего — группа `full`). Встроенная группа `write` с запретом `ftp`/`policy` не подходит: бэкап падает с `not enough permissions`. После смены прав пересоздайте подключение к устройству. - Доступ устройств к S3 не нужен: файлы забирает и загружает Control Server (ему нужен доступ к `storage.yandexcloud.net:443`). ## Безопасность - Замените значения по умолчанию `API_TOKEN` и `SESSION_SECRET` на случайные (`openssl rand -hex 24`); порт 8000 не стоит открывать в недоверенную сеть. - Пароли устройств шифруются ключом `SECRET_KEY` (Fernet); потеря ключа = потеря доступа к сохранённым паролям. - **Секреты в бэкапах:** `.rsc` создаётся с `show-sensitive`, а `.backup` — без шифрования, поэтому файлы содержат пароли и ключи открытым текстом. Ограничьте доступ к бакету и ссылкам скачивания. ## Запуск ```bash cp .env.example .env # заполнить SECRET_KEY, ADMIN_PASSWORD, API_TOKEN, S3_* docker compose up -d --build # UI: http://localhost:8000, OpenAPI: /docs ``` БД (SQLite) хранится в Docker-томе `ros_data` (`docker volume inspect ros_control_ros_data`), переживает пересборку контейнера; схема обновляется автоматически при старте. Остановка: `docker compose down` (том сохраняется; `down -v` удалит БД). Ключ Fernet: `python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"`. ### Настройки (`.env`) | Переменная | По умолчанию | Назначение | |---|---|---| | `SECRET_KEY` | — | ключ Fernet для паролей устройств (обязателен) | | `SESSION_SECRET` | `change-me` | подпись cookie-сессии UI | | `ADMIN_USER` / `ADMIN_PASSWORD` | `admin` / `change-me` | вход в UI | | `API_TOKEN` | `change-me` | Bearer-токен API | | `DATABASE_URL` | `sqlite:///./data/ros_control.db` | база метаданных (в compose — том) | | `S3_ENDPOINT`, `S3_REGION`, `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_PREFIX` | Yandex Object Storage, `backups` | бакет для резервных копий | | `MAX_CONCURRENCY` | `10` | одновременные обращения к устройствам | | `ROS_CONNECT_TIMEOUT` | `4` | секунд на установление соединения (скорость обнаружения недоступности) | | `ROS_TIMEOUT` | `30` | секунд на ответ устройства (долгие операции) | | `POLL_INTERVAL` | `30` | период фонового опроса, с; `0` — выключить | | `UPDATE_CHECK_INTERVAL` | `1800` | как часто опрос проверяет обновления ROS, с | ### Разработка ```bash python3 -m venv venv && ./venv/bin/pip install -r requirements.txt set -a; . ./.env; set +a ./venv/bin/uvicorn app.main:app --reload ./venv/bin/python -m pytest # 9 тестов, фоновый опрос в тестах выключен ``` ## API v1 Заголовок `Authorization: Bearer `. Интерактивная документация — `/docs`. Пример: ```bash curl -s -H "Authorization: Bearer $API_TOKEN" http://localhost:8000/api/v1/devices | python3 -m json.tool ``` Устройство: `name`, `host`, `port`, `username`, `password` (только при записи), `verify_tls`, `use_tls` (`false` — HTTP), `group_id` (`null` — без группы), `note`. | Метод | Путь | Назначение | |---|---|---| | GET/POST | `/api/v1/devices` | список (фильтры `group`, `q`, `status`, `updates`, `channel`) / добавить | | GET/PATCH/DELETE | `/api/v1/devices/{id}` | получить / изменить / удалить | | POST | `/api/v1/devices/refresh`, `/devices/{id}/refresh` | обновить статус | | POST | `/api/v1/devices/{id}/backups` | бэкап (задача) | | PUT | `/api/v1/devices/{id}/update/channel` | `{"channel": "stable\|long-term\|testing\|development"}` | | POST | `/api/v1/devices/{id}/update/install` | обновление ROS (задача) | | POST | `/api/v1/devices/{id}/firmware/upgrade` | обновление FW (задача) | | GET/POST | `/api/v1/groups` | группы (с числом устройств) / создать | | PATCH/DELETE | `/api/v1/groups/{id}` | переименовать / удалить (устройства остаются без группы) | | POST | `/api/v1/batch/{backup\|ros_update\|fw_update}` | `{"device_ids": [...]}` и/или `{"group_id": N}` — групповая операция | | PUT | `/api/v1/batch/channel` | `{"device_ids": [...] или "group_id": N, "channel": "..."}` | | GET/DELETE | `/api/v1/backups`, `/backups/download?key=` | бэкапы в бакете (фильтры `device_id`, `group`, `kind`, `date_from`, `date_to`, `q`) | | GET | `/api/v1/jobs`, `/jobs/{id}` | состояние задач | Внешние справочники: [RouterOS REST API](https://help.mikrotik.com/docs/spaces/ROS/pages/47579162/REST+API#RESTAPI-HTTPMethods), [Yandex Object Storage S3 API](https://yandex.cloud/ru/docs/storage/s3/api-ref/). ## Структура - `app/ros` — клиент RouterOS REST и операции (статус, бэкап, скачивание файлов, обновления) - `app/s3.py` — бакет; `app/security.py` — шифрование и авторизация; `app/models.py`, `app/db.py` — БД и миграции - `app/services` — устройства и фильтры, группы, бэкапы, операции, задачи, фоновый опрос (`poller.py`) - `app/api` — JSON API; `app/ui` — WEB UI (Jinja2 + HTMX, `static/` со стилями, скриптом и шрифтами) - `tests` — минимальный набор; `docs/changes` — планы и итоги каждого изменения ## История изменений Планы и итоги — в `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` — клик по имени устройства открывает окно изменения.