Files
EvoBGP/internal/pipeline/module_hash.go
T
Denozordec 34ecc5c235
CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 1m2s
CI / go (push) Successful in 27s
CI / docker-web (push) Successful in 1m27s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 16s
CI / docker-go (push) Successful in 8m5s
feat: enhance DoH profile management and resolver policy in modules
- Added `DohResolverPolicy` schema to OpenAPI documentation, defining policies for domain resolution.
- Updated module handling to support multiple DoH profiles via `doh_profile_ids` and introduced `doh_resolver_policy` in the API.
- Refactored related functions to accommodate the new DoH profile structure, ensuring backward compatibility with existing `doh_profile_id`.
- Enhanced UI components to allow selection and management of DoH profiles and policies in the web interface.
- Updated database interactions to handle new fields and ensure proper data normalization.
2026-05-19 14:57:50 +07:00

123 lines
3.6 KiB
Go

package pipeline
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"
"strings"
"evobgp/internal/store"
)
// moduleIngestInputHash fingerprints module config and child entries so snapshots invalidate on CRUD.
func moduleIngestInputHash(st store.Backend, tenantID string, mod *store.Module) (string, error) {
if st == nil || mod == nil {
return "", fmt.Errorf("module hash: missing store or module")
}
h := sha256.New()
_, _ = fmt.Fprintf(h, "type=%s\n", strings.TrimSpace(mod.Type))
_, _ = fmt.Fprintf(h, "enabled=%t\n", mod.Enabled)
if mod.DefaultCommunityID != nil {
_, _ = fmt.Fprintf(h, "default_community=%s\n", strings.TrimSpace(*mod.DefaultCommunityID))
}
_, _ = fmt.Fprintf(h, "doh_policy=%s\n", store.NormalizeDohResolverPolicy(mod.DohResolverPolicy))
for _, pid := range mod.EffectiveDohProfileIDs() {
_, _ = fmt.Fprintf(h, "doh_profile=%s\n", pid)
if prof, err := st.GetDohProfile(tenantID, pid); err == nil && prof != nil {
_, _ = fmt.Fprintf(h, "doh_url=%s\n", strings.TrimSpace(prof.URL))
if prof.TimeoutMs != nil {
_, _ = fmt.Fprintf(h, "doh_timeout=%d\n", *prof.TimeoutMs)
}
}
}
switch mod.Type {
case "IP_RANGES":
list, err := st.ListIPRangeEntries(tenantID, mod.ID)
if err != nil {
return "", err
}
sort.Slice(list, func(i, j int) bool { return list[i].Prefix < list[j].Prefix })
for _, e := range list {
comm := ""
if e.CommunityID != nil {
comm = *e.CommunityID
}
_, _ = fmt.Fprintf(h, "ip=%s|c=%s\n", e.Prefix, comm)
}
case "AS_PREFIXES":
list, err := st.ListASEntries(tenantID, mod.ID)
if err != nil {
return "", err
}
sort.Slice(list, func(i, j int) bool { return list[i].ASN < list[j].ASN })
for _, e := range list {
comm := ""
if e.CommunityID != nil {
comm = *e.CommunityID
}
_, _ = fmt.Fprintf(h, "as=%d|c=%s\n", e.ASN, comm)
}
case "CDN_CIDRS":
list, err := st.ListCDNSources(tenantID, mod.ID)
if err != nil {
return "", err
}
sort.Slice(list, func(i, j int) bool { return list[i].ID < list[j].ID })
for _, s := range list {
comm := ""
if s.CommunityID != nil {
comm = *s.CommunityID
}
interval := 0
if s.RefreshIntervalSec != nil {
interval = *s.RefreshIntervalSec
}
_, _ = fmt.Fprintf(h, "cdn=%s|url=%s|kind=%s|path=%s|c=%s|etag=%s|interval=%d\n",
s.ID, strings.TrimSpace(s.URL), s.SourceKind, strings.TrimSpace(s.PrefixPath), comm,
strings.TrimSpace(s.Etag), interval)
}
case "DOMAINS":
list, err := st.ListDomainEntries(tenantID, mod.ID)
if err != nil {
return "", err
}
sort.Slice(list, func(i, j int) bool { return list[i].FQDN < list[j].FQDN })
for _, e := range list {
comm := ""
if e.CommunityID != nil {
comm = *e.CommunityID
}
_, _ = fmt.Fprintf(h, "dom=%s|c=%s\n", strings.TrimSpace(e.FQDN), comm)
}
default:
_, _ = fmt.Fprintf(h, "unknown_type=%s\n", mod.Type)
}
return hex.EncodeToString(h.Sum(nil)), nil
}
func persistModuleSnapshot(st store.Backend, tenantID string, mod *store.Module, rows []store.PrefixRow) error {
if st == nil || mod == nil {
return nil
}
hash, err := moduleIngestInputHash(st, tenantID, mod)
if err != nil {
return err
}
return st.SetModulePrefixSnapshot(tenantID, mod.ID, hash, rows)
}
func moduleRowsFromSnapshot(st store.Backend, tenantID string, mod *store.Module) ([]store.PrefixRow, bool, error) {
hash, err := moduleIngestInputHash(st, tenantID, mod)
if err != nil {
return nil, false, err
}
snap, ok, err := st.GetModulePrefixSnapshot(tenantID, mod.ID)
if err != nil || !ok || snap == nil || snap.InputHash != hash {
return nil, false, err
}
cp := append([]store.PrefixRow(nil), snap.Prefixes...)
return cp, true, nil
}