Fix JavaScript UI and complete database table migration

- Update status page JavaScript to reset all fields to '-' on error
- Fix status page to not show 'Connected' when API returns error
- Update remaining database methods to use new live_routes_v4/v6 tables
- Fix GetStatsContext to count routes from both IPv4 and IPv6 tables
- Fix UpsertLiveRoute to insert into correct table based on IP version
- Fix DeleteLiveRoute to determine table from prefix IP version
This commit is contained in:
Jeffrey Paul 2025-07-28 22:39:01 +02:00
parent 3673264552
commit fc32090483
4 changed files with 354182 additions and 372 deletions

View File

@ -847,11 +847,17 @@ func (d *Database) GetStatsContext(ctx context.Context) (Stats, error) {
stats.FileSizeBytes = fileInfo.Size() stats.FileSizeBytes = fileInfo.Size()
} }
// Get live routes count // Get live routes count from both tables
err = d.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM live_routes").Scan(&stats.LiveRoutes) var v4Count, v6Count int
err = d.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM live_routes_v4").Scan(&v4Count)
if err != nil { if err != nil {
return stats, fmt.Errorf("failed to count live routes: %w", err) return stats, fmt.Errorf("failed to count IPv4 routes: %w", err)
} }
err = d.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM live_routes_v6").Scan(&v6Count)
if err != nil {
return stats, fmt.Errorf("failed to count IPv6 routes: %w", err)
}
stats.LiveRoutes = v4Count + v6Count
// Get prefix distribution // Get prefix distribution
stats.IPv4PrefixDistribution, stats.IPv6PrefixDistribution, err = d.GetPrefixDistributionContext(ctx) stats.IPv4PrefixDistribution, stats.IPv6PrefixDistribution, err = d.GetPrefixDistributionContext(ctx)
@ -868,19 +874,38 @@ func (d *Database) UpsertLiveRoute(route *LiveRoute) error {
d.lock("UpsertLiveRoute") d.lock("UpsertLiveRoute")
defer d.unlock() defer d.unlock()
query := ` // Choose table based on IP version
INSERT INTO live_routes (id, prefix, mask_length, ip_version, origin_asn, peer_ip, as_path, next_hop, tableName := "live_routes_v4"
last_updated, v4_ip_start, v4_ip_end) if route.IPVersion == ipVersionV6 {
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) tableName = "live_routes_v6"
ON CONFLICT(prefix, origin_asn, peer_ip) DO UPDATE SET }
mask_length = excluded.mask_length,
ip_version = excluded.ip_version, var query string
as_path = excluded.as_path, if route.IPVersion == ipVersionV4 {
next_hop = excluded.next_hop, query = fmt.Sprintf(`
last_updated = excluded.last_updated, INSERT INTO %s (id, prefix, mask_length, origin_asn, peer_ip, as_path, next_hop,
v4_ip_start = excluded.v4_ip_start, last_updated, ip_start, ip_end)
v4_ip_end = excluded.v4_ip_end VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
` ON CONFLICT(prefix, origin_asn, peer_ip) DO UPDATE SET
mask_length = excluded.mask_length,
as_path = excluded.as_path,
next_hop = excluded.next_hop,
last_updated = excluded.last_updated,
ip_start = excluded.ip_start,
ip_end = excluded.ip_end
`, tableName)
} else {
query = fmt.Sprintf(`
INSERT INTO %s (id, prefix, mask_length, origin_asn, peer_ip, as_path, next_hop,
last_updated)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(prefix, origin_asn, peer_ip) DO UPDATE SET
mask_length = excluded.mask_length,
as_path = excluded.as_path,
next_hop = excluded.next_hop,
last_updated = excluded.last_updated
`, tableName)
}
// Encode AS path as JSON // Encode AS path as JSON
pathJSON, err := json.Marshal(route.ASPath) pathJSON, err := json.Marshal(route.ASPath)
@ -888,28 +913,40 @@ func (d *Database) UpsertLiveRoute(route *LiveRoute) error {
return fmt.Errorf("failed to encode AS path: %w", err) return fmt.Errorf("failed to encode AS path: %w", err)
} }
// Convert v4_ip_start and v4_ip_end to interface{} for SQL NULL handling if route.IPVersion == ipVersionV4 {
var v4Start, v4End interface{} // Convert v4_ip_start and v4_ip_end to interface{} for SQL NULL handling
if route.V4IPStart != nil { var v4Start, v4End interface{}
v4Start = *route.V4IPStart if route.V4IPStart != nil {
} v4Start = *route.V4IPStart
if route.V4IPEnd != nil { }
v4End = *route.V4IPEnd if route.V4IPEnd != nil {
} v4End = *route.V4IPEnd
}
_, err = d.db.Exec(query, _, err = d.db.Exec(query,
route.ID.String(), route.ID.String(),
route.Prefix, route.Prefix,
route.MaskLength, route.MaskLength,
route.IPVersion, route.OriginASN,
route.OriginASN, route.PeerIP,
route.PeerIP, string(pathJSON),
string(pathJSON), route.NextHop,
route.NextHop, route.LastUpdated,
route.LastUpdated, v4Start,
v4Start, v4End,
v4End, )
) } else {
_, err = d.db.Exec(query,
route.ID.String(),
route.Prefix,
route.MaskLength,
route.OriginASN,
route.PeerIP,
string(pathJSON),
route.NextHop,
route.LastUpdated,
)
}
return err return err
} }
@ -920,16 +957,25 @@ func (d *Database) DeleteLiveRoute(prefix string, originASN int, peerIP string)
d.lock("DeleteLiveRoute") d.lock("DeleteLiveRoute")
defer d.unlock() defer d.unlock()
var query string // Determine table based on prefix IP version
var err error _, ipnet, err := net.ParseCIDR(prefix)
if err != nil {
return fmt.Errorf("invalid prefix format: %w", err)
}
tableName := "live_routes_v4"
if ipnet.IP.To4() == nil {
tableName = "live_routes_v6"
}
var query string
if originASN == 0 { if originASN == 0 {
// Delete all routes for this prefix from this peer // Delete all routes for this prefix from this peer
query = `DELETE FROM live_routes WHERE prefix = ? AND peer_ip = ?` query = fmt.Sprintf(`DELETE FROM %s WHERE prefix = ? AND peer_ip = ?`, tableName)
_, err = d.db.Exec(query, prefix, peerIP) _, err = d.db.Exec(query, prefix, peerIP)
} else { } else {
// Delete specific route // Delete specific route
query = `DELETE FROM live_routes WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?` query = fmt.Sprintf(`DELETE FROM %s WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`, tableName)
_, err = d.db.Exec(query, prefix, originASN, peerIP) _, err = d.db.Exec(query, prefix, originASN, peerIP)
} }

View File

@ -251,7 +251,7 @@ func (s *Server) handleStats() http.HandlerFunc {
return return
case err := <-errChan: case err := <-errChan:
s.logger.Error("Failed to get database stats", "error", err) s.logger.Error("Failed to get database stats", "error", err)
http.Error(w, err.Error(), http.StatusInternalServerError) writeJSONError(w, http.StatusInternalServerError, err.Error())
return return
case dbStats = <-statsChan: case dbStats = <-statsChan:

View File

@ -286,6 +286,39 @@
}); });
} }
function resetAllFields() {
// Reset all metric fields to '-'
document.getElementById('connected').textContent = '-';
document.getElementById('connected').className = 'metric-value';
document.getElementById('uptime').textContent = '-';
document.getElementById('go_version').textContent = '-';
document.getElementById('goroutines').textContent = '-';
document.getElementById('memory_usage').textContent = '-';
document.getElementById('total_messages').textContent = '-';
document.getElementById('messages_per_sec').textContent = '-';
document.getElementById('total_bytes').textContent = '-';
document.getElementById('mbits_per_sec').textContent = '-';
document.getElementById('asns').textContent = '-';
document.getElementById('prefixes').textContent = '-';
document.getElementById('ipv4_prefixes').textContent = '-';
document.getElementById('ipv6_prefixes').textContent = '-';
document.getElementById('peerings').textContent = '-';
document.getElementById('peers').textContent = '-';
document.getElementById('database_size').textContent = '-';
document.getElementById('live_routes').textContent = '-';
document.getElementById('ipv4_routes').textContent = '-';
document.getElementById('ipv6_routes').textContent = '-';
document.getElementById('ipv4_updates_per_sec').textContent = '-';
document.getElementById('ipv6_updates_per_sec').textContent = '-';
// Clear handler stats
document.getElementById('handler-stats-container').innerHTML = '';
// Clear prefix distributions
document.getElementById('ipv4-prefix-distribution').innerHTML = '<div class="metric"><span class="metric-label">No data</span></div>';
document.getElementById('ipv6-prefix-distribution').innerHTML = '<div class="metric"><span class="metric-label">No data</span></div>';
}
function updateStatus() { function updateStatus() {
fetch('/api/v1/stats') fetch('/api/v1/stats')
.then(response => response.json()) .then(response => response.json())
@ -294,6 +327,7 @@
if (response.status === 'error') { if (response.status === 'error') {
document.getElementById('error').textContent = 'Error: ' + response.error.msg; document.getElementById('error').textContent = 'Error: ' + response.error.msg;
document.getElementById('error').style.display = 'block'; document.getElementById('error').style.display = 'block';
resetAllFields();
return; return;
} }
@ -340,6 +374,7 @@
.catch(error => { .catch(error => {
document.getElementById('error').textContent = 'Error fetching status: ' + error; document.getElementById('error').textContent = 'Error fetching status: ' + error;
document.getElementById('error').style.display = 'block'; document.getElementById('error').style.display = 'block';
resetAllFields();
}); });
} }

354389
log.txt

File diff suppressed because it is too large Load Diff