# Admin Dashboard `admin-dashboard` — 4-й компонент системы: браузерная веб-панель, дающая полное покрытие административного API `control-api` ([docs/API.md](API.md#управление-очередью-и-конфигурацией)) без единого `curl`. Отдельный, полностью самостоятельный процесс — не хранит состояния, не подключается к базе данных напрямую, общается с `control-api` только через его же HTTP admin API. ## Устройство - Рендеринг полностью на сервере: `html/template` + [htmx](https://htmx.org) (частичные обновления без перезагрузки страницы) + [Alpine.js](https://alpinejs.dev) (точечная клиентская интерактивность). Оформление — собственная небольшая дизайн-система (`internal/dashboard/static/dashboard.css`), не построена на готовом CSS-фреймворке: боковая навигация, карточки-панели со скруглением и мягкой тенью, статусы — пастельные «пилюли» (не Bootstrap-бейджи). Шрифты — Manrope (интерфейс/заголовки) и IBM Plex Mono (IP-адреса, таймстампы, числа — моноширинные для табличного выравнивания), с кириллицей. Адаптивна: боковая панель на узких экранах уходит в выдвижное меню, таблицы складываются в карточки. Никакой сборки фронтенда нет — шрифты и JS-библиотеки вендорены как статические файлы (`internal/dashboard/static/vendor/`, см. `VENDOR.md` там же) и встроены в бинарник через `//go:embed`. Дашборд работает полностью офлайн — при открытии страницы браузер не делает ни одного запроса за пределы самого дашборда (можно проверить через DevTools → Network). - Браузер никогда не видит JSON `control-api` напрямую: каждая страница и каждый htmx-фрагмент — это HTML, отрендеренный Go-хендлером дашборда после вызова `control-api`. CORS и reverse-proxy не нужны. - Как и `control-api`, дашборд **не аутентифицирован** — ограничивайте доступ на уровне сети/файрвола (см. [SETUP.md](SETUP.md#сетевые-доступы)). ## Запуск ```bash cp configs/admin-dashboard.example.yaml /etc/cloud-ip-validator/admin-dashboard.yaml # отредактируйте control_api.base_url под ваш стенд admin-dashboard -config /etc/cloud-ip-validator/admin-dashboard.yaml ``` Развёртывание как systemd-юнита — по образцу остальных компонентов, см. [SETUP.md](SETUP.md#развёртывание-admin-dashboard). Порт по умолчанию — `:8090` (у `control-api` — `:8080`). ## Страницы и что на них можно делать | Страница | Назначение | |---|---| | `/overview` | Сводная статистика: счётчики по состояниям, «текущая проверка» (live-снимок всех IP не в терминальном состоянии) и «последние N завершённых» (по умолчанию 20, `overview.last_completed_count`) с разбивкой pass/partial/fail/cancelled. Обновляется каждые `overview.poll_interval_seconds` секунд без перезагрузки страницы. | | `/ips` | Полная очередь. Форма сверху принимает список адресов (по одному на строке или через запятую) и отправляет их в `POST /api/v1/admin/ips` — **один и тот же вызов** добавляет новые адреса и принудительно перезапускает уже завершённые (см. ниже). У каждого адреса — кнопка «Перепроверить» (для `done`/`failed`) или «Отменить» (для активных состояний), и всегда — «Удалить» (безвозвратно, в отличие от «Отменить», см. ниже). Чекбоксы у строк + кнопка «Удалить выбранные» удаляют список одним вызовом; «Очистить всё» удаляет вообще всё, включая активные проверки — обе операции требуют явного подтверждения. Пока не истекла настроенная на `/settings` пауза (`fip_settle_seconds`), только что привязавший Floating IP адрес показывает отдельный бейдж «прогрев FIP» вместо обычного статуса. | | `/ips/{ip}` | Детали одного адреса: все проверки текущей попытки и вся история событий. | | `/validators` | Список валидаторов + создание/изменение `os_port_id`/удаление. | | `/sites` | Три фиксированных слота площадок (1/2/3) — назначить/сменить/освободить `site_id`. | | `/targets` | Группы целей для egress-проверок — создание/редактирование/удаление. | | `/check-types` | Типы проверок (`https`/`icmp`/`ssh`/...), включение/выключение, привязка к группам целей. | | `/settings` | Две формы: `fip_settle_seconds` — пауза (в секундах) между привязкой Floating IP и началом self-check («прогрев» дата-плейна OpenStack, см. [USAGE.md](USAGE.md#пауза-перед-self-check-fip_settle_seconds)); и типы проверок пробера — TCP-порты (через запятую) + чекбокс ICMP, общие для всех площадок (см. [USAGE.md](USAGE.md#управление-типами-проверок-пробера)). | ### «Текущая» и «последняя завершённая» проверка В `control-api` нет понятия «запуска»/«цикла проверки» как отдельной сущности — есть только общая очередь IP-адресов (`docs/PLAN_ADMIN_DASHBOARD.md`). Дашборд ничего не меняет в этом устройстве и не заводит своего состояния: - **Текущая проверка** — все адреса, которые прямо сейчас не в состоянии `done`/`failed` (`queued`, `assigning_fip`, `awaiting_self_check`, `checking`, `aggregating`), вычисляется заново на каждый запрос из `GET /api/v1/admin/status` + `GET /api/v1/admin/ips`. - **Последняя завершённая проверка** — последние N адресов, перешедших в `done`/`failed`, отсортированные по `AggregatedAt` по убыванию (не «последний запуск», а именно скользящее окно последних по времени завершений). ### Добавление адресов и принудительный повтор — один и тот же вызов Форма на `/ips` всегда бьёт в `POST /api/v1/admin/ips`. Поведение зависит от текущего состояния каждого конкретного адреса (см. [docs/API.md](API.md#post-apiv1adminips)): новый — встаёт в очередь; уже `done`/`failed` — принудительно перезапускается; уже `queued` — просто переупорядочивается; уже активно проверяется — не трогается (дашборд честно показывает это в таблице, а не делает вид, что запрос ничего не значил). ### Удаление адресов — безвозвратно, в отличие от «Отменить» «Отменить» (`POST .../cancel`) останавливает проверку, но сохраняет адрес и его историю как `failed`/`cancelled` — он остаётся виден в очереди. «Удалить» (кнопка в строке, «Удалить выбранные» по чекбоксам, «Очистить всё») стирает строку и всю её историю проверок/событий физически, без возможности восстановления — работает из любого состояния, включая активно проверяемое (Floating IP отвязывается, валидатор освобождается). Все три операции удаления в UI защищены `hx-confirm` с формулировкой, отражающей необратимость — «Очистить всё» предупреждает отдельно, так как затрагивает и активные проверки. Подробнее — [API.md](API.md#delete-apiv1adminipsip-post-apiv1adminipsdelete-post-apiv1adminipsclear) и [USAGE.md](USAGE.md#удаление-адресов-из-очереди). ## Конфигурация См. `configs/admin-dashboard.example.yaml`. Ключевые поля: - `server.listen_addr` — где слушает сам дашборд (по умолчанию `:8090`). - `control_api.base_url` — адрес `control-api`, обязателен. - `control_api.timeout_seconds` — таймаут HTTP-запросов к `control-api`. - `overview.last_completed_count` — размер окна «последних завершённых» на странице обзора. - `overview.poll_interval_seconds` — как часто браузер опрашивает `/overview/fragment` для live-обновления. ## Отображение ошибок Любая ошибка `control-api` (4xx/5xx с телом `{"error":"..."}`) или сбой связи с ним (недоступен, таймаут) показывается баннером наверху страницы, а не приводит к падению дашборда — таблица/страница при этом всегда отражает актуальное состояние `control-api` (дашборд перезапрашивает данные после любой попытки мутации, независимо от её исхода). Жёлтый баннер — бизнес-ошибка (4xx, например «валидатор занят»), красный — инфраструктурная проблема (5xx или `control-api` недоступен).