github.com/ethersphere/bee/v2@v2.2.0/pkg/api/router.go (about)

     1  // Copyright 2020 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  	"expvar"
     9  	"fmt"
    10  	"net/http"
    11  	"net/http/pprof"
    12  	"strings"
    13  
    14  	"github.com/ethersphere/bee/v2/pkg/jsonhttp"
    15  	"github.com/ethersphere/bee/v2/pkg/log/httpaccess"
    16  	"github.com/ethersphere/bee/v2/pkg/swarm"
    17  	"github.com/gorilla/handlers"
    18  	"github.com/gorilla/mux"
    19  	"github.com/prometheus/client_golang/prometheus/promhttp"
    20  	"resenje.org/web"
    21  )
    22  
    23  const (
    24  	apiVersion = "v1" // Only one api version exists, this should be configurable with more.
    25  	rootPath   = "/" + apiVersion
    26  )
    27  
    28  func (s *Service) MountTechnicalDebug() {
    29  	router := mux.NewRouter()
    30  	router.NotFoundHandler = http.HandlerFunc(jsonhttp.NotFoundHandler)
    31  	s.router = router
    32  
    33  	s.mountTechnicalDebug()
    34  
    35  	s.Handler = web.ChainHandlers(
    36  		httpaccess.NewHTTPAccessLogHandler(s.logger, s.tracer, "api access"),
    37  		handlers.CompressHandler,
    38  		s.corsHandler,
    39  		web.NoCacheHeadersHandler,
    40  		web.FinalHandler(router),
    41  	)
    42  }
    43  
    44  func (s *Service) MountDebug() {
    45  	s.mountBusinessDebug()
    46  
    47  	s.Handler = web.ChainHandlers(
    48  		httpaccess.NewHTTPAccessLogHandler(s.logger, s.tracer, "api access"),
    49  		handlers.CompressHandler,
    50  		s.corsHandler,
    51  		web.NoCacheHeadersHandler,
    52  		web.FinalHandler(s.router),
    53  	)
    54  }
    55  
    56  func (s *Service) MountAPI() {
    57  	if s.router == nil {
    58  		s.router = mux.NewRouter()
    59  		s.router.NotFoundHandler = http.HandlerFunc(jsonhttp.NotFoundHandler)
    60  	}
    61  
    62  	s.mountAPI()
    63  
    64  	compressHandler := func(h http.Handler) http.Handler {
    65  		downloadEndpoints := []string{
    66  			"/bzz",
    67  			"/bytes",
    68  			"/chunks",
    69  			rootPath + "/bzz",
    70  			rootPath + "/bytes",
    71  			rootPath + "/chunks",
    72  		}
    73  
    74  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    75  			// Skip compression for GET requests on download endpoints.
    76  			// This is done in order to preserve Content-Length header in response,
    77  			// because CompressHandler is always removing it.
    78  			if r.Method == http.MethodGet {
    79  				for _, endpoint := range downloadEndpoints {
    80  					if strings.HasPrefix(r.URL.Path, endpoint) {
    81  						h.ServeHTTP(w, r)
    82  						return
    83  					}
    84  				}
    85  			}
    86  
    87  			if r.Method == http.MethodHead {
    88  				h.ServeHTTP(w, r)
    89  				return
    90  			}
    91  
    92  			handlers.CompressHandler(h).ServeHTTP(w, r)
    93  		})
    94  	}
    95  
    96  	s.Handler = web.ChainHandlers(
    97  		httpaccess.NewHTTPAccessLogHandler(s.logger, s.tracer, "api access"),
    98  		compressHandler,
    99  		s.responseCodeMetricsHandler,
   100  		s.pageviewMetricsHandler,
   101  		s.corsHandler,
   102  		web.FinalHandler(s.router),
   103  	)
   104  }
   105  
   106  func (s *Service) mountTechnicalDebug() {
   107  	s.router.Handle("/node", jsonhttp.MethodHandler{
   108  		"GET": http.HandlerFunc(s.nodeGetHandler),
   109  	})
   110  
   111  	s.router.Handle("/addresses", jsonhttp.MethodHandler{
   112  		"GET": http.HandlerFunc(s.addressesHandler),
   113  	})
   114  
   115  	s.router.Handle("/chainstate", jsonhttp.MethodHandler{
   116  		"GET": http.HandlerFunc(s.chainStateHandler),
   117  	})
   118  
   119  	s.router.Handle("/debugstore", jsonhttp.MethodHandler{
   120  		"GET": web.ChainHandlers(
   121  			httpaccess.NewHTTPAccessSuppressLogHandler(),
   122  			web.FinalHandlerFunc(s.debugStorage),
   123  		),
   124  	})
   125  
   126  	s.router.Path("/metrics").Handler(web.ChainHandlers(
   127  		httpaccess.NewHTTPAccessSuppressLogHandler(),
   128  		web.FinalHandler(promhttp.InstrumentMetricHandler(
   129  			s.metricsRegistry,
   130  			promhttp.HandlerFor(s.metricsRegistry, promhttp.HandlerOpts{}),
   131  		)),
   132  	))
   133  
   134  	s.router.Handle("/debug/pprof", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   135  		u := r.URL
   136  		u.Path += "/"
   137  		http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
   138  	}))
   139  	s.router.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
   140  	s.router.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
   141  	s.router.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
   142  	s.router.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
   143  
   144  	s.router.PathPrefix("/debug/pprof/").Handler(http.HandlerFunc(pprof.Index))
   145  	s.router.Handle("/debug/vars", expvar.Handler())
   146  
   147  	s.router.Handle("/loggers", jsonhttp.MethodHandler{
   148  		"GET": web.ChainHandlers(
   149  			httpaccess.NewHTTPAccessSuppressLogHandler(),
   150  			web.FinalHandlerFunc(s.loggerGetHandler),
   151  		),
   152  	})
   153  	s.router.Handle("/loggers/{exp}", jsonhttp.MethodHandler{
   154  		"GET": web.ChainHandlers(
   155  			httpaccess.NewHTTPAccessSuppressLogHandler(),
   156  			web.FinalHandlerFunc(s.loggerGetHandler),
   157  		),
   158  	})
   159  	s.router.Handle("/loggers/{exp}/{verbosity}", jsonhttp.MethodHandler{
   160  		"PUT": web.ChainHandlers(
   161  			httpaccess.NewHTTPAccessSuppressLogHandler(),
   162  			web.FinalHandlerFunc(s.loggerSetVerbosityHandler),
   163  		),
   164  	})
   165  
   166  	s.router.Handle("/readiness", web.ChainHandlers(
   167  		httpaccess.NewHTTPAccessSuppressLogHandler(),
   168  		web.FinalHandlerFunc(s.readinessHandler),
   169  	))
   170  
   171  	s.router.Handle("/health", web.ChainHandlers(
   172  		httpaccess.NewHTTPAccessSuppressLogHandler(),
   173  		web.FinalHandlerFunc(s.healthHandler),
   174  	))
   175  }
   176  
   177  func (s *Service) mountAPI() {
   178  	subdomainRouter := s.router.Host("{subdomain:.*}.swarm.localhost").Subrouter()
   179  
   180  	subdomainRouter.Handle("/{path:.*}", jsonhttp.MethodHandler{
   181  		"GET": web.ChainHandlers(
   182  			web.FinalHandlerFunc(s.subdomainHandler),
   183  		),
   184  	})
   185  
   186  	s.router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
   187  		fmt.Fprintln(w, "Ethereum Swarm Bee")
   188  	})
   189  
   190  	s.router.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
   191  		fmt.Fprintln(w, "User-agent: *\nDisallow: /")
   192  	})
   193  
   194  	// handle is a helper closure which simplifies the router setup.
   195  	handle := func(path string, handler http.Handler) {
   196  		s.router.Handle(path, handler)
   197  		s.router.Handle(rootPath+path, handler)
   198  	}
   199  
   200  	handle("/bytes", jsonhttp.MethodHandler{
   201  		"POST": web.ChainHandlers(
   202  			s.contentLengthMetricMiddleware(),
   203  			s.newTracingHandler("bytes-upload"),
   204  			web.FinalHandlerFunc(s.bytesUploadHandler),
   205  		),
   206  	})
   207  
   208  	handle("/bytes/{address}", jsonhttp.MethodHandler{
   209  		"GET": web.ChainHandlers(
   210  			s.contentLengthMetricMiddleware(),
   211  			s.newTracingHandler("bytes-download"),
   212  			s.actDecryptionHandler(),
   213  			web.FinalHandlerFunc(s.bytesGetHandler),
   214  		),
   215  		"HEAD": web.ChainHandlers(
   216  			s.newTracingHandler("bytes-head"),
   217  			s.actDecryptionHandler(),
   218  			web.FinalHandlerFunc(s.bytesHeadHandler),
   219  		),
   220  	})
   221  
   222  	handle("/chunks", jsonhttp.MethodHandler{
   223  		"POST": web.ChainHandlers(
   224  			jsonhttp.NewMaxBodyBytesHandler(swarm.SocMaxChunkSize),
   225  			web.FinalHandlerFunc(s.chunkUploadHandler),
   226  		),
   227  	})
   228  
   229  	handle("/chunks/stream", web.ChainHandlers(
   230  		s.newTracingHandler("chunks-stream-upload"),
   231  		web.FinalHandlerFunc(s.chunkUploadStreamHandler),
   232  	))
   233  
   234  	handle("/chunks/{address}", jsonhttp.MethodHandler{
   235  		"GET": web.ChainHandlers(
   236  			s.actDecryptionHandler(),
   237  			web.FinalHandlerFunc(s.chunkGetHandler),
   238  		),
   239  		"HEAD": web.ChainHandlers(
   240  			s.actDecryptionHandler(),
   241  			web.FinalHandlerFunc(s.hasChunkHandler),
   242  		),
   243  	})
   244  
   245  	handle("/envelope/{address}", jsonhttp.MethodHandler{
   246  		"POST": http.HandlerFunc(s.envelopePostHandler),
   247  	})
   248  
   249  	handle("/soc/{owner}/{id}", jsonhttp.MethodHandler{
   250  		"POST": web.ChainHandlers(
   251  			jsonhttp.NewMaxBodyBytesHandler(swarm.ChunkWithSpanSize),
   252  			web.FinalHandlerFunc(s.socUploadHandler),
   253  		),
   254  	})
   255  
   256  	handle("/feeds/{owner}/{topic}", jsonhttp.MethodHandler{
   257  		"GET": http.HandlerFunc(s.feedGetHandler),
   258  		"POST": web.ChainHandlers(
   259  			jsonhttp.NewMaxBodyBytesHandler(swarm.ChunkWithSpanSize),
   260  			web.FinalHandlerFunc(s.feedPostHandler),
   261  		),
   262  	})
   263  
   264  	handle("/bzz", jsonhttp.MethodHandler{
   265  		"POST": web.ChainHandlers(
   266  			s.contentLengthMetricMiddleware(),
   267  			s.newTracingHandler("bzz-upload"),
   268  			web.FinalHandlerFunc(s.bzzUploadHandler),
   269  		),
   270  	})
   271  
   272  	handle("/grantee", jsonhttp.MethodHandler{
   273  		"POST": web.ChainHandlers(
   274  			web.FinalHandlerFunc(s.actCreateGranteesHandler),
   275  		),
   276  	})
   277  
   278  	handle("/grantee/{address}", jsonhttp.MethodHandler{
   279  		"GET": web.ChainHandlers(
   280  			web.FinalHandlerFunc(s.actListGranteesHandler),
   281  		),
   282  		"PATCH": web.ChainHandlers(
   283  			web.FinalHandlerFunc(s.actGrantRevokeHandler),
   284  		),
   285  	})
   286  
   287  	handle("/bzz/{address}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   288  		u := r.URL
   289  		u.Path += "/"
   290  		http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
   291  	}))
   292  
   293  	handle("/bzz/{address}/{path:.*}", jsonhttp.MethodHandler{
   294  		"GET": web.ChainHandlers(
   295  			s.contentLengthMetricMiddleware(),
   296  			s.newTracingHandler("bzz-download"),
   297  			s.actDecryptionHandler(),
   298  			web.FinalHandlerFunc(s.bzzDownloadHandler),
   299  		),
   300  		"HEAD": web.ChainHandlers(
   301  			s.actDecryptionHandler(),
   302  			web.FinalHandlerFunc(s.bzzHeadHandler),
   303  		),
   304  	})
   305  
   306  	handle("/pss/send/{topic}/{targets}", web.ChainHandlers(
   307  		web.FinalHandler(jsonhttp.MethodHandler{
   308  			"POST": web.ChainHandlers(
   309  				jsonhttp.NewMaxBodyBytesHandler(swarm.ChunkSize),
   310  				web.FinalHandlerFunc(s.pssPostHandler),
   311  			),
   312  		})),
   313  	)
   314  
   315  	handle("/pss/subscribe/{topic}", web.ChainHandlers(
   316  		web.FinalHandlerFunc(s.pssWsHandler),
   317  	))
   318  
   319  	handle("/tags", web.ChainHandlers(
   320  		web.FinalHandler(jsonhttp.MethodHandler{
   321  			"GET": http.HandlerFunc(s.listTagsHandler),
   322  			"POST": web.ChainHandlers(
   323  				jsonhttp.NewMaxBodyBytesHandler(1024),
   324  				web.FinalHandlerFunc(s.createTagHandler),
   325  			),
   326  		})),
   327  	)
   328  
   329  	handle("/tags/{id}", web.ChainHandlers(
   330  		web.FinalHandler(jsonhttp.MethodHandler{
   331  			"GET":    http.HandlerFunc(s.getTagHandler),
   332  			"DELETE": http.HandlerFunc(s.deleteTagHandler),
   333  			"PATCH": web.ChainHandlers(
   334  				jsonhttp.NewMaxBodyBytesHandler(1024),
   335  				web.FinalHandlerFunc(s.doneSplitHandler),
   336  			),
   337  		})),
   338  	)
   339  
   340  	handle("/pins", web.ChainHandlers(
   341  		web.FinalHandler(jsonhttp.MethodHandler{
   342  			"GET": http.HandlerFunc(s.listPinnedRootHashes),
   343  		})),
   344  	)
   345  
   346  	handle("/pins/check", web.ChainHandlers(
   347  		web.FinalHandler(jsonhttp.MethodHandler{
   348  			"GET": http.HandlerFunc(s.pinIntegrityHandler),
   349  		}),
   350  	))
   351  
   352  	handle("/pins/{reference}", web.ChainHandlers(
   353  		web.FinalHandler(jsonhttp.MethodHandler{
   354  			"GET":    http.HandlerFunc(s.getPinnedRootHash),
   355  			"POST":   http.HandlerFunc(s.pinRootHash),
   356  			"DELETE": http.HandlerFunc(s.unpinRootHash),
   357  		})),
   358  	)
   359  
   360  	handle("/stewardship/{address}", jsonhttp.MethodHandler{
   361  		"GET": web.ChainHandlers(
   362  			web.FinalHandlerFunc(s.stewardshipGetHandler),
   363  		),
   364  		"PUT": web.ChainHandlers(
   365  			web.FinalHandlerFunc(s.stewardshipPutHandler),
   366  		),
   367  	})
   368  
   369  	handle("/readiness", web.ChainHandlers(
   370  		httpaccess.NewHTTPAccessSuppressLogHandler(),
   371  		web.FinalHandlerFunc(s.readinessHandler),
   372  	))
   373  
   374  	handle("/health", web.ChainHandlers(
   375  		httpaccess.NewHTTPAccessSuppressLogHandler(),
   376  		web.FinalHandlerFunc(s.healthHandler),
   377  	))
   378  }
   379  
   380  func (s *Service) mountBusinessDebug() {
   381  	handle := func(path string, handler http.Handler) {
   382  		s.router.Handle(path, handler)
   383  		s.router.Handle(rootPath+path, handler)
   384  	}
   385  
   386  	if s.transaction != nil {
   387  		handle("/transactions", jsonhttp.MethodHandler{
   388  			"GET": http.HandlerFunc(s.transactionListHandler),
   389  		})
   390  		handle("/transactions/{hash}", jsonhttp.MethodHandler{
   391  			"GET":    http.HandlerFunc(s.transactionDetailHandler),
   392  			"POST":   http.HandlerFunc(s.transactionResendHandler),
   393  			"DELETE": http.HandlerFunc(s.transactionCancelHandler),
   394  		})
   395  	}
   396  
   397  	handle("/peers", jsonhttp.MethodHandler{
   398  		"GET": http.HandlerFunc(s.peersHandler),
   399  	})
   400  
   401  	handle("/pingpong/{address}", jsonhttp.MethodHandler{
   402  		"POST": http.HandlerFunc(s.pingpongHandler),
   403  	})
   404  
   405  	handle("/reservestate", jsonhttp.MethodHandler{
   406  		"GET": http.HandlerFunc(s.reserveStateHandler),
   407  	})
   408  
   409  	handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{
   410  		"POST": http.HandlerFunc(s.peerConnectHandler),
   411  	})
   412  
   413  	handle("/blocklist", jsonhttp.MethodHandler{
   414  		"GET": http.HandlerFunc(s.blocklistedPeersHandler),
   415  	})
   416  
   417  	handle("/peers/{address}", jsonhttp.MethodHandler{
   418  		"DELETE": http.HandlerFunc(s.peerDisconnectHandler),
   419  	})
   420  
   421  	//handle("/chunks/{address}", jsonhttp.MethodHandler{
   422  	//	"GET": http.HandlerFunc(s.hasChunkHandler),
   423  	//})
   424  
   425  	handle("/topology", jsonhttp.MethodHandler{
   426  		"GET": http.HandlerFunc(s.topologyHandler),
   427  	})
   428  
   429  	handle("/welcome-message", jsonhttp.MethodHandler{
   430  		"GET": http.HandlerFunc(s.getWelcomeMessageHandler),
   431  		"POST": web.ChainHandlers(
   432  			jsonhttp.NewMaxBodyBytesHandler(welcomeMessageMaxRequestSize),
   433  			web.FinalHandlerFunc(s.setWelcomeMessageHandler),
   434  		),
   435  	})
   436  
   437  	handle("/balances", jsonhttp.MethodHandler{
   438  		"GET": http.HandlerFunc(s.compensatedBalancesHandler),
   439  	})
   440  
   441  	handle("/balances/{peer}", jsonhttp.MethodHandler{
   442  		"GET": http.HandlerFunc(s.compensatedPeerBalanceHandler),
   443  	})
   444  
   445  	handle("/consumed", jsonhttp.MethodHandler{
   446  		"GET": http.HandlerFunc(s.balancesHandler),
   447  	})
   448  
   449  	handle("/consumed/{peer}", jsonhttp.MethodHandler{
   450  		"GET": http.HandlerFunc(s.peerBalanceHandler),
   451  	})
   452  
   453  	handle("/timesettlements", jsonhttp.MethodHandler{
   454  		"GET": http.HandlerFunc(s.settlementsHandlerPseudosettle),
   455  	})
   456  
   457  	if s.swapEnabled {
   458  		handle("/settlements", jsonhttp.MethodHandler{
   459  			"GET": http.HandlerFunc(s.settlementsHandler),
   460  		})
   461  
   462  		handle("/settlements/{peer}", jsonhttp.MethodHandler{
   463  			"GET": http.HandlerFunc(s.peerSettlementsHandler),
   464  		})
   465  
   466  		handle("/chequebook/cheque/{peer}", jsonhttp.MethodHandler{
   467  			"GET": http.HandlerFunc(s.chequebookLastPeerHandler),
   468  		})
   469  
   470  		handle("/chequebook/cheque", jsonhttp.MethodHandler{
   471  			"GET": http.HandlerFunc(s.chequebookAllLastHandler),
   472  		})
   473  
   474  		handle("/chequebook/cashout/{peer}", jsonhttp.MethodHandler{
   475  			"GET": http.HandlerFunc(s.swapCashoutStatusHandler),
   476  			"POST": web.ChainHandlers(
   477  				s.gasConfigMiddleware("swap cashout"),
   478  				web.FinalHandlerFunc(s.swapCashoutHandler),
   479  			),
   480  		})
   481  	}
   482  
   483  	if s.chequebookEnabled {
   484  		handle("/chequebook/balance", jsonhttp.MethodHandler{
   485  			"GET": http.HandlerFunc(s.chequebookBalanceHandler),
   486  		})
   487  
   488  		handle("/chequebook/address", jsonhttp.MethodHandler{
   489  			"GET": http.HandlerFunc(s.chequebookAddressHandler),
   490  		})
   491  
   492  		handle("/chequebook/deposit", jsonhttp.MethodHandler{
   493  			"POST": web.ChainHandlers(
   494  				s.gasConfigMiddleware("chequebook deposit"),
   495  				web.FinalHandlerFunc(s.chequebookDepositHandler),
   496  			),
   497  		})
   498  
   499  		handle("/chequebook/withdraw", jsonhttp.MethodHandler{
   500  			"POST": web.ChainHandlers(
   501  				s.gasConfigMiddleware("chequebook withdraw"),
   502  				web.FinalHandlerFunc(s.chequebookWithdrawHandler),
   503  			),
   504  		})
   505  
   506  		if s.swapEnabled {
   507  			handle("/wallet", jsonhttp.MethodHandler{
   508  				"GET": http.HandlerFunc(s.walletHandler),
   509  			})
   510  			handle("/wallet/withdraw/{coin}", jsonhttp.MethodHandler{
   511  				"POST": web.ChainHandlers(
   512  					s.gasConfigMiddleware("wallet withdraw"),
   513  					web.FinalHandlerFunc(s.walletWithdrawHandler),
   514  				),
   515  			})
   516  		}
   517  	}
   518  
   519  	handle("/stamps", web.ChainHandlers(
   520  		s.postageSyncStatusCheckHandler,
   521  		web.FinalHandler(jsonhttp.MethodHandler{
   522  			"GET": http.HandlerFunc(s.postageGetStampsHandler),
   523  		})),
   524  	)
   525  
   526  	handle("/stamps/{batch_id}", web.ChainHandlers(
   527  		s.postageSyncStatusCheckHandler,
   528  		web.FinalHandler(jsonhttp.MethodHandler{
   529  			"GET": http.HandlerFunc(s.postageGetStampHandler),
   530  		})),
   531  	)
   532  
   533  	handle("/stamps/{batch_id}/buckets", web.ChainHandlers(
   534  		s.postageSyncStatusCheckHandler,
   535  		web.FinalHandler(jsonhttp.MethodHandler{
   536  			"GET": http.HandlerFunc(s.postageGetStampBucketsHandler),
   537  		})),
   538  	)
   539  
   540  	handle("/stamps/{amount}/{depth}", web.ChainHandlers(
   541  		s.postageAccessHandler,
   542  		s.postageSyncStatusCheckHandler,
   543  		s.gasConfigMiddleware("create batch"),
   544  		web.FinalHandler(jsonhttp.MethodHandler{
   545  			"POST": http.HandlerFunc(s.postageCreateHandler),
   546  		})),
   547  	)
   548  
   549  	handle("/stamps/topup/{batch_id}/{amount}", web.ChainHandlers(
   550  		s.postageAccessHandler,
   551  		s.postageSyncStatusCheckHandler,
   552  		s.gasConfigMiddleware("topup batch"),
   553  		web.FinalHandler(jsonhttp.MethodHandler{
   554  			"PATCH": http.HandlerFunc(s.postageTopUpHandler),
   555  		})),
   556  	)
   557  
   558  	handle("/stamps/dilute/{batch_id}/{depth}", web.ChainHandlers(
   559  		s.postageAccessHandler,
   560  		s.postageSyncStatusCheckHandler,
   561  		s.gasConfigMiddleware("dilute batch"),
   562  		web.FinalHandler(jsonhttp.MethodHandler{
   563  			"PATCH": http.HandlerFunc(s.postageDiluteHandler),
   564  		})),
   565  	)
   566  
   567  	handle("/batches", web.ChainHandlers(
   568  		web.FinalHandler(jsonhttp.MethodHandler{
   569  			"GET": http.HandlerFunc(s.postageGetAllBatchesHandler),
   570  		})),
   571  	)
   572  
   573  	handle("/accounting", jsonhttp.MethodHandler{
   574  		"GET": http.HandlerFunc(s.accountingInfoHandler),
   575  	})
   576  
   577  	handle("/readiness", web.ChainHandlers(
   578  		httpaccess.NewHTTPAccessSuppressLogHandler(),
   579  		web.FinalHandlerFunc(s.readinessHandler),
   580  	))
   581  
   582  	handle("/health", web.ChainHandlers(
   583  		httpaccess.NewHTTPAccessSuppressLogHandler(),
   584  		web.FinalHandlerFunc(s.healthHandler),
   585  	))
   586  
   587  	handle("/stake/withdrawable", web.ChainHandlers(
   588  		s.stakingAccessHandler,
   589  		s.gasConfigMiddleware("get or withdraw withdrawable stake"),
   590  		web.FinalHandler(jsonhttp.MethodHandler{
   591  			"GET":    http.HandlerFunc(s.getWithdrawableStakeHandler),
   592  			"DELETE": http.HandlerFunc(s.withdrawStakeHandler),
   593  		})),
   594  	)
   595  
   596  	handle("/stake/{amount}", web.ChainHandlers(
   597  		s.stakingAccessHandler,
   598  		s.gasConfigMiddleware("deposit stake"),
   599  		web.FinalHandler(jsonhttp.MethodHandler{
   600  			"POST": http.HandlerFunc(s.stakingDepositHandler),
   601  		}),
   602  	))
   603  
   604  	handle("/stake", web.ChainHandlers(
   605  		s.stakingAccessHandler,
   606  		s.gasConfigMiddleware("get or migrate stake"),
   607  		web.FinalHandler(jsonhttp.MethodHandler{
   608  			"GET":    http.HandlerFunc(s.getPotentialStake),
   609  			"DELETE": http.HandlerFunc(s.migrateStakeHandler),
   610  		})),
   611  	)
   612  
   613  	handle("/redistributionstate", jsonhttp.MethodHandler{
   614  		"GET": http.HandlerFunc(s.redistributionStatusHandler),
   615  	})
   616  
   617  	handle("/status", jsonhttp.MethodHandler{
   618  		"GET": web.ChainHandlers(
   619  			httpaccess.NewHTTPAccessSuppressLogHandler(),
   620  			web.FinalHandlerFunc(s.statusGetHandler),
   621  		),
   622  	})
   623  
   624  	handle("/status/peers", jsonhttp.MethodHandler{
   625  		"GET": web.ChainHandlers(
   626  			httpaccess.NewHTTPAccessSuppressLogHandler(),
   627  			s.statusAccessHandler,
   628  			web.FinalHandlerFunc(s.statusGetPeersHandler),
   629  		),
   630  	})
   631  
   632  	handle("/rchash/{depth}/{anchor1}/{anchor2}", web.ChainHandlers(
   633  		web.FinalHandler(jsonhttp.MethodHandler{
   634  			"GET": http.HandlerFunc(s.rchash),
   635  		}),
   636  	))
   637  }