NagyZoltanPeter 98c3979119 chore: return all peers from rest admin (#3395)
* Updated version of getting peers by /admin endpoints
2025-04-25 15:36:41 +02:00

344 lines
12 KiB
Nim

{.push raises: [].}
import
std/[sets, strformat, sequtils, tables],
chronicles,
json_serialization,
presto/route,
libp2p/[peerinfo, switch, peerid, protocols/pubsub/pubsubpeer]
import
waku/[
waku_core,
waku_core/topics/pubsub_topic,
waku_store_legacy/common,
waku_store/common,
waku_filter_v2,
waku_lightpush_legacy/common,
waku_relay,
waku_peer_exchange,
waku_node,
node/peer_manager,
waku_enr/sharding,
],
../responses,
../serdes,
../rest_serdes,
./types
export types
logScope:
topics = "waku node rest admin api"
const ROUTE_ADMIN_V1_PEERS* = "/admin/v1/peers" # returns all peers
const ROUTE_ADMIN_V1_SINGLE_PEER* = "/admin/v1/peer/{peerId}"
const ROUTE_ADMIN_V1_SERVICE_PEERS* = "/admin/v1/peers/service" # returns all peers
const ROUTE_ADMIN_V1_CONNECTED_PEERS* = "/admin/v1/peers/connected"
const ROUTE_ADMIN_V1_CONNECTED_PEERS_ON_SHARD* =
"/admin/v1/peers/connected/on/{shardId}"
const ROUTE_ADMIN_V1_RELAY_PEERS* = "/admin/v1/peers/relay"
const ROUTE_ADMIN_V1_RELAY_PEERS_ON_SHARD* = "/admin/v1/peers/relay/on/{shardId}"
const ROUTE_ADMIN_V1_MESH_PEERS* = "/admin/v1/peers/mesh"
const ROUTE_ADMIN_V1_MESH_PEERS_ON_SHARD* = "/admin/v1/peers/mesh/on/{shardId}"
const ROUTE_ADMIN_V1_FILTER_SUBS* = "/admin/v1/filter/subscriptions"
type PeerProtocolTuple =
tuple[
multiaddr: string,
protocol: string,
shards: seq[uint16],
connected: Connectedness,
agent: string,
origin: PeerOrigin,
]
proc tuplesToWakuPeers(peers: var WakuPeers, peersTup: seq[PeerProtocolTuple]) =
for peer in peersTup:
peers.add(
peer.multiaddr, peer.protocol, peer.shards, peer.connected, peer.agent,
peer.origin,
)
proc populateAdminPeerInfo(
peers: var WakuPeers, node: WakuNode, codec: Option[string] = none[string]()
) =
if codec.isNone():
peers = node.peerManager.switch.peerStore.peers().mapIt(WakuPeer.init(it))
else:
let peersTuples = node.peerManager.switch.peerStore.peers(codec.get()).mapIt(
(
multiaddr: constructMultiaddrStr(it),
protocol: codec.get(),
shards: it.getShards(),
connected: it.connectedness,
agent: it.agent,
origin: it.origin,
)
)
tuplesToWakuPeers(peers, peersTuples)
proc populateAdminPeerInfoForAll(node: WakuNode): WakuPeers =
var peers: WakuPeers = @[]
populateAdminPeerInfo(peers, node)
return peers
proc populateAdminPeerInfoForCodecs(node: WakuNode, codecs: seq[string]): WakuPeers =
var peers: WakuPeers = @[]
for codec in codecs:
populateAdminPeerInfo(peers, node, some(codec))
return peers
proc installAdminV1GetPeersHandler(router: var RestRouter, node: WakuNode) =
router.api(MethodGet, ROUTE_ADMIN_V1_PEERS) do() -> RestApiResponse:
let peers = populateAdminPeerInfoForAll(node)
let resp = RestApiResponse.jsonResponse(peers, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
router.api(MethodGet, ROUTE_ADMIN_V1_SINGLE_PEER) do(
peerId: string
) -> RestApiResponse:
let peerIdString = peerId.valueOr:
return RestApiResponse.badRequest("Invalid argument:" & $error)
let peerIdVal: PeerId = PeerId.init(peerIdString).valueOr:
return RestApiResponse.badRequest("Invalid argument:" & $error)
if node.peerManager.switch.peerStore.peerExists(peerIdVal):
let peerInfo = node.peerManager.switch.peerStore.getPeer(peerIdVal)
let peer = WakuPeer.init(peerInfo)
let resp = RestApiResponse.jsonResponse(peer, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
else:
return RestApiResponse.notFound(fmt("Peer with ID {peerId} not found"))
router.api(MethodGet, ROUTE_ADMIN_V1_SERVICE_PEERS) do() -> RestApiResponse:
let peers = populateAdminPeerInfoForCodecs(
node,
@[
WakuRelayCodec, WakuFilterSubscribeCodec, WakuStoreCodec, WakuLegacyStoreCodec,
WakuLegacyLightPushCodec, WakuLightPushCodec, WakuPeerExchangeCodec,
WakuReconciliationCodec,
],
)
let resp = RestApiResponse.jsonResponse(peers, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
router.api(MethodGet, ROUTE_ADMIN_V1_CONNECTED_PEERS) do() -> RestApiResponse:
let allPeers = populateAdminPeerInfoForAll(node)
let connectedPeers = allPeers.filterIt(it.connected == Connectedness.Connected)
let resp = RestApiResponse.jsonResponse(connectedPeers, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
router.api(MethodGet, ROUTE_ADMIN_V1_CONNECTED_PEERS_ON_SHARD) do(
shardId: uint16
) -> RestApiResponse:
let shard = shardId.valueOr:
return RestApiResponse.badRequest(fmt("Invalid shardId: {error}"))
let allPeers = populateAdminPeerInfoForAll(node)
let connectedPeers = allPeers.filterIt(
it.connected == Connectedness.Connected and it.shards.contains(shard)
)
let resp = RestApiResponse.jsonResponse(connectedPeers, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
router.api(MethodGet, ROUTE_ADMIN_V1_RELAY_PEERS) do() -> RestApiResponse:
if node.wakuRelay.isNil():
return RestApiResponse.serviceUnavailable(
"Error: Relay Protocol is not mounted to the node"
)
var relayPeers: PeersOfShards = @[]
for topic in node.wakuRelay.getSubscribedTopics():
let relayShard = RelayShard.parse(topic).valueOr:
error "Invalid subscribed topic", error = error, topic = topic
continue
let pubsubPeers =
node.wakuRelay.getConnectedPubSubPeers(topic).get(initHashSet[PubSubPeer](0))
relayPeers.add(
PeersOfShard(
shard: relayShard.shardId,
peers: toSeq(pubsubPeers).mapIt(WakuPeer.init(it, node.peerManager)),
)
)
let resp = RestApiResponse.jsonResponse(relayPeers, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
router.api(MethodGet, ROUTE_ADMIN_V1_RELAY_PEERS_ON_SHARD) do(
shardId: uint16
) -> RestApiResponse:
let shard = shardId.valueOr:
return RestApiResponse.badRequest(fmt("Invalid shardId: {error}"))
if node.wakuRelay.isNil():
return RestApiResponse.serviceUnavailable(
"Error: Relay Protocol is not mounted to the node"
)
let topic =
toPubsubTopic(RelayShard(clusterId: node.wakuSharding.clusterId, shardId: shard))
let pubsubPeers =
node.wakuRelay.getConnectedPubSubPeers(topic).get(initHashSet[PubSubPeer](0))
let relayPeer = PeersOfShard(
shard: shard, peers: toSeq(pubsubPeers).mapIt(WakuPeer.init(it, node.peerManager))
)
let resp = RestApiResponse.jsonResponse(relayPeer, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
router.api(MethodGet, ROUTE_ADMIN_V1_MESH_PEERS) do() -> RestApiResponse:
if node.wakuRelay.isNil():
return RestApiResponse.serviceUnavailable(
"Error: Relay Protocol is not mounted to the node"
)
var relayPeers: PeersOfShards = @[]
for topic in node.wakuRelay.getSubscribedTopics():
let relayShard = RelayShard.parse(topic).valueOr:
error "Invalid subscribed topic", error = error, topic = topic
continue
let peers =
node.wakuRelay.getPubSubPeersInMesh(topic).get(initHashSet[PubSubPeer](0))
relayPeers.add(
PeersOfShard(
shard: relayShard.shardId,
peers: toSeq(peers).mapIt(WakuPeer.init(it, node.peerManager)),
)
)
let resp = RestApiResponse.jsonResponse(relayPeers, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
router.api(MethodGet, ROUTE_ADMIN_V1_MESH_PEERS_ON_SHARD) do(
shardId: uint16
) -> RestApiResponse:
let shard = shardId.valueOr:
return RestApiResponse.badRequest(fmt("Invalid shardId: {error}"))
if node.wakuRelay.isNil():
return RestApiResponse.serviceUnavailable(
"Error: Relay Protocol is not mounted to the node"
)
let topic =
toPubsubTopic(RelayShard(clusterId: node.wakuSharding.clusterId, shardId: shard))
let peers =
node.wakuRelay.getPubSubPeersInMesh(topic).get(initHashSet[PubSubPeer](0))
let relayPeer = PeersOfShard(
shard: shard, peers: toSeq(peers).mapIt(WakuPeer.init(it, node.peerManager))
)
let resp = RestApiResponse.jsonResponse(relayPeer, status = Http200).valueOr:
error "An error occurred while building the json response: ", error = error
return RestApiResponse.internalServerError(
fmt("An error occurred while building the json response: {error}")
)
return resp
proc installAdminV1PostPeersHandler(router: var RestRouter, node: WakuNode) =
router.api(MethodPost, ROUTE_ADMIN_V1_PEERS) do(
contentBody: Option[ContentBody]
) -> RestApiResponse:
let peers: seq[string] = decodeRequestBody[seq[string]](contentBody).valueOr:
let e = $error
return RestApiResponse.badRequest(fmt("Failed to decode request: {e}"))
for i, peer in peers:
let peerInfo = parsePeerInfo(peer).valueOr:
let e = $error
return RestApiResponse.badRequest(fmt("Couldn't parse remote peer info: {e}"))
if not (await node.peerManager.connectPeer(peerInfo, source = "rest")):
return RestApiResponse.badRequest(
fmt("Failed to connect to peer at index: {i} - {peer}")
)
return RestApiResponse.ok()
proc installAdminV1GetFilterSubsHandler(router: var RestRouter, node: WakuNode) =
router.api(MethodGet, ROUTE_ADMIN_V1_FILTER_SUBS) do() -> RestApiResponse:
if node.wakuFilter.isNil():
return
RestApiResponse.badRequest("Error: Filter Protocol is not mounted to the node")
var
subscriptions: seq[FilterSubscription] = @[]
filterCriteria: seq[FilterTopic]
for peerId in node.wakuFilter.subscriptions.peersSubscribed.keys:
filterCriteria = node.wakuFilter.subscriptions.getPeerSubscriptions(peerId).mapIt(
FilterTopic(pubsubTopic: it[0], contentTopic: it[1])
)
subscriptions.add(
FilterSubscription(peerId: $peerId, filterCriteria: filterCriteria)
)
let resp = RestApiResponse.jsonResponse(subscriptions, status = Http200)
if resp.isErr():
error "An error ocurred while building the json respose: ", error = resp.error
return RestApiResponse.internalServerError(
fmt("An error ocurred while building the json respose: {resp.error}")
)
return resp.get()
proc installAdminApiHandlers*(router: var RestRouter, node: WakuNode) =
installAdminV1GetPeersHandler(router, node)
installAdminV1PostPeersHandler(router, node)
installAdminV1GetFilterSubsHandler(router, node)