github.com/ethersphere/bee/v2@v2.2.0/pkg/api/status.go (about) 1 // Copyright 2023 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package api 6 7 import ( 8 "context" 9 "net/http" 10 "sort" 11 "sync" 12 "time" 13 14 "github.com/ethersphere/bee/v2/pkg/jsonhttp" 15 "github.com/ethersphere/bee/v2/pkg/swarm" 16 "github.com/ethersphere/bee/v2/pkg/topology" 17 ) 18 19 type statusSnapshotResponse struct { 20 Overlay string `json:"overlay"` 21 Proximity uint `json:"proximity"` 22 BeeMode string `json:"beeMode"` 23 ReserveSize uint64 `json:"reserveSize"` 24 ReserveSizeWithinRadius uint64 `json:"reserveSizeWithinRadius"` 25 PullsyncRate float64 `json:"pullsyncRate"` 26 StorageRadius uint8 `json:"storageRadius"` 27 ConnectedPeers uint64 `json:"connectedPeers"` 28 NeighborhoodSize uint64 `json:"neighborhoodSize"` 29 RequestFailed bool `json:"requestFailed,omitempty"` 30 BatchCommitment uint64 `json:"batchCommitment"` 31 IsReachable bool `json:"isReachable"` 32 LastSyncedBlock uint64 `json:"lastSyncedBlock"` 33 } 34 35 type statusResponse struct { 36 Snapshots []statusSnapshotResponse `json:"snapshots"` 37 } 38 39 // statusAccessHandler is a middleware that limits the number of simultaneous 40 // status requests. 41 func (s *Service) statusAccessHandler(h http.Handler) http.Handler { 42 logger := s.logger.WithName("status_access").Build() 43 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 44 if !s.statusSem.TryAcquire(1) { 45 logger.Debug("simultaneous operations not supported") 46 logger.Error(nil, "simultaneous operations not supported") 47 jsonhttp.TooManyRequests(w, "simultaneous operations not supported") 48 return 49 } 50 defer s.statusSem.Release(1) 51 52 h.ServeHTTP(w, r) 53 }) 54 } 55 56 // statusGetHandler returns the current node status. 57 func (s *Service) statusGetHandler(w http.ResponseWriter, _ *http.Request) { 58 logger := s.logger.WithName("get_status").Build() 59 60 if s.beeMode == DevMode { 61 logger.Warning("status endpoint is disabled in dev mode") 62 jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation) 63 return 64 } 65 66 ss, err := s.statusService.LocalSnapshot() 67 if err != nil { 68 logger.Debug("status snapshot", "error", err) 69 logger.Error(nil, "status snapshot") 70 jsonhttp.InternalServerError(w, err) 71 return 72 } 73 74 jsonhttp.OK(w, statusSnapshotResponse{ 75 Proximity: 256, 76 Overlay: s.overlay.String(), 77 BeeMode: ss.BeeMode, 78 ReserveSize: ss.ReserveSize, 79 ReserveSizeWithinRadius: ss.ReserveSizeWithinRadius, 80 PullsyncRate: ss.PullsyncRate, 81 StorageRadius: uint8(ss.StorageRadius), 82 ConnectedPeers: ss.ConnectedPeers, 83 NeighborhoodSize: ss.NeighborhoodSize, 84 BatchCommitment: ss.BatchCommitment, 85 IsReachable: ss.IsReachable, 86 LastSyncedBlock: ss.LastSyncedBlock, 87 }) 88 } 89 90 // statusGetPeersHandler returns the status of currently connected peers. 91 func (s *Service) statusGetPeersHandler(w http.ResponseWriter, r *http.Request) { 92 logger := s.logger.WithName("get_status_peers").Build() 93 94 if s.beeMode == DevMode { 95 logger.Warning("status endpoint is disabled in dev mode") 96 jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation) 97 return 98 } 99 100 var ( 101 wg sync.WaitGroup 102 mu sync.Mutex // mu protects snapshots. 103 snapshots []statusSnapshotResponse 104 ) 105 106 peerFunc := func(address swarm.Address, po uint8) (bool, bool, error) { 107 ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) 108 109 wg.Add(1) 110 go func() { 111 defer cancel() 112 defer wg.Done() 113 114 snapshot := statusSnapshotResponse{ 115 Overlay: address.String(), 116 Proximity: uint(po), 117 } 118 119 ss, err := s.statusService.PeerSnapshot(ctx, address) 120 if err != nil { 121 logger.Debug("unable to get status snapshot for peer", "peer_address", address, "error", err) 122 snapshot.RequestFailed = true 123 } else { 124 snapshot.BeeMode = ss.BeeMode 125 snapshot.ReserveSize = ss.ReserveSize 126 snapshot.ReserveSizeWithinRadius = ss.ReserveSizeWithinRadius 127 snapshot.PullsyncRate = ss.PullsyncRate 128 snapshot.StorageRadius = uint8(ss.StorageRadius) 129 snapshot.ConnectedPeers = ss.ConnectedPeers 130 snapshot.NeighborhoodSize = ss.NeighborhoodSize 131 snapshot.BatchCommitment = ss.BatchCommitment 132 snapshot.IsReachable = ss.IsReachable 133 snapshot.LastSyncedBlock = ss.LastSyncedBlock 134 } 135 136 mu.Lock() 137 snapshots = append(snapshots, snapshot) 138 mu.Unlock() 139 }() 140 141 return false, false, nil 142 } 143 144 err := s.topologyDriver.EachConnectedPeer( 145 peerFunc, 146 topology.Select{}, 147 ) 148 if err != nil { 149 logger.Debug("status snapshot", "error", err) 150 logger.Error(nil, "status snapshot") 151 jsonhttp.InternalServerError(w, err) 152 return 153 } 154 155 wg.Wait() 156 157 sort.Slice(snapshots, func(i, j int) bool { 158 return snapshots[i].Proximity < snapshots[j].Proximity 159 }) 160 jsonhttp.OK(w, statusResponse{Snapshots: snapshots}) 161 }