package reports import ( "time" "evobgp/internal/store" ) // RouterListsCatalog is the aggregated router-lists payload for GET /router-lists/catalog. type RouterListsCatalog struct { Modules []map[string]any Domains []map[string]any ASNs []map[string]any IPRanges []map[string]any Communities []map[string]any } // BuildRouterListsCatalog loads modules and entries for the tenant catalog endpoint. func BuildRouterListsCatalog(st store.Backend, tenantID string) (*RouterListsCatalog, error) { mods := st.ListModules(tenantID) out := &RouterListsCatalog{ Modules: make([]map[string]any, 0), Domains: make([]map[string]any, 0), ASNs: make([]map[string]any, 0), IPRanges: make([]map[string]any, 0), Communities: make([]map[string]any, 0), } for _, mod := range mods { switch mod.Type { case "DOMAINS", "AS_PREFIXES", "IP_RANGES": out.Modules = append(out.Modules, moduleMap(mod)) default: continue } switch mod.Type { case "DOMAINS": list, err := st.ListDomainEntries(tenantID, mod.ID) if err != nil { return nil, err } for _, x := range list { out.Domains = append(out.Domains, map[string]any{ "module_id": mod.ID, "entry": domainEntryMap(x), }) } case "AS_PREFIXES": list, err := st.ListASEntries(tenantID, mod.ID) if err != nil { return nil, err } for _, x := range list { out.ASNs = append(out.ASNs, map[string]any{ "module_id": mod.ID, "entry": asEntryMap(x), }) } case "IP_RANGES": list, err := st.ListIPRangeEntries(tenantID, mod.ID) if err != nil { return nil, err } for _, x := range list { out.IPRanges = append(out.IPRanges, map[string]any{ "module_id": mod.ID, "entry": ipRangeMap(x), }) } } } comms, err := st.ListCommunities(tenantID) if err != nil { return nil, err } for _, c := range comms { out.Communities = append(out.Communities, map[string]any{ "id": c.ID, "community": c.Community, "title": c.Title, }) } return out, nil } func moduleMap(mod *store.Module) map[string]any { m := map[string]any{ "id": mod.ID, "type": mod.Type, "name": mod.Name, "enabled": mod.Enabled, "priority": mod.Priority, "refresh_interval_sec": mod.RefreshIntervalSec, "cron_expr": mod.CronExpr, } if mod.LastRefreshedAt != nil { m["last_refreshed_at"] = mod.LastRefreshedAt.UTC().Format(time.RFC3339Nano) } else { m["last_refreshed_at"] = nil } if mod.DefaultCommunityID != nil { m["default_community_id"] = *mod.DefaultCommunityID } else { m["default_community_id"] = nil } ids := mod.EffectiveDohProfileIDs() if len(ids) > 0 { m["doh_profile_ids"] = ids } else { m["doh_profile_ids"] = []string{} } m["doh_resolver_policy"] = store.NormalizeDohResolverPolicy(mod.DohResolverPolicy) if mod.DohProfileID != nil { m["doh_profile_id"] = *mod.DohProfileID } else { m["doh_profile_id"] = nil } return m } func domainEntryMap(x *store.DomainEntry) map[string]any { m := map[string]any{"id": x.ID, "fqdn": x.FQDN} if x.CommunityID != nil { m["community_id"] = *x.CommunityID } else { m["community_id"] = nil } return m } func asEntryMap(x *store.ASEntry) map[string]any { m := map[string]any{"id": x.ID, "asn": x.ASN} if x.CommunityID != nil { m["community_id"] = *x.CommunityID } else { m["community_id"] = nil } return m } func ipRangeMap(x *store.IPRangeEntry) map[string]any { m := map[string]any{"id": x.ID, "prefix": x.Prefix} if x.CommunityID != nil { m["community_id"] = *x.CommunityID } else { m["community_id"] = nil } return m }