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
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:
@@ -0,0 +1,173 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"sort"
|
||||
|
||||
"evobgp/internal/store"
|
||||
)
|
||||
|
||||
// Prefix collapse replaces the former prune+merge fixed-point loop (O(n²) per pass,
|
||||
// multiple passes) with a single sort + linear stack pass: O(n log n) overall.
|
||||
//
|
||||
// Classic trie-collapse without building a trie:
|
||||
// 1. Sort prefixes by (address, mask len ascending).
|
||||
// 2. Walk left to right keeping a stack of "open" prefixes:
|
||||
// - while the stack top contains the new prefix -> the top covers it, drop the new one (prune);
|
||||
// - else, while the stack top is a sibling of the new prefix (same mask, XOR of the
|
||||
// network bit equals the parent block) and merging is allowed for that mask length
|
||||
// -> pop the sibling, replace the new prefix with the parent and re-check;
|
||||
// - otherwise push the new prefix.
|
||||
//
|
||||
// A parent absorbs its sibling children only when both halves are present, which matches
|
||||
// the previous merge-to-fixed-point semantics, including the guard that forbids merging
|
||||
// above a floor mask (IPv4 /8, IPv6 /16).
|
||||
|
||||
type collapseItem struct {
|
||||
row store.PrefixRow
|
||||
pfx netip.Prefix
|
||||
}
|
||||
|
||||
// collapsePrefixGroup collapses one (community, source) group of same-family prefixes.
|
||||
// All rows must be masked; family and floor are enforced by minBits.
|
||||
func collapsePrefixGroup(rows []store.PrefixRow, is4 bool) []store.PrefixRow {
|
||||
if len(rows) <= 1 {
|
||||
return rows
|
||||
}
|
||||
items := make([]collapseItem, 0, len(rows))
|
||||
seen := make(map[string]struct{}, len(rows))
|
||||
for _, row := range rows {
|
||||
if _, dup := seen[row.Prefix]; dup {
|
||||
continue
|
||||
}
|
||||
seen[row.Prefix] = struct{}{}
|
||||
pfx, err := netip.ParsePrefix(row.Prefix)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
pfx = pfx.Masked()
|
||||
if is4 != pfx.Addr().Is4() {
|
||||
continue
|
||||
}
|
||||
items = append(items, collapseItem{row: row, pfx: pfx})
|
||||
}
|
||||
if len(items) <= 1 {
|
||||
out := make([]store.PrefixRow, 0, len(items))
|
||||
for _, it := range items {
|
||||
out = append(out, it.row)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Sort by (address, mask len): a parent always sorts before its children, and a
|
||||
// shorter sibling sorts before a longer one within the same parent block.
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
a, b := items[i].pfx, items[j].pfx
|
||||
if a.Addr() != b.Addr() {
|
||||
return lessAddr(a.Addr(), b.Addr())
|
||||
}
|
||||
return a.Bits() < b.Bits()
|
||||
})
|
||||
|
||||
minBits := 8
|
||||
if !is4 {
|
||||
minBits = 16
|
||||
}
|
||||
|
||||
stack := make([]collapseItem, 0, len(items))
|
||||
for _, it := range items {
|
||||
cur := it
|
||||
dropped := false
|
||||
for len(stack) > 0 {
|
||||
top := stack[len(stack)-1]
|
||||
if top.pfx.Contains(cur.pfx.Addr()) && top.pfx.Bits() <= cur.pfx.Bits() {
|
||||
// Covered by an existing prefix: drop (prune).
|
||||
dropped = true
|
||||
break
|
||||
}
|
||||
if cur.pfx.Bits() == top.pfx.Bits() && cur.pfx.Bits() > minBits && areSiblings(top.pfx, cur.pfx) {
|
||||
// Merge siblings into the parent (parent keeps the lower sibling's attributes),
|
||||
// then re-check the parent against the new stack top.
|
||||
stack = stack[:len(stack)-1]
|
||||
parentBits := cur.pfx.Bits() - 1
|
||||
parent := netip.PrefixFrom(maskAddr(cur.pfx.Addr(), parentBits, is4), parentBits).Masked()
|
||||
cur = collapseItem{row: top.row, pfx: parent}
|
||||
cur.row.Prefix = parent.String()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if !dropped {
|
||||
stack = append(stack, cur)
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]store.PrefixRow, 0, len(stack))
|
||||
for _, it := range stack {
|
||||
out = append(out, it.row)
|
||||
}
|
||||
sortPrefixRows(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// areSiblings reports whether two same-length prefixes combine into their common parent.
|
||||
func areSiblings(a, b netip.Prefix) bool {
|
||||
if a.Bits() != b.Bits() || a.Bits() == 0 {
|
||||
return false
|
||||
}
|
||||
parentBits := a.Bits() - 1
|
||||
pa := maskAddr(a.Addr(), parentBits, a.Addr().Is4())
|
||||
pb := maskAddr(b.Addr(), parentBits, b.Addr().Is4())
|
||||
return pa == pb
|
||||
}
|
||||
|
||||
// maskAddr clears the host bits below prefixLen (IPv4: 32-bit space; IPv6: 128-bit).
|
||||
func maskAddr(a netip.Addr, prefixLen int, is4 bool) netip.Addr {
|
||||
if is4 {
|
||||
v := uint32FromIPv4(a)
|
||||
if prefixLen <= 0 {
|
||||
v = 0
|
||||
} else if prefixLen < 32 {
|
||||
v &= ^(uint32(1)<<(32-prefixLen) - 1)
|
||||
}
|
||||
return u32ToIPv4(v)
|
||||
}
|
||||
b := a.As16()
|
||||
hostBits := 128 - prefixLen
|
||||
fullBytes := hostBits / 8
|
||||
for i := 15; i > 15-fullBytes; i-- {
|
||||
b[i] = 0
|
||||
}
|
||||
if rem := hostBits % 8; rem > 0 {
|
||||
idx := 15 - fullBytes
|
||||
if idx >= 0 && idx < 16 {
|
||||
b[idx] &= byte(0xFF << rem)
|
||||
}
|
||||
}
|
||||
return netip.AddrFrom16(b)
|
||||
}
|
||||
|
||||
func uint32FromIPv4(a netip.Addr) uint32 {
|
||||
o := a.As4()
|
||||
return uint32(o[0])<<24 | uint32(o[1])<<16 | uint32(o[2])<<8 | uint32(o[3])
|
||||
}
|
||||
|
||||
func u32ToIPv4(v uint32) netip.Addr {
|
||||
return netip.AddrFrom4([4]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
|
||||
}
|
||||
|
||||
func lessAddr(a, b netip.Addr) bool {
|
||||
if a.Is4() != b.Is4() {
|
||||
return a.Is4()
|
||||
}
|
||||
if a.Is4() {
|
||||
return uint32FromIPv4(a) < uint32FromIPv4(b)
|
||||
}
|
||||
a16, b16 := a.As16(), b.As16()
|
||||
for i := 0; i < 16; i++ {
|
||||
if a16[i] != b16[i] {
|
||||
return a16[i] < b16[i]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user