Files
EvoBGP/internal/pipeline/parse.go
T
Denozordec e20c9f3113
CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 25s
CI / go (push) Successful in 29s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m1s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m1s
CI / docker-bird (push) Successful in 42s
CI / bird2 (push) Successful in 14s
CI / docker-go-prime (push) Successful in 1m22s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 58s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 2m0s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m16s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m14s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m17s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m2s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m15s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m15s
feat: implement CDN source preview functionality and enhance data handling. Add a new endpoint for previewing CDN sources, allowing users to fetch and parse CIDR prefixes from specified URLs. Update OpenAPI documentation to include new request and response schemas, and modify internal logic to support JSON parsing with prefix path traversal. Enhance UI components to accommodate new preview features, improving user experience and data management.
2026-04-06 15:08:17 +07:00

148 lines
3.1 KiB
Go

package pipeline
import (
"bufio"
"encoding/json"
"net/netip"
"strings"
)
// ParseCIDRLines extracts unique IPv4/IPv6 CIDRs from plain text (one per line, # comments, empty lines skipped).
func ParseCIDRLines(body string) []netip.Prefix {
seen := make(map[string]struct{})
var out []netip.Prefix
sc := bufio.NewScanner(strings.NewReader(body))
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
pfx := parseOneCIDR(line)
if !pfx.IsValid() {
continue
}
m := pfx.Masked()
s := m.String()
if _, ok := seen[s]; ok {
continue
}
seen[s] = struct{}{}
out = append(out, m)
}
return out
}
// ExtractCIDRs parses CIDRs from either plaintext lines or JSON payload.
// For sourceKind="json", prefixPath supports dotted traversal, with [] for arrays:
// e.g. "prefixes[]", "data.items[].cidr".
func ExtractCIDRs(body, sourceKind, prefixPath string) ([]netip.Prefix, error) {
if strings.EqualFold(strings.TrimSpace(sourceKind), "json") {
return parseCIDRsFromJSON(body, prefixPath)
}
return ParseCIDRLines(body), nil
}
func parseCIDRsFromJSON(body, prefixPath string) ([]netip.Prefix, error) {
var root any
if err := json.Unmarshal([]byte(body), &root); err != nil {
return nil, err
}
values := jsonValuesAtPath(root, prefixPath)
seen := make(map[string]struct{})
var out []netip.Prefix
for _, raw := range values {
pfx := parseOneCIDR(raw)
if !pfx.IsValid() {
continue
}
m := pfx.Masked()
s := m.String()
if _, ok := seen[s]; ok {
continue
}
seen[s] = struct{}{}
out = append(out, m)
}
return out, nil
}
func jsonValuesAtPath(root any, prefixPath string) []string {
path := strings.TrimSpace(prefixPath)
if path == "" {
return flattenJSONStrings(root)
}
parts := strings.Split(path, ".")
nodes := []any{root}
for _, p := range parts {
part := strings.TrimSpace(p)
if part == "" {
continue
}
iter := strings.HasSuffix(part, "[]")
key := strings.TrimSuffix(part, "[]")
var next []any
for _, n := range nodes {
obj, ok := n.(map[string]any)
if !ok {
continue
}
child, ok := obj[key]
if !ok {
continue
}
if iter {
if arr, ok := child.([]any); ok {
next = append(next, arr...)
}
continue
}
next = append(next, child)
}
nodes = next
if len(nodes) == 0 {
return nil
}
}
var out []string
for _, n := range nodes {
out = append(out, flattenJSONStrings(n)...)
}
return out
}
func flattenJSONStrings(v any) []string {
switch x := v.(type) {
case string:
return []string{strings.TrimSpace(x)}
case []any:
var out []string
for _, item := range x {
out = append(out, flattenJSONStrings(item)...)
}
return out
case map[string]any:
var out []string
for _, item := range x {
out = append(out, flattenJSONStrings(item)...)
}
return out
default:
return nil
}
}
func parseOneCIDR(s string) netip.Prefix {
if p, err := netip.ParsePrefix(s); err == nil {
return p
}
if addr, err := netip.ParseAddr(s); err == nil {
if addr.Is4() {
p, _ := addr.Prefix(32)
return p
}
p, _ := addr.Prefix(128)
return p
}
return netip.Prefix{}
}