Registry and Analytics: run, subnet, direction and protocol filters, successes-by-target chart

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>
This commit is contained in:
ayurishchevandClaude Sonnet 5.5 committed 2026-10-06 14:23:48 +03:00
1 parent 068c10ea1c
commit ded196ec8d
40 files changed
+2545 -188

No files matched your search

+16 -1
View File
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"testing"
"time"
@@ -303,7 +304,21 @@ func TestAdminRegistryPaginationFiltersAndCompat(t *testing.T) {
if p = page("limit=50&q=0.0.1"); p.Total != 4 { // 10.0.0.1, .10, .11, .12
t.Fatalf("q: %+v", p)
}
for _, bad := range []string{"limit=0", "limit=1001", "offset=1", "limit=5&last_result=bogus", "limit=5&offset=-3"} {
// run, subnet, direction and protocol together with the older filters; the
// status is then that of the narrowed checks, and the run is echoed.
runs, err := d.ListRuns(context.Background())
if err != nil || len(runs) != 1 {
t.Fatalf("runs: %+v %v", runs, err)
}
runQ := "limit=50&run=" + strconv.FormatInt(runs[0].ID, 10) + "&direction=egress&protocol="
if p = page(runQ + "https&subnet=10.0.0.0/28&last_result=pass&q=10.0.0."); p.Total != 2 || p.Run != runs[0].ID || p.Items[0].Egress.Total != 1 || p.Items[0].Ingress.Total != 0 {
t.Fatalf("run+subnet+direction+protocol+status: %+v", p)
}
if p = page(runQ + "tls"); p.Total != 0 {
t.Fatalf("tls on egress: %+v", p)
}
for _, bad := range []string{"limit=0", "limit=1001", "offset=1", "limit=5&last_result=bogus", "limit=5&offset=-3",
"limit=5&direction=sideways", "limit=5&protocol=udp", "limit=5&protocol=TCP"} {
if resp, body := fc.do(http.MethodGet, "/api/v1/admin/registry?"+bad, nil); resp.StatusCode != http.StatusBadRequest {
t.Fatalf("%s: expected 400, got %d %s", bad, resp.StatusCode, body)
}