Registry (/registry):
- filters by run (slice by the address's cycle in that run), subnet
(drop-down of configured subnets), direction (egress/ingress) and
protocol (icmp, tcp, ssh, https, tls); status in scope is computed over
the narrowed checks
- chart "successful checks per target (egress) / site (ingress)" when both
direction and protocol are chosen; a row opens the list of addresses
(dialog, CSV)
- API: direction/protocol parameters and run in GET /admin/registry,
GET /admin/registry/breakdown and /breakdown/list
- subnet filter passes ids as one JSON parameter (SQLite variable limit)
Analytics (/analytics):
- subnet filter recomputes the whole page over the addresses of the run
inside the subnet; only their checks are read; cache per run and subnet
- direction and protocol focus the page; with both set the registry chart
is shown
- subnet parameter in GET /admin/analytics/runs/{id} and lists (JSON, CSV)
Docs: plans and summaries in docs/changes, README, API, USAGE.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
82 lines
13 KiB
Markdown
82 lines
13 KiB
Markdown
# План: выпадающий список подсетей и чарт «успешные проверки по целям / площадкам» в «Реестре»
|
||
|
||
Редакция 2 (решения по вопросам подтверждены пользователем).
|
||
|
||
Продолжение [2026-10-06_12-12_registry-subnet-direction-protocol-filters](2026-10-06_12-12_registry-subnet-direction-protocol-filters-plan.md). Фильтры работают; улучшаем удобство и восприятие.
|
||
|
||
## Задача
|
||
|
||
1. **Подсеть** выбирается из выпадающего списка (сейчас — поле ввода с подсказками).
|
||
2. Когда выбраны **направление и протокол** (например, Egress + https), над таблицей показывается чарт «количество успешных проверок» по каждой цели 1…N:
|
||
- **Egress** — в разрезе целей;
|
||
- **Ingress** — в разрезе площадок (та же логика).
|
||
3. Строка чарта кликабельна и открывает список адресов с детализацией.
|
||
4. Подход переиспользуем из «Аналитика → Egress по целям».
|
||
|
||
## Что уже есть (граф + код)
|
||
|
||
- Аналитика: блок «Egress по целям» — строки `an-bar-row` (название, полоса `an-track`/`an-fill`, число и процент), вкладки по типу проверки. Диалог со списком адресов (`analytics-dialog.js`, шаблон `analytics_dialog`, «Копировать», «Скачать CSV», подсказки при наведении) подключается страницей и уже обслуживает списки по запросу `load(url)` → `fill(...)`. Стили — `static/analytics.css`.
|
||
- Данные в БД: в `checks` для egress `target` — адрес цели (`https://github.com`), `source='egress'`. Для ingress `source = inbound-site-N` (площадка), а `target` — сам проверяемый адрес, поэтому ingress группируется по `source`, а не по `target`. Имена площадок берутся по индексу (как `SiteNames` в аналитике), без имени — `site-N`.
|
||
- Срез данных (запуск / последний цикл, направление, протокол, статус, подсеть) уже реализован в `internal/db/queries_registry.go` (`registrySlice`, `scopeFrom`). Чарт строится по тому же срезу и тому же набору адресов, что и таблица.
|
||
- Список настроенных подсетей уже грузится в форму (`GetSubnets`), 45 штук на стенде.
|
||
- Таблица `/registry` обновляется через htmx (`#registry-table-wrap`).
|
||
|
||
## Решения (подтверждены пользователем)
|
||
|
||
1. **Когда показывать чарт:** выбраны **и** направление, **и** протокол. С одним направлением вместо чарта — короткая подсказка «Выберите протокол, чтобы увидеть распределение по целям/площадкам».
|
||
2. **Набор адресов — все под текущим фильтром** (запуск, подсеть, статус, поиск), а не только текущая страница. Чарт совпадает с «Найдено адресов: N». Чарт строится в разрезе проверок выбранного типа в рамках проверочного цикла среза: цикл адреса в выбранном запуске или его последний цикл.
|
||
3. **Что считается:** число записанных проверок выбранного типа в проверочном цикле среза: `успешных из всего`. Полоса — доля успешных; подпись: `1 234 из 1 250 · 98,7%`. У tcp и tls на ingress у адреса может быть несколько проверок на площадку (порты 22 и 443), поэтому считаются именно проверки, а не адреса (пояснение в подписи под чартом).
|
||
4. **Порядок строк:** **от большего к меньшему** по числу успешных проверок, при равенстве — по названию. Полоса масштабируется по наибольшему значению; доля успешных остаётся в подписи.
|
||
5. **Подпись цели:** адрес цели без схемы и без завершающего `/` (`repo.almalinux.org/almalinux`), ключ — исходное значение `target`. Разные пути на одном хосте остаются разными строками.
|
||
6. **Клик по строке:** диалог со списком **всех** адресов набора, у которых есть проверки этой цели/площадки, провалы сверху (порядок списка не связан с порядком чарта). Столбцы: Адрес, Результат (`успешно`/`провал`), Тип проверки (`https`, `tcp-22`…), Цель или Площадка, Валидатор (для egress), Задержка (мс), Детали (ошибка), Проверено. Сверху — счётчики «успешно N · провал M». Доступны «Копировать» и «Скачать CSV».
|
||
7. **Подсеть в списке** (ручной ввод убирается, решение подтверждено): варианты — «Все подсети» + настроенные подсети (`CIDR — метка`). Ручной ввод убирается. Подсеть из URL (переход из аналитики), которой нет в списке, добавляется отдельным выбранным пунктом. Если подсети не настроены — список недоступен, рядом ссылка на `/settings` с их настройкой.
|
||
8. **Egress + ssh/tcp/tls** (таких проверок нет): чарт показывает «Для этого сочетания проверок нет», как и пустая таблица.
|
||
|
||
## Изменения
|
||
|
||
### 1. БД (`internal/db`)
|
||
- Выделить сборку условий `FROM`/`WHERE` из `ListRegistryPage` в функцию, которую используют и список, и новая выборка (поведение списка не меняется; существующие тесты — страховка).
|
||
- Новый файл `queries_registry_breakdown.go`:
|
||
- `RegistryBreakdown(ctx, filter) (*Breakdown, error)`: требует `Level` и `Family` (иначе `ErrValidation`); один запрос `GROUP BY` по `target` (egress) или `source` (ingress) поверх проверок среза: `COUNT(*)`, `SUM(success)`. Возвращает группу (`target`|`site`), число адресов, строки `{key, total, ok}`.
|
||
- `RegistryBreakdownList(ctx, filter, key)`: строки проверок выбранного ключа с адресом, типом, валидатором, задержкой, деталью, временем; провалы сверху.
|
||
- Индексы `idx_checks_run` и `idx_checks_registry_cycle` уже подходят; проверить `EXPLAIN QUERY PLAN`.
|
||
|
||
### 2. HTTP API (`internal/httpapi`)
|
||
- `GET /api/v1/admin/registry/breakdown` — те же параметры, что у реестра (`q`, `last_result`, `run`, `subnet`, `direction`, `protocol`); без `direction` или `protocol` — `400`. Ответ: `{group, direction, protocol, run, addresses, rows:[{key,label,total,ok}]}`; имена площадок подставляются здесь.
|
||
- `GET /api/v1/admin/registry/breakdown/list?…&key=` — таблица `{columns, rows}`; `format=csv` — файл. Неизвестный ключ — `404`.
|
||
- Маршруты — в таблицу маршрутов (`TestRouteTableIsClassified`: admin-доступ). `docs/API.md` — описание и примеры.
|
||
|
||
### 3. Дашборд (`internal/dashboard`)
|
||
- `client.go`: `GetRegistryBreakdown`, `GetRegistryBreakdownList`, `…CSV`.
|
||
- `handlers_registry.go`: при выбранных направлении и протоколе запросить чарт и передать в шаблон; ошибка чарта показывается в самом блоке, страница не ломается. Прокси-маршруты `GET /registry/breakdown/list` и `GET /registry/breakdown/csv` (как `analytics/lists`).
|
||
- `templates/registry.html`:
|
||
- поле «Подсеть» → `<select>` (htmx-атрибуты те же, что у соседних полей);
|
||
- блок `registry_breakdown` внутри `registry_table` (обновляется вместе с таблицей): заголовок «Успешные проверки по целям» / «…по площадкам», тип проверки, строки-кнопки `an-bar-row` с серверной отрисовкой полос (без JS-библиотек), подпись и подсказка про учёт проверок;
|
||
- подключение `analytics.css`, `analytics-dialog.js`, шаблона `analytics_dialog` и нового `static/registry.js`.
|
||
- `static/registry.js` (небольшой): делегированный клик по `[data-bd-key]` (переживает htmx-замену), запрос списка с текущей строкой фильтров из адресной строки, `AnalyticsDialog.load/fill` с подсказками столбцов и ссылкой на CSV.
|
||
- Проверить, что `analytics.css` не конфликтует со стилями реестра (все классы с префиксом `an-`; токены темы общие). Если есть конфликт — вынести нужные правила в `dashboard.css`.
|
||
- Доступность: строки — `<button>`, фокус с клавиатуры, `aria-label` с числами; светлая и тёмная темы; мобильная раскладка (строка переносится, полоса остаётся).
|
||
|
||
### 4. Тесты (минимум)
|
||
- `internal/db`: один табличный тест выборок — egress по целям, ingress по площадкам, срез запуска, подсеть, статус в области, список по ключу (порядок, провалы сверху), `ErrValidation` без направления или протокола.
|
||
- `internal/httpapi`: `400` без параметров, форма ответа, список и CSV, `404` на неизвестный ключ.
|
||
- `internal/dashboard`: чарт есть при направлении + протоколе и отсутствует без них; подсеть — `<select>` с выбранным пунктом и пунктом из URL; прокси списка.
|
||
- `TestScaleSmoke6440`: чарт и список на 6440 адресов в пределах лимита.
|
||
- Правки существующих тестов, где проверялось поле ввода подсети.
|
||
|
||
## Порядок работы
|
||
|
||
1. Утвердить план и «Решения».
|
||
2. Субагент (Sonnet 5.5, Medium effort) пишет код и тесты: п.1 → п.2 → п.3 → п.4.
|
||
3. Ревью, `gofmt`, `go build ./... && go vet ./... && go test -count=1 ./...`, `EXPLAIN QUERY PLAN`.
|
||
4. Проверка на тестовом стенде `civ-test` (порты 18081/18091): пересобрать образы тега `filters`, `docker compose up -d` в `/opt/lvraid/claude/civ-teststand`, снять замеры на копии боевой БД. Боевые контейнеры не трогаем.
|
||
5. Summary в `docs/changes/…-summary.md`; обновить `README.md`, `docs/API.md`, `docs/USAGE.md`; обновить граф `/graphify . --update`.
|
||
|
||
## Риски
|
||
|
||
- Объём списка: до 6–7 тысяч адресов в диалоге, как в аналитике; для CSV — серверная выгрузка.
|
||
- Время ответа чарта на 1 млн проверок: ожидается долей секунды (один `GROUP BY` по срезу); проверить замером на копии БД стенда. При деградации — не считать чарт, пока не выбраны оба фильтра (уже предусмотрено).
|
||
- Названия площадок и целей берутся из текущей конфигурации: удалённая площадка отображается как `site-N`.
|
||
- Незавершённый запуск даёт частичные данные — как и таблица.
|
||
- Подсеть только из списка: чтобы отфильтровать произвольный CIDR, подсеть нужно добавить в настройки (ссылка рядом с полем).
|