2026-08-23 20:39:22 +03:00
# 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 )
(частичные обновления без перезагрузки страницы) +
2026-08-23 21:58:19 +03:00
[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).
2026-08-23 20:39:22 +03:00
- Браузер никогда не видит 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` секунд без перезагрузки страницы. |
2026-08-23 22:24:55 +03:00
| `/ips` | Полная очередь. Форма сверху принимает список адресов (по одному на строке или через запятую) и отправляет их в `POST /api/v1/admin/ips` — **один и тот же вызов** добавляет новые адреса и принудительно перезапускает уже завершённые (см. ниже). У каждого адреса — кнопка «Перепроверить» (для `done` /`failed` ) или «Отменить» (для активных состояний), и всегда — «Удалить» (безвозвратно, в отличие от «Отменить», см. ниже). Чекбоксы у строк + кнопка «Удалить выбранные» удаляют список одним вызовом; «Очистить всё» удаляет вообще всё, включая активные проверки — обе операции требуют явного подтверждения. |
2026-08-23 20:39:22 +03:00
| `/ips/{ip}` | Детали одного адреса: все проверки текущей попытки и вся история событий. |
| `/validators` | Список валидаторов + создание/изменение `os_port_id` /удаление. |
| `/sites` | Три фиксированных слота площадок (1/2/3) — назначить/сменить/освободить `site_id` . |
| `/targets` | Группы целей для egress-проверок — создание/редактирование/удаление. |
| `/check-types` | Типы проверок (`https` /`icmp` /`ssh` /...), включение/выключение, привязка к группам целей. |
### «Текущая» и «последняя завершённая» проверка
В `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` —
просто переупорядочивается; уже активно проверяется — не трогается
(дашборд честно показывает это в таблице, а не делает вид, что запрос
ничего не значил).
2026-08-23 22:24:55 +03:00
### Удаление адресов — безвозвратно, в отличие от «Отменить»
«Отменить» (`POST .../cancel` ) останавливает проверку, но сохраняет
адрес и его историю как `failed` /`cancelled` — он остаётся виден в
очереди. «Удалить» (кнопка в строке, «Удалить выбранные» по чекбоксам,
«Очистить всё») стирает строку и всю её историю проверок/событий
физически, без возможности восстановления — работает из любого
состояния, включая активно проверяемое (Floating IP отвязывается,
валидатор освобождается). Все три операции удаления в UI защищены
`hx-confirm` с формулировкой, отражающей необратимость — «Очистить всё»
предупреждает отдельно, так как затрагивает и активные проверки. Подробнее
— [API.md ](API.md#delete-apiv1adminipsip-post-apiv1adminipsdelete-post-apiv1adminipsclear )
и [USAGE.md ](USAGE.md#удаление-адресов-из-очереди ).
2026-08-23 20:39:22 +03:00
## Конфигурация
См. `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` недоступен).