diff --git a/cmd/waku/node.go b/cmd/waku/node.go index 0126aaa2..b705d155 100644 --- a/cmd/waku/node.go +++ b/cmd/waku/node.go @@ -505,7 +505,7 @@ func Execute(options NodeOptions) error { var restServer *rest.WakuRest if options.RESTServer.Enable { wg.Add(1) - restServer = rest.NewWakuRest(wakuNode, options.RESTServer.Address, options.RESTServer.Port, options.PProf, options.RESTServer.RelayCacheCapacity, logger) + restServer = rest.NewWakuRest(wakuNode, options.RESTServer.Address, options.RESTServer.Port, options.PProf, options.RESTServer.Admin, options.RESTServer.RelayCacheCapacity, logger) restServer.Start(ctx, &wg) } diff --git a/cmd/waku/server/rest/admin.go b/cmd/waku/server/rest/admin.go new file mode 100644 index 00000000..a757b6f5 --- /dev/null +++ b/cmd/waku/server/rest/admin.go @@ -0,0 +1,120 @@ +package rest + +import ( + "encoding/json" + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + ma "github.com/multiformats/go-multiaddr" + "github.com/waku-org/go-waku/cmd/waku/server" + "github.com/waku-org/go-waku/logging" + "github.com/waku-org/go-waku/waku/v2/node" + "github.com/waku-org/go-waku/waku/v2/peerstore" + waku_proto "github.com/waku-org/go-waku/waku/v2/protocol" + "go.uber.org/zap" +) + +type AdminService struct { + node *node.WakuNode + mux *chi.Mux + log *zap.Logger +} + +type WakuPeer struct { + ID string `json:"id"` + MultiAddrs []string `json:"multiaddrs"` + Protocols []string `json:"protocols"` + Connected bool `json:"connected"` +} + +type WakuPeerInfo struct { + MultiAddr string `json:"multiaddr"` + Shards []int `json:"shards"` + Protocols []string `json:"protocols"` +} + +const routeAdminV1Peers = "/admin/v1/peers" + +func NewAdminService(node *node.WakuNode, m *chi.Mux, log *zap.Logger) *AdminService { + d := &AdminService{ + node: node, + mux: m, + log: log, + } + + m.Get(routeAdminV1Peers, d.getV1Peers) + m.Post(routeAdminV1Peers, d.postV1Peer) + + return d +} + +func (a *AdminService) getV1Peers(w http.ResponseWriter, req *http.Request) { + peers, err := a.node.Peers() + if err != nil { + writeErrOrResponse(w, err, nil) + return + } + response := make([]WakuPeer, 0) + for _, peer := range peers { + wPeer := WakuPeer{ + ID: peer.ID.Pretty(), + Connected: peer.Connected, + } + + for _, addr := range peer.Addrs { + wPeer.MultiAddrs = append(wPeer.MultiAddrs, addr.String()) + } + for _, proto := range peer.Protocols { + if !server.IsWakuProtocol(proto) { + continue + } + wPeer.Protocols = append(wPeer.Protocols, string(proto)) + } + response = append(response, wPeer) + } + + writeErrOrResponse(w, nil, response) +} + +func (a *AdminService) postV1Peer(w http.ResponseWriter, req *http.Request) { + var pInfo WakuPeerInfo + var topics []string + var protos []protocol.ID + + decoder := json.NewDecoder(req.Body) + if err := decoder.Decode(&pInfo); err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + defer req.Body.Close() + + addr, err := ma.NewMultiaddr(pInfo.MultiAddr) + if err != nil { + a.log.Error("building multiaddr", zap.Error(err)) + writeErrOrResponse(w, err, nil) + return + } + + for _, shard := range pInfo.Shards { + topic := waku_proto.NewStaticShardingPubsubTopic(waku_proto.ClusterIndex, uint16(shard)) + topics = append(topics, topic.String()) + } + + id, err := a.node.AddPeer(addr, peerstore.Static, topics, protos...) + if err != nil { + a.log.Error("failed to add peer", zap.Error(err)) + writeErrOrResponse(w, err, nil) + return + } + a.log.Info("add peer successful", logging.HostID("peerID", id)) + pi := peer.AddrInfo{ID: id, Addrs: []ma.Multiaddr{addr}} + err = a.node.Host().Connect(req.Context(), pi) + if err != nil { + a.log.Error("failed to connect to peer", logging.HostID("peerID", id), zap.Error(err)) + writeErrOrResponse(w, err, nil) + return + } + writeErrOrResponse(w, nil, nil) +} diff --git a/cmd/waku/server/rest/admin_api.yaml b/cmd/waku/server/rest/admin_api.yaml new file mode 100644 index 00000000..a96c6881 --- /dev/null +++ b/cmd/waku/server/rest/admin_api.yaml @@ -0,0 +1,88 @@ +openapi: 3.0.3 +info: + title: Waku V2 node REST API + version: 1.0.0 + contact: + name: VAC Team + url: https://forum.vac.dev/ + +tags: + - name: admin + description: Admin REST API for WakuV2 node + +paths: + /admin/v1/peers: + get: + summary: Get connected peers info + description: Retrieve information about connected peers. + operationId: getPeerInfo + tags: + - admin + responses: + '200': + description: Information about a Waku v2 node. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/WakuPeer' + '5XX': + description: Unexpected error. + post: + summary: Adds new peer(s) to connect with + description: Adds new peer(s) to connect with. + operationId: postPeerInfo + tags: + - admin + requestBody: + content: + application/json: + schema: + type: object + items: + $ref: '#/components/schemas/WakuPeerInfo' + responses: + '200': + description: Ok + '400': + description: Cannot connect to one or more peers. + '5XX': + description: Unexpected error. + +components: + schemas: + WakuPeerInfo: + type: object + required: + - multiaddr + - shards + - protocols + protocols: + type: array + items: + type: string + shards: + type: array + items: + type: integer + WakuPeer: + type: object + required: + - id + - addrs + - protocols + - connected + properties: + connected: + type: string + addrs: + type: array + items: + type: string + protocols: + type: array + items: + type: string + connected: + type: boolean \ No newline at end of file diff --git a/cmd/waku/server/rest/waku_rest.go b/cmd/waku/server/rest/waku_rest.go index 0041ee08..ab39e7c2 100644 --- a/cmd/waku/server/rest/waku_rest.go +++ b/cmd/waku/server/rest/waku_rest.go @@ -21,7 +21,7 @@ type WakuRest struct { relayService *RelayService } -func NewWakuRest(node *node.WakuNode, address string, port int, enablePProf bool, relayCacheCapacity int, log *zap.Logger) *WakuRest { +func NewWakuRest(node *node.WakuNode, address string, port int, enablePProf bool, enableAdmin bool, relayCacheCapacity int, log *zap.Logger) *WakuRest { wrpc := new(WakuRest) wrpc.log = log.Named("rest") @@ -56,6 +56,10 @@ func NewWakuRest(node *node.WakuNode, address string, port int, enablePProf bool wrpc.relayService = relayService } + if enableAdmin { + _ = NewAdminService(node, mux, wrpc.log) + } + return wrpc } diff --git a/cmd/waku/server/rest/waku_rest_test.go b/cmd/waku/server/rest/waku_rest_test.go index abba8ce3..b6d3bd7a 100644 --- a/cmd/waku/server/rest/waku_rest_test.go +++ b/cmd/waku/server/rest/waku_rest_test.go @@ -13,7 +13,7 @@ func TestWakuRest(t *testing.T) { n, err := node.New(options) require.NoError(t, err) - rpc := NewWakuRest(n, "127.0.0.1", 8080, false, 10, utils.Logger()) + rpc := NewWakuRest(n, "127.0.0.1", 8080, false, false, 10, utils.Logger()) require.NotNil(t, rpc.server) require.Equal(t, rpc.server.Addr, "127.0.0.1:8080") } diff --git a/cmd/waku/server/rpc/admin.go b/cmd/waku/server/rpc/admin.go index 41bd17a4..956300c8 100644 --- a/cmd/waku/server/rpc/admin.go +++ b/cmd/waku/server/rpc/admin.go @@ -7,12 +7,8 @@ import ( ma "github.com/multiformats/go-multiaddr" "go.uber.org/zap" + "github.com/waku-org/go-waku/cmd/waku/server" "github.com/waku-org/go-waku/waku/v2/node" - "github.com/waku-org/go-waku/waku/v2/protocol/filter" - "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter" - "github.com/waku-org/go-waku/waku/v2/protocol/lightpush" - "github.com/waku-org/go-waku/waku/v2/protocol/relay" - "github.com/waku-org/go-waku/waku/v2/protocol/store" ) type AdminService struct { @@ -54,15 +50,6 @@ func (a *AdminService) PostV1Peers(req *http.Request, args *PeersArgs, reply *Su return nil } -func isWakuProtocol(protocol protocol.ID) bool { - return protocol == legacy_filter.FilterID_v20beta1 || - protocol == filter.FilterPushID_v20beta1 || - protocol == filter.FilterSubscribeID_v20beta1 || - protocol == relay.WakuRelayID_v200 || - protocol == lightpush.LightPushID_v20beta1 || - protocol == store.StoreID_v20beta4 -} - func (a *AdminService) GetV1Peers(req *http.Request, args *GetPeersArgs, reply *PeersReply) error { peers, err := a.node.Peers() if err != nil { @@ -72,7 +59,7 @@ func (a *AdminService) GetV1Peers(req *http.Request, args *GetPeersArgs, reply * for _, peer := range peers { for _, addr := range peer.Addrs { for _, proto := range peer.Protocols { - if !isWakuProtocol(proto) { + if !server.IsWakuProtocol(proto) { continue } *reply = append(*reply, PeerReply{ diff --git a/cmd/waku/server/utils.go b/cmd/waku/server/utils.go new file mode 100644 index 00000000..121dd084 --- /dev/null +++ b/cmd/waku/server/utils.go @@ -0,0 +1,19 @@ +package server + +import ( + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/waku-org/go-waku/waku/v2/protocol/filter" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter" + "github.com/waku-org/go-waku/waku/v2/protocol/lightpush" + "github.com/waku-org/go-waku/waku/v2/protocol/relay" + "github.com/waku-org/go-waku/waku/v2/protocol/store" +) + +func IsWakuProtocol(protocol protocol.ID) bool { + return protocol == legacy_filter.FilterID_v20beta1 || + protocol == filter.FilterPushID_v20beta1 || + protocol == filter.FilterSubscribeID_v20beta1 || + protocol == relay.WakuRelayID_v200 || + protocol == lightpush.LightPushID_v20beta1 || + protocol == store.StoreID_v20beta4 +}