Freshness diagnostics API, UI trust notes, mission control/stats updates, and deploy scripts.
Made-with: Cursor
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/explorer/backend/api/freshness"
|
||||
)
|
||||
|
||||
func relaySnapshotStatus(relay map[string]interface{}) string {
|
||||
@@ -129,6 +131,81 @@ func (s *Server) BuildBridgeStatusData(ctx context.Context) map[string]interface
|
||||
if ov := readOptionalVerifyJSON(); ov != nil {
|
||||
data["operator_verify"] = ov
|
||||
}
|
||||
if s.freshnessLoader != nil {
|
||||
if snapshot, completeness, sampling, err := s.freshnessLoader(ctx); err == nil && snapshot != nil {
|
||||
subsystems := map[string]interface{}{
|
||||
"rpc_head": map[string]interface{}{
|
||||
"status": chainStatusFromProbe(p138),
|
||||
"updated_at": valueOrNil(snapshot.ChainHead.Timestamp),
|
||||
"age_seconds": valueOrNil(snapshot.ChainHead.AgeSeconds),
|
||||
"source": snapshot.ChainHead.Source,
|
||||
"confidence": snapshot.ChainHead.Confidence,
|
||||
"provenance": snapshot.ChainHead.Provenance,
|
||||
"completeness": snapshot.ChainHead.Completeness,
|
||||
},
|
||||
"tx_index": map[string]interface{}{
|
||||
"status": completenessStatus(completeness.TransactionsFeed),
|
||||
"updated_at": valueOrNil(snapshot.LatestIndexedTransaction.Timestamp),
|
||||
"age_seconds": valueOrNil(snapshot.LatestIndexedTransaction.AgeSeconds),
|
||||
"source": snapshot.LatestIndexedTransaction.Source,
|
||||
"confidence": snapshot.LatestIndexedTransaction.Confidence,
|
||||
"provenance": snapshot.LatestIndexedTransaction.Provenance,
|
||||
"completeness": completeness.TransactionsFeed,
|
||||
},
|
||||
"stats_summary": map[string]interface{}{
|
||||
"status": completenessStatus(completeness.BlocksFeed),
|
||||
"updated_at": valueOrNil(sampling.StatsGeneratedAt),
|
||||
"age_seconds": ageSinceRFC3339(sampling.StatsGeneratedAt),
|
||||
"source": freshness.SourceReported,
|
||||
"confidence": freshness.ConfidenceMedium,
|
||||
"provenance": freshness.ProvenanceComposite,
|
||||
"completeness": completeness.BlocksFeed,
|
||||
},
|
||||
}
|
||||
if len(sampling.Issues) > 0 {
|
||||
subsystems["freshness_queries"] = map[string]interface{}{
|
||||
"status": "degraded",
|
||||
"updated_at": valueOrNil(sampling.StatsGeneratedAt),
|
||||
"age_seconds": ageSinceRFC3339(sampling.StatsGeneratedAt),
|
||||
"source": freshness.SourceDerived,
|
||||
"confidence": freshness.ConfidenceMedium,
|
||||
"provenance": freshness.ProvenanceComposite,
|
||||
"completeness": freshness.CompletenessPartial,
|
||||
"issues": sampling.Issues,
|
||||
}
|
||||
}
|
||||
modeKind := "live"
|
||||
modeReason := any(nil)
|
||||
modeScope := any(nil)
|
||||
if relays, ok := data["ccip_relays"].(map[string]interface{}); ok && len(relays) > 0 {
|
||||
modeKind = "snapshot"
|
||||
modeReason = "live_homepage_stream_not_attached"
|
||||
modeScope = "relay_monitoring_homepage_card_only"
|
||||
subsystems["bridge_relay_monitoring"] = map[string]interface{}{
|
||||
"status": overall,
|
||||
"updated_at": now,
|
||||
"age_seconds": int64(0),
|
||||
"source": freshness.SourceReported,
|
||||
"confidence": freshness.ConfidenceHigh,
|
||||
"provenance": freshness.ProvenanceMissionFeed,
|
||||
"completeness": freshness.CompletenessComplete,
|
||||
}
|
||||
}
|
||||
data["freshness"] = snapshot
|
||||
data["subsystems"] = subsystems
|
||||
data["sampling"] = sampling
|
||||
data["mode"] = map[string]interface{}{
|
||||
"kind": modeKind,
|
||||
"updated_at": now,
|
||||
"age_seconds": int64(0),
|
||||
"reason": modeReason,
|
||||
"scope": modeScope,
|
||||
"source": freshness.SourceReported,
|
||||
"confidence": freshness.ConfidenceHigh,
|
||||
"provenance": freshness.ProvenanceMissionFeed,
|
||||
}
|
||||
}
|
||||
}
|
||||
if relays := FetchCCIPRelayHealths(ctx); relays != nil {
|
||||
data["ccip_relays"] = relays
|
||||
if ccip := primaryRelayHealth(relays); ccip != nil {
|
||||
@@ -142,5 +219,58 @@ func (s *Server) BuildBridgeStatusData(ctx context.Context) map[string]interface
|
||||
}
|
||||
}
|
||||
}
|
||||
if mode, ok := data["mode"].(map[string]interface{}); ok {
|
||||
if relays, ok := data["ccip_relays"].(map[string]interface{}); ok && len(relays) > 0 {
|
||||
mode["kind"] = "snapshot"
|
||||
mode["reason"] = "live_homepage_stream_not_attached"
|
||||
mode["scope"] = "relay_monitoring_homepage_card_only"
|
||||
if subsystems, ok := data["subsystems"].(map[string]interface{}); ok {
|
||||
subsystems["bridge_relay_monitoring"] = map[string]interface{}{
|
||||
"status": data["status"],
|
||||
"updated_at": now,
|
||||
"age_seconds": int64(0),
|
||||
"source": freshness.SourceReported,
|
||||
"confidence": freshness.ConfidenceHigh,
|
||||
"provenance": freshness.ProvenanceMissionFeed,
|
||||
"completeness": freshness.CompletenessComplete,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func valueOrNil[T any](value *T) any {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return *value
|
||||
}
|
||||
|
||||
func ageSinceRFC3339(value *string) any {
|
||||
if value == nil || *value == "" {
|
||||
return nil
|
||||
}
|
||||
parsed, err := time.Parse(time.RFC3339, *value)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
age := int64(time.Since(parsed).Seconds())
|
||||
if age < 0 {
|
||||
age = 0
|
||||
}
|
||||
return age
|
||||
}
|
||||
|
||||
func completenessStatus(value freshness.Completeness) string {
|
||||
switch value {
|
||||
case freshness.CompletenessComplete:
|
||||
return "operational"
|
||||
case freshness.CompletenessPartial:
|
||||
return "partial"
|
||||
case freshness.CompletenessStale:
|
||||
return "stale"
|
||||
default:
|
||||
return "unavailable"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user