package track1 import ( "context" "os" "strings" "time" "github.com/explorer/backend/api/freshness" ) func relaySnapshotStatus(relay map[string]interface{}) string { if relay == nil { return "" } if probe, ok := relay["url_probe"].(map[string]interface{}); ok { if okValue, exists := probe["ok"].(bool); exists && !okValue { return "down" } if body, ok := probe["body"].(map[string]interface{}); ok { if status, ok := body["status"].(string); ok { return strings.ToLower(strings.TrimSpace(status)) } } } if _, ok := relay["file_snapshot_error"].(string); ok { return "down" } if snapshot, ok := relay["file_snapshot"].(map[string]interface{}); ok { if status, ok := snapshot["status"].(string); ok { return strings.ToLower(strings.TrimSpace(status)) } } return "" } func relayNeedsAttention(relay map[string]interface{}) bool { status := relaySnapshotStatus(relay) switch status { case "degraded", "stale", "stopped", "down": return true default: return false } } // BuildBridgeStatusData builds the inner `data` object for bridge/status and SSE payloads. func (s *Server) BuildBridgeStatusData(ctx context.Context) map[string]interface{} { rpc138 := strings.TrimSpace(os.Getenv("RPC_URL")) if rpc138 == "" { rpc138 = "http://localhost:8545" } var probes []RPCProbeResult p138 := ProbeEVMJSONRPC(ctx, "chain-138", "138", rpc138) probes = append(probes, p138) if eth := strings.TrimSpace(os.Getenv("ETH_MAINNET_RPC_URL")); eth != "" { probes = append(probes, ProbeEVMJSONRPC(ctx, "ethereum-mainnet", "1", eth)) } for _, row := range ParseExtraRPCProbes() { name, u, ck := row[0], row[1], row[2] probes = append(probes, ProbeEVMJSONRPC(ctx, name, ck, u)) } overall := "operational" if !p138.OK { overall = "degraded" } else { for _, p := range probes { if !p.OK { overall = "degraded" break } } } now := time.Now().UTC().Format(time.RFC3339) chains := map[string]interface{}{ "138": map[string]interface{}{ "name": "Defi Oracle Meta Mainnet", "status": chainStatusFromProbe(p138), "last_sync": now, "latency_ms": p138.LatencyMs, "head_age_sec": p138.HeadAgeSeconds, "block_number": p138.BlockNumberDec, "endpoint": p138.Endpoint, "probe_error": p138.Error, }, } for _, p := range probes { if p.ChainKey != "1" && p.Name != "ethereum-mainnet" { continue } chains["1"] = map[string]interface{}{ "name": "Ethereum Mainnet", "status": chainStatusFromProbe(p), "last_sync": now, "latency_ms": p.LatencyMs, "head_age_sec": p.HeadAgeSeconds, "block_number": p.BlockNumberDec, "endpoint": p.Endpoint, "probe_error": p.Error, } break } probeJSON := make([]map[string]interface{}, 0, len(probes)) for _, p := range probes { probeJSON = append(probeJSON, map[string]interface{}{ "name": p.Name, "chainKey": p.ChainKey, "endpoint": p.Endpoint, "ok": p.OK, "latencyMs": p.LatencyMs, "blockNumber": p.BlockNumber, "blockNumberDec": p.BlockNumberDec, "headAgeSeconds": p.HeadAgeSeconds, "error": p.Error, }) } data := map[string]interface{}{ "status": overall, "chains": chains, "rpc_probe": probeJSON, "checked_at": now, } if ov := readOptionalVerifyJSON(); ov != nil { data["operator_verify"] = ov } if s.freshnessLoader != nil { if snapshot, completeness, sampling, diagnostics, 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 if diagnostics != nil { data["diagnostics"] = diagnostics } 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 { data["ccip_relay"] = ccip } for _, value := range relays { relay, ok := value.(map[string]interface{}) if ok && relayNeedsAttention(relay) { data["status"] = "degraded" break } } } 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" } }