Refactor docs; Add 'ipng_source_tag', add udp listener for nginx-ipng-stats plugin

This commit is contained in:
2026-04-17 09:50:54 +02:00
parent 0ecca06069
commit 577ed3dad5
26 changed files with 1319 additions and 1718 deletions

View File

@@ -146,8 +146,13 @@ func applyTerm(term string, fs *filterState) error {
return fmt.Errorf("invalid asn expression %q", expr)
}
fs.ASN = expr
case "source_tag":
if op != "=" {
return fmt.Errorf("source_tag only supports =, not %q", op)
}
fs.SourceTag = value
default:
return fmt.Errorf("unknown field %q; valid: status, website, uri, prefix, is_tor, asn", field)
return fmt.Errorf("unknown field %q; valid: status, website, uri, prefix, is_tor, asn, source_tag", field)
}
return nil
}
@@ -196,6 +201,9 @@ func FilterExprString(f filterState) string {
if f.ASN != "" {
parts = append(parts, asnTermStr(f.ASN))
}
if f.SourceTag != "" {
parts = append(parts, "source_tag="+quoteMaybe(f.SourceTag))
}
return strings.Join(parts, " AND ")
}

View File

@@ -229,8 +229,17 @@ func TestDrillURL(t *testing.T) {
if !strings.Contains(u, "f_asn=12345") {
t.Errorf("drill from asn: missing f_asn in %q", u)
}
if !strings.Contains(u, "by=source_tag") {
t.Errorf("drill from asn: expected next by=source_tag in %q", u)
}
p.GroupByS = "source_tag"
u = p.drillURL("direct")
if !strings.Contains(u, "f_source_tag=direct") {
t.Errorf("drill from source_tag: missing f_source_tag in %q", u)
}
if !strings.Contains(u, "by=website") {
t.Errorf("drill from asn: expected cycle back to by=website in %q", u)
t.Errorf("drill from source_tag: expected cycle back to by=website in %q", u)
}
}

View File

@@ -58,6 +58,7 @@ type filterState struct {
URIReNeg string // RE2 regex exclusion against request URI
IsTor string // "", "1" (TOR only), "0" (non-TOR only)
ASN string // expression: "12345", "!=65000", ">=1000", etc.
SourceTag string // exact ipng_source_tag match
}
// QueryParams holds all parsed URL parameters for one page request.
@@ -95,7 +96,7 @@ var windowSpecs = []struct{ s, label string }{
}
var groupBySpecs = []struct{ s, label string }{
{"website", "website"}, {"asn", "asn"}, {"prefix", "prefix"}, {"status", "status"}, {"uri", "uri"},
{"website", "website"}, {"asn", "asn"}, {"prefix", "prefix"}, {"status", "status"}, {"uri", "uri"}, {"source_tag", "source"},
}
func parseWindowString(s string) (pb.Window, string) {
@@ -127,6 +128,8 @@ func parseGroupByString(s string) (pb.GroupBy, string) {
return pb.GroupBy_HTTP_RESPONSE, "status"
case "asn":
return pb.GroupBy_ASN_NUMBER, "asn"
case "source_tag":
return pb.GroupBy_SOURCE_TAG, "source_tag"
default:
return pb.GroupBy_WEBSITE, "website"
}
@@ -168,12 +171,13 @@ func (h *Handler) parseParams(r *http.Request) QueryParams {
URIReNeg: q.Get("f_uri_re_neg"),
IsTor: q.Get("f_is_tor"),
ASN: q.Get("f_asn"),
SourceTag: q.Get("f_source_tag"),
},
}
}
func buildFilter(f filterState) *pb.Filter {
if f.Website == "" && f.Prefix == "" && f.URI == "" && f.Status == "" && f.WebsiteRe == "" && f.URIRe == "" && f.WebsiteReNeg == "" && f.URIReNeg == "" && f.IsTor == "" && f.ASN == "" {
if f.Website == "" && f.Prefix == "" && f.URI == "" && f.Status == "" && f.WebsiteRe == "" && f.URIRe == "" && f.WebsiteReNeg == "" && f.URIReNeg == "" && f.IsTor == "" && f.ASN == "" && f.SourceTag == "" {
return nil
}
out := &pb.Filter{}
@@ -216,6 +220,9 @@ func buildFilter(f filterState) *pb.Filter {
out.AsnOp = op
}
}
if f.SourceTag != "" {
out.IpngSourceTag = &f.SourceTag
}
return out
}
@@ -256,6 +263,9 @@ func (p QueryParams) toValues() url.Values {
if p.Filter.ASN != "" {
v.Set("f_asn", p.Filter.ASN)
}
if p.Filter.SourceTag != "" {
v.Set("f_source_tag", p.Filter.SourceTag)
}
return v
}
@@ -278,7 +288,7 @@ func (p QueryParams) clearFilterURL() string {
return p.buildURL(map[string]string{
"f_website": "", "f_prefix": "", "f_uri": "", "f_status": "",
"f_website_re": "", "f_uri_re": "", "f_website_re_neg": "", "f_uri_re_neg": "",
"f_is_tor": "", "f_asn": "",
"f_is_tor": "", "f_asn": "", "f_source_tag": "",
})
}
@@ -293,7 +303,9 @@ func nextGroupBy(s string) string {
return "status"
case "status":
return "asn"
default: // asn → back to website
case "asn":
return "source_tag"
default: // source_tag → back to website
return "website"
}
}
@@ -311,6 +323,8 @@ func groupByFilterKey(s string) string {
return "f_status"
case "asn":
return "f_asn"
case "source_tag":
return "f_source_tag"
default:
return "f_website"
}
@@ -391,6 +405,12 @@ func buildCrumbs(p QueryParams) []Crumb {
RemoveURL: p.buildURL(map[string]string{"f_asn": ""}),
})
}
if p.Filter.SourceTag != "" {
crumbs = append(crumbs, Crumb{
Text: "source_tag=" + p.Filter.SourceTag,
RemoveURL: p.buildURL(map[string]string{"f_source_tag": ""}),
})
}
return crumbs
}