diff --git a/internal/database/database.go b/internal/database/database.go index bf03e21..5d9a0a8 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -773,7 +773,6 @@ func (d *Database) GetASDetails(asn int) (*ASN, []LiveRoute, error) { FROM live_routes WHERE origin_asn = ? GROUP BY prefix, mask_length, ip_version - ORDER BY ip_version, mask_length, prefix ` rows, err := d.db.Query(query, asn) @@ -785,10 +784,24 @@ func (d *Database) GetASDetails(asn int) (*ASN, []LiveRoute, error) { var prefixes []LiveRoute for rows.Next() { var route LiveRoute - err := rows.Scan(&route.Prefix, &route.MaskLength, &route.IPVersion, &route.LastUpdated) + var lastUpdatedStr string + err := rows.Scan(&route.Prefix, &route.MaskLength, &route.IPVersion, &lastUpdatedStr) if err != nil { + d.logger.Error("Failed to scan prefix row", "error", err, "asn", asn) + continue } + // Parse the timestamp string + route.LastUpdated, err = time.Parse("2006-01-02 15:04:05-07:00", lastUpdatedStr) + if err != nil { + // Try without timezone + route.LastUpdated, err = time.Parse("2006-01-02 15:04:05", lastUpdatedStr) + if err != nil { + d.logger.Error("Failed to parse timestamp", "error", err, "timestamp", lastUpdatedStr) + + continue + } + } route.OriginASN = asn prefixes = append(prefixes, route) } diff --git a/internal/server/handlers.go b/internal/server/handlers.go index 5894820..67090ff 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -1,6 +1,7 @@ package server import ( + "bytes" "context" "encoding/json" "errors" @@ -8,6 +9,7 @@ import ( "net/http" "net/url" "runtime" + "sort" "strconv" "time" @@ -500,6 +502,43 @@ func (s *Server) handleASDetail() http.HandlerFunc { } } + // Sort prefixes by network address + sort.Slice(ipv4Prefixes, func(i, j int) bool { + // Parse the prefixes to compare network addresses + ipI, netI, _ := net.ParseCIDR(ipv4Prefixes[i].Prefix) + ipJ, netJ, _ := net.ParseCIDR(ipv4Prefixes[j].Prefix) + + // Compare by network address first + cmp := bytes.Compare(ipI.To4(), ipJ.To4()) + if cmp != 0 { + return cmp < 0 + } + + // If network addresses are equal, compare by mask length + onesI, _ := netI.Mask.Size() + onesJ, _ := netJ.Mask.Size() + + return onesI < onesJ + }) + + sort.Slice(ipv6Prefixes, func(i, j int) bool { + // Parse the prefixes to compare network addresses + ipI, netI, _ := net.ParseCIDR(ipv6Prefixes[i].Prefix) + ipJ, netJ, _ := net.ParseCIDR(ipv6Prefixes[j].Prefix) + + // Compare by network address first + cmp := bytes.Compare(ipI.To16(), ipJ.To16()) + if cmp != 0 { + return cmp < 0 + } + + // If network addresses are equal, compare by mask length + onesI, _ := netI.Mask.Size() + onesJ, _ := netJ.Mask.Size() + + return onesI < onesJ + }) + // Prepare template data data := struct { ASN *database.ASN