Files
cloud-ip-validator/internal/dashboard/templates/analytics.html
T
ayurishchevandClaude Sonnet 5.5 b7669c9e41 Add the Analytics section: check runs, analytics API and page
Runs (migration 0011): a run groups the cycles of one launch. It opens when an
address enters an idle queue, takes everything submitted or re-checked while it
is open and is finalized when all its addresses are done; a re-check after that
opens a new run, so results of different runs never mix. check_runs,
run_results (one result per address and run, with the verdict and the expected
and stored check counts), subnets, run_id on ip_queue and checks. Existing data
is split into runs at pauses of more than an hour; ingress checks get the
validator that held the address (also at write time from now on).

Analytics (internal/analytics): figures computed from the stored checks of the
latest cycle of each address in the run, as facts next to the verdict: summary,
reasons of partial, data quality, subnets, targets and the subnet x target
matrix by check type, ingress by site, error classes, validators, and the
address lists behind the indicators and error classes. API: analytics runs,
report, lists (JSON or CSV), subnet list; run and subnet filters for the
registry.

Dashboard: /analytics matching the approved mockup (run selector, indicators
with address lists and CSV, error-class dialogs, drill-down to the registry),
subnet list on /settings. Sidebar: the control-api link state, theme toggle and
logout moved to the top, the three dots next to the logo removed, sections
grouped.

Rebuilt bin/control-api and bin/admin-dashboard to match. Plan, summary and the
updated README, API, USAGE, DASHBOARD and ADMIN_CLEANUP docs are in docs/.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 18:36:03 +03:00

137 lines
5.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "analytics_page"}}
<!doctype html>
<html lang="ru">
<head>{{template "html_head" .}}
<link rel="stylesheet" href="/static/analytics.css">
</head>
<body>
<div class="bg-grid"></div>
<input type="checkbox" id="nav-toggle" class="nav-toggle">
<div class="shell">
{{template "sidebar_nav" .}}
<div>
{{template "topbar_mobile" .}}
<main class="main">
<div id="error-banner">{{template "banner_inner" .Banner}}</div>
{{template "analytics_content" .}}
</main>
</div>
</div>
{{if .HasRun}}
<script type="application/json" id="analytics-data">{{.DataJSON}}</script>
<script src="/static/analytics.js"></script>
{{end}}
</body>
</html>
{{end}}
{{define "analytics_content"}}
<div class="an">
<div class="an-head">
<h1 class="an-title">Аналитика</h1>
<p class="an-sub">Один выбранный запуск. Данные других запусков на странице не участвуют.</p>
</div>
{{if not .Runs}}
<section class="an-panel">
<p class="an-note">Запусков проверки пока нет. Они появляются, когда адреса ставятся в очередь на странице <a href="/ips">«Очередь IP»</a> или запускается автоматический цикл.</p>
</section>
{{else}}
<section class="an-panel" aria-label="Выбор запуска">
<div class="an-runbar">
<label for="an-run">Запуск</label>
{{if .PrevURL}}<a class="an-btn" href="{{.PrevURL}}" aria-label="Предыдущий запуск">◀</a>{{else}}<span class="an-btn" aria-disabled="true" style="opacity:.4">◀</span>{{end}}
<select id="an-run" onchange="if (this.value) location.href = '/analytics?run=' + encodeURIComponent(this.value)">
{{range .Runs}}<option value="{{.ID}}"{{if .Selected}} selected{{end}}{{if .Disabled}} disabled{{end}}>{{.Label}}</option>
{{end}}
</select>
{{if .NextURL}}<a class="an-btn" href="{{.NextURL}}" aria-label="Следующий запуск">▶</a>{{else}}<span class="an-btn" aria-disabled="true" style="opacity:.4">▶</span>{{end}}
</div>
{{if .HasRun}}<p class="an-note" id="an-runnote"></p>{{else}}<p class="an-note">Завершённых запусков пока нет: данные появятся, когда все адреса запуска получат итог.</p>{{end}}
</section>
{{end}}
{{if .HasRun}}
<div class="an-kpis" id="an-kpis"></div>
<div class="an-cols">
<section class="an-panel" aria-labelledby="an-h-reasons">
<h2 id="an-h-reasons">Почему partial</h2>
<div class="an-rows" id="an-reasons"></div>
<p class="an-note">Адрес считается один раз, по главной причине. Всё, где есть egress, учитывается как egress.</p>
</section>
<section class="an-panel" aria-labelledby="an-h-dq">
<h2 id="an-h-dq">Качество данных</h2>
<div class="an-dq" id="an-dq"></div>
<p class="an-note">Вердикт ставится при агрегации. Результаты, пришедшие позже, остаются в этом же запуске, но в вердикт не входят.</p>
</section>
</div>
<section class="an-panel" aria-labelledby="an-h-sub">
<div class="an-head">
<h2 id="an-h-sub">Подсети</h2>
<div class="an-runbar">
<div class="an-tabs" role="group" aria-label="Сортировка подсетей">
<button type="button" data-sort="worst" aria-pressed="true">хуже всего</button>
<button type="button" data-sort="size" aria-pressed="false">больше адресов</button>
</div>
<button class="an-btn" id="an-allsub" type="button"></button>
</div>
</div>
<div class="an-scroll"><table id="an-subtbl"></table></div>
<p class="an-note">Строка ведёт в «Реестр» с фильтром по запуску и подсети.</p>
</section>
<section class="an-panel" aria-labelledby="an-h-eg">
<div class="an-head">
<h2 id="an-h-eg">Egress по целям</h2>
<div class="an-tabs" role="group" aria-label="Тип проверки" id="an-types"></div>
</div>
<div class="an-rows" id="an-targets"></div>
<div id="an-matrixwrap">
<div class="an-head"><h2 style="margin-top:6px">Подсеть × цель</h2>
<div class="an-legend" id="an-hlegend"></div></div>
<div class="an-scroll"><table class="an-heat" id="an-matrix"></table></div>
</div>
<p class="an-note" id="an-matrixnote"></p>
</section>
<div class="an-cols">
<section class="an-panel" aria-labelledby="an-h-in">
<h2 id="an-h-in">Ingress по площадкам</h2>
<div class="an-scroll"><table id="an-sites"></table></div>
</section>
<section class="an-panel" aria-labelledby="an-h-err">
<h2 id="an-h-err">Классы ошибок ingress</h2>
<div class="an-rows" id="an-errs"></div>
</section>
</div>
<section class="an-panel" aria-labelledby="an-h-val">
<h2 id="an-h-val">Валидаторы: доля провалов egress https</h2>
<div class="an-vals" id="an-vals" role="img" aria-label="Доля проваленных https-проверок по валидаторам"></div>
<div class="an-vlab"><span id="an-vfirst"></span><span id="an-vavg"></span><span id="an-vlast"></span></div>
<p class="an-note">Ровная полоса значит: проблема зависит от подсети адреса, а не от валидатора.</p>
</section>
<dialog id="an-dlg" aria-labelledby="an-dlg-title">
<div class="an-dlg">
<h3 id="an-dlg-title"></h3>
<p class="an-note" id="an-dlg-note"></p>
<div class="an-dist" id="an-dlg-dist" hidden></div>
<div class="an-list"><table id="an-dlg-tbl"></table></div>
<div class="an-foot">
<span class="an-note" id="an-dlg-msg" role="status"></span>
<div class="an-acts">
<button class="an-btn" type="button" id="an-dlg-copy">Копировать</button>
<button class="an-btn an-primary" type="button" id="an-dlg-csv">Скачать CSV</button>
<button class="an-btn" type="button" id="an-dlg-close" autofocus>Закрыть</button>
</div>
</div>
</div>
</dialog>
<div id="an-tip" role="tooltip"></div>
{{end}}
</div>
{{end}}