refactor(web): remove deprecated dashboard components and enhance KPI grid
quality / commitlint (push) Skipped
quality / changes (push) Successful in 9s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 46s
quality / web (push) Successful in 1m16s
quality / go (push) Successful in 2m42s
quality / bird2 (push) Successful in 16s
CD / quality (push) Successful in 5m19s
CD / publish (push) Successful in 7m19s

- Deleted unused components: `DashboardActivityTimeline`, `DashboardFramePanel`, `DashboardModulesGrid`, `DashboardRecentJobsGrid`, and `DashboardRecentRevisionsGrid` to streamline the dashboard.
- Updated `DashboardKpiGrid` to improve KPI display logic, including progress indicators and enhanced badge functionality.
- Refactored `DashboardNetworkHealth` to provide better status representation based on loading states and network conditions.
- Introduced new properties for KPI cards to support progress tracking and improved visual feedback.

This cleanup aims to enhance performance and maintainability of the dashboard while providing a better user experience.
This commit is contained in:
Denozordec
2026-08-31 10:15:59 +07:00
parent e469c421ca
commit dc803bcb34
51 changed files with 1895 additions and 1078 deletions
+83 -16
View File
@@ -24,28 +24,43 @@ func asnCacheTTL() time.Duration {
return time.Duration(sec) * time.Second
}
// resolveASNForEntry fetches prefixes and holder with shared TTL cache (asn_prefix_cache).
func resolveASNForEntry(ctx context.Context, st store.Backend, hc *http.Client, asn int64) ([]netip.Prefix, string, error) {
ttl := asnCacheTTL()
if st != nil {
if ent, ok, err := st.GetASNPrefixCache(asn); err == nil && ok && ent != nil && time.Since(ent.FetchedAt) < ttl {
out := make([]netip.Prefix, 0, len(ent.Prefixes))
for _, p := range ent.Prefixes {
pfx, perr := netip.ParsePrefix(strings.TrimSpace(p))
if perr != nil {
continue
}
out = append(out, pfx.Masked())
}
return out, ent.Holder, nil
// asnHolderTTL is how long a holder name stays authoritative between refreshes;
// holder text changes rarely, so it survives short prefix-cache TTLs.
func asnHolderTTL() time.Duration {
sec := 7 * 24 * 3600
if s := strings.TrimSpace(os.Getenv("EVOBGP_ASN_HOLDER_TTL_SEC")); s != "" {
if v, err := strconv.Atoi(s); err == nil && v > 0 {
sec = v
}
}
return time.Duration(sec) * time.Second
}
// resolveASNForEntry fetches prefixes and holder with shared TTL cache (asn_prefix_cache).
// The holder lookup starts concurrently with the prefix fetch (one RTT instead of two);
// a holder failure is non-fatal — the previously cached holder name is kept.
func resolveASNForEntry(ctx context.Context, st store.Backend, hc *http.Client, asn int64) ([]netip.Prefix, string, error) {
ttl := asnCacheTTL()
var prevHolder string
if st != nil {
if ent, ok, err := st.GetASNPrefixCache(asn); err == nil && ok && ent != nil {
prevHolder = ent.Holder
if time.Since(ent.FetchedAt) < ttl {
return parseASNCachePrefixes(ent.Prefixes), ent.Holder, nil
}
}
}
holderCh := startASNHolderFetch(ctx, hc, st, asn, prevHolder)
pfxs, err := asnresolve.AnnouncedPrefixes(ctx, hc, asn)
if err != nil {
holder := <-holderCh // drain to avoid leaking the goroutine's channel send
_ = holder
return nil, "", err
}
asnresolve.PolitePause()
holder, _ := asnresolve.ASHolderName(ctx, hc, asn)
holder := <-holderCh
if st != nil {
strs := make([]string, len(pfxs))
for i, p := range pfxs {
@@ -57,3 +72,55 @@ func resolveASNForEntry(ctx context.Context, st store.Backend, hc *http.Client,
}
return pfxs, holder, nil
}
// startASNHolderFetch launches the holder lookup concurrently. The returned buffered
// channel always yields exactly one value, so callers may abandon it without leaking.
func startASNHolderFetch(ctx context.Context, hc *http.Client, st store.Backend, asn int64, prevHolder string) <-chan string {
ch := make(chan string, 1)
go func() {
if prevHolder != "" && holderStillFresh(st, asn, prevHolder) {
ch <- prevHolder
return
}
holder, err := asnresolve.ASHolderName(ctx, hc, asn)
if err != nil || strings.TrimSpace(holder) == "" {
ch <- prevHolder
return
}
ch <- strings.TrimSpace(holder)
}()
return ch
}
// holderStillFresh reports whether the cached holder name is within its own (long) TTL.
func holderStillFresh(st store.Backend, asn int64, prevHolder string) bool {
if st == nil {
return false
}
ent, ok, err := st.GetASNPrefixCache(asn)
return err == nil && ok && ent != nil && ent.Holder == prevHolder && time.Since(ent.FetchedAt) < asnHolderTTL()
}
func parseASNCachePrefixes(raw []string) []netip.Prefix {
out := make([]netip.Prefix, 0, len(raw))
for _, p := range raw {
pfx, perr := netip.ParsePrefix(strings.TrimSpace(p))
if perr != nil {
continue
}
out = append(out, pfx.Masked())
}
return out
}
func parseCachedASNCachedPrefixes(raw []string) []netip.Prefix {
out := make([]netip.Prefix, 0, len(raw))
for _, p := range raw {
pfx, perr := netip.ParsePrefix(strings.TrimSpace(p))
if perr != nil {
continue
}
out = append(out, pfx.Masked())
}
return out
}