Files
explorer-monorepo/backend/api/rest/track_routes.go
defiQUG 0c869f7930 feat(freshness): enhance diagnostics and update snapshot structure
- Introduced a new Diagnostics struct to capture transaction visibility state and activity state.
- Updated BuildSnapshot function to return diagnostics alongside snapshot, completeness, and sampling.
- Enhanced test cases to validate the new diagnostics data.
- Updated frontend components to utilize the new diagnostics information for improved user feedback on freshness context.

This change improves the observability of transaction activity and enhances the user experience by providing clearer insights into the freshness of data.
2026-04-12 18:22:08 -07:00

150 lines
5.6 KiB
Go

package rest
import (
"context"
"net/http"
"os"
"strings"
"time"
"github.com/explorer/backend/api/freshness"
"github.com/explorer/backend/api/middleware"
"github.com/explorer/backend/api/track1"
"github.com/explorer/backend/api/track2"
"github.com/explorer/backend/api/track3"
"github.com/explorer/backend/api/track4"
"github.com/explorer/backend/libs/go-rpc-gateway"
)
// SetupTrackRoutes sets up track-specific routes with proper middleware
func (s *Server) SetupTrackRoutes(mux *http.ServeMux, authMiddleware *middleware.AuthMiddleware) {
// Initialize Track 1 (RPC Gateway) using reusable lib
rpcURL := os.Getenv("RPC_URL")
if rpcURL == "" {
rpcURL = "http://localhost:8545"
}
var cache gateway.Cache
if redisURL := os.Getenv("REDIS_URL"); redisURL != "" {
if c, err := gateway.NewRedisCache(redisURL); err == nil {
cache = c
}
}
if cache == nil {
cache = gateway.NewInMemoryCache()
}
rateLimitConfig := gateway.RateLimitConfig{
RequestsPerSecond: 10,
RequestsPerMinute: 100,
BurstSize: 20,
}
var rateLimiter gateway.RateLimiter
if redisURL := os.Getenv("REDIS_URL"); redisURL != "" {
if rl, err := gateway.NewRedisRateLimiter(redisURL, rateLimitConfig); err == nil {
rateLimiter = rl
}
}
if rateLimiter == nil {
rateLimiter = gateway.NewInMemoryRateLimiter(rateLimitConfig)
}
rpcGateway := gateway.NewRPCGateway(rpcURL, cache, rateLimiter)
track1Server := track1.NewServer(rpcGateway, func(ctx context.Context) (*freshness.Snapshot, *freshness.SummaryCompleteness, *freshness.Sampling, *freshness.Diagnostics, error) {
if s.db == nil {
return nil, nil, nil, nil, nil
}
now := time.Now().UTC()
snapshot, completeness, sampling, diagnostics, err := freshness.BuildSnapshot(
ctx,
s.chainID,
s.db.QueryRow,
func(ctx context.Context) (*freshness.Reference, error) {
return freshness.ProbeChainHead(ctx, rpcURL)
},
now,
nil,
nil,
)
if err != nil {
return nil, nil, nil, nil, err
}
return &snapshot, &completeness, &sampling, &diagnostics, nil
})
// Track 1 routes (public, optional auth)
mux.HandleFunc("/api/v1/track1/blocks/latest", track1Server.HandleLatestBlocks)
mux.HandleFunc("/api/v1/track1/txs/latest", track1Server.HandleLatestTransactions)
mux.HandleFunc("/api/v1/track1/block/", track1Server.HandleBlockDetail)
mux.HandleFunc("/api/v1/track1/tx/", track1Server.HandleTransactionDetail)
mux.HandleFunc("/api/v1/track1/address/", track1Server.HandleAddressBalance)
mux.HandleFunc("/api/v1/track1/bridge/status", track1Server.HandleBridgeStatus)
mux.HandleFunc("/api/v1/mission-control/stream", track1Server.HandleMissionControlStream)
mux.HandleFunc("/api/v1/mission-control/liquidity/token/", s.handleMissionControlLiquidityTokenPath)
mux.HandleFunc("/api/v1/mission-control/bridge/trace", s.HandleMissionControlBridgeTrace)
// Initialize Track 2 server
track2Server := track2.NewServer(s.db, s.chainID)
// Track 2 routes (require Track 2+)
track2Middleware := authMiddleware.RequireTrack(2)
// Track 2 route handlers with auth
track2AuthHandler := func(handler http.HandlerFunc) http.HandlerFunc {
return authMiddleware.RequireAuth(track2Middleware(http.HandlerFunc(handler))).ServeHTTP
}
mux.HandleFunc("/api/v1/track2/search", track2AuthHandler(track2Server.HandleSearch))
// Address routes - need to parse path
mux.HandleFunc("/api/v1/track2/address/", track2AuthHandler(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
parts := strings.Split(strings.TrimPrefix(path, "/api/v1/track2/address/"), "/")
if len(parts) >= 2 {
if parts[1] == "txs" {
track2Server.HandleAddressTransactions(w, r)
return
} else if parts[1] == "tokens" {
track2Server.HandleAddressTokens(w, r)
return
} else if parts[1] == "internal-txs" {
track2Server.HandleInternalTransactions(w, r)
return
}
}
writeError(w, http.StatusBadRequest, "bad_request", "Invalid Track 2 address path")
}))
mux.HandleFunc("/api/v1/track2/token/", track2AuthHandler(track2Server.HandleTokenInfo))
// Initialize Track 3 server
track3Server := track3.NewServer(s.db, s.chainID)
// Track 3 routes (require Track 3+)
track3Middleware := authMiddleware.RequireTrack(3)
track3AuthHandler := func(handler http.HandlerFunc) http.HandlerFunc {
return authMiddleware.RequireAuth(track3Middleware(http.HandlerFunc(handler))).ServeHTTP
}
mux.HandleFunc("/api/v1/track3/analytics/flows", track3AuthHandler(track3Server.HandleFlows))
mux.HandleFunc("/api/v1/track3/analytics/bridge", track3AuthHandler(track3Server.HandleBridge))
mux.HandleFunc("/api/v1/track3/analytics/token-distribution/", track3AuthHandler(track3Server.HandleTokenDistribution))
mux.HandleFunc("/api/v1/track3/analytics/address-risk/", track3AuthHandler(track3Server.HandleAddressRisk))
// Initialize Track 4 server
track4Server := track4.NewServer(s.db, s.chainID)
// Track 4 routes (require Track 4 + IP whitelist)
track4Middleware := authMiddleware.RequireTrack(4)
track4AuthHandler := func(handler http.HandlerFunc) http.HandlerFunc {
return authMiddleware.RequireAuth(track4Middleware(http.HandlerFunc(handler))).ServeHTTP
}
mux.HandleFunc("/api/v1/track4/operator/bridge/events", track4AuthHandler(track4Server.HandleBridgeEvents))
mux.HandleFunc("/api/v1/track4/operator/validators", track4AuthHandler(track4Server.HandleValidators))
mux.HandleFunc("/api/v1/track4/operator/contracts", track4AuthHandler(track4Server.HandleContracts))
mux.HandleFunc("/api/v1/track4/operator/protocol-state", track4AuthHandler(track4Server.HandleProtocolState))
mux.HandleFunc("/api/v1/track4/operator/run-script", track4AuthHandler(track4Server.HandleRunScript))
}