diff --git a/api/api.go b/api/api.go index ecdbb65a..bd5125c4 100644 --- a/api/api.go +++ b/api/api.go @@ -119,6 +119,7 @@ type ( Syncer interface { Addr() string Peers() []*syncer.Peer + PeerInfo(string) (syncer.PeerInfo, error) Connect(ctx context.Context, addr string) (*syncer.Peer, error) BroadcastTransactionSet(txns []types.Transaction) diff --git a/api/endpoints.go b/api/endpoints.go index e8eb70cc..9a237c54 100644 --- a/api/endpoints.go +++ b/api/endpoints.go @@ -12,6 +12,7 @@ import ( rhp3 "go.sia.tech/core/rhp/v3" "go.sia.tech/core/types" + "go.sia.tech/coreutils/syncer" "go.sia.tech/hostd/build" "go.sia.tech/hostd/host/contracts" "go.sia.tech/hostd/host/metrics" @@ -112,15 +113,29 @@ func (a *api) handleGETSyncerAddr(jc jape.Context) { } func (a *api) handleGETSyncerPeers(jc jape.Context) { - p := a.syncer.Peers() - peers := make([]Peer, len(p)) - for i, peer := range p { - peers[i] = Peer{ - Address: peer.ConnAddr, - Version: peer.Version(), + var peers []Peer + for _, p := range a.syncer.Peers() { + // create peer response with known fields + peer := Peer{ + Address: p.Addr(), + Inbound: p.Inbound, + Version: p.Version(), } + // add more info if available + info, err := a.syncer.PeerInfo(p.Addr()) + if errors.Is(err, syncer.ErrPeerNotFound) { + continue + } else if err != nil { + jc.Error(err, http.StatusInternalServerError) + return + } + peer.FirstSeen = info.FirstSeen + peer.ConnectedSince = info.LastConnect + peer.SyncedBlocks = info.SyncedBlocks + peer.SyncDuration = info.SyncDuration + peers = append(peers, peer) } - a.writeResponse(jc, PeerResp(peers)) + jc.Encode(peers) } func (a *api) handlePUTSyncerPeer(jc jape.Context) { diff --git a/api/types.go b/api/types.go index 810c405c..1da9f07a 100644 --- a/api/types.go +++ b/api/types.go @@ -127,10 +127,16 @@ type ( SubtractMinerFee bool `json:"subtractMinerFee"` } - // A Peer is a peer in the network. + // A Peer is a currently-connected peer. Peer struct { Address string `json:"address"` + Inbound bool `json:"inbound"` Version string `json:"version"` + + FirstSeen time.Time `json:"firstSeen,omitempty"` + ConnectedSince time.Time `json:"connectedSince,omitempty"` + SyncedBlocks uint64 `json:"syncedBlocks,omitempty"` + SyncDuration time.Duration `json:"syncDuration,omitempty"` } // A Setting updates a single setting on the host. It can be combined with