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:
1 parent
068c10ea1c
commit
ded196ec8d
40 files changed
+2545
-188
No files matched your search
@@ -60,6 +60,13 @@ type fakeControlAPI struct {
|
||||
scanFreeAddresses []string
|
||||
registry map[string]registryItem
|
||||
registryChecks map[string][]check
|
||||
// The chart of /registry: the breakdown served (breakdownStatus != 0 makes
|
||||
// it fail with that status), the table behind a row and the raw queries
|
||||
// of both endpoints.
|
||||
breakdown registryBreakdown
|
||||
breakdownStatus int
|
||||
breakdownList string
|
||||
breakdownReqs []string
|
||||
|
||||
// Scan job state machine (see the scan handlers): POST starts a job that
|
||||
// stays "running" for scanRunPolls GET polls (0 = finishes at once), then
|
||||
@@ -483,6 +490,29 @@ func (f *fakeControlAPI) handler() http.Handler {
|
||||
}
|
||||
writeJSON(w, http.StatusOK, registryPage{Items: page, Total: len(matched), Limit: limit, Offset: offset})
|
||||
})
|
||||
mux.HandleFunc("GET /api/v1/admin/registry/breakdown", func(w http.ResponseWriter, r *http.Request) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.breakdownReqs = append(f.breakdownReqs, r.URL.RequestURI())
|
||||
if f.breakdownStatus != 0 {
|
||||
writeAPIErr(w, f.breakdownStatus, "breakdown unavailable")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, f.breakdown)
|
||||
})
|
||||
mux.HandleFunc("GET /api/v1/admin/registry/breakdown/list", func(w http.ResponseWriter, r *http.Request) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.breakdownReqs = append(f.breakdownReqs, r.URL.RequestURI())
|
||||
if r.URL.Query().Get("format") == "csv" {
|
||||
w.Header().Set("Content-Type", "text/csv; charset=utf-8")
|
||||
w.Header().Set("Content-Disposition", `attachment; filename="registry_ingress_tls_rxmsk.csv"`)
|
||||
_, _ = w.Write([]byte("Адрес\r\n1.2.3.4\r\n"))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(f.breakdownList))
|
||||
})
|
||||
mux.HandleFunc("GET /api/v1/admin/registry/{ip}", func(w http.ResponseWriter, r *http.Request) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
Reference in new issue
Block a user