diff --git a/app/cmd/rpc/query.go b/app/cmd/rpc/query.go index e6f06bdb0..a26c2d775 100644 --- a/app/cmd/rpc/query.go +++ b/app/cmd/rpc/query.go @@ -27,6 +27,16 @@ func Version(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { WriteResponse(w, APIVersion, r.URL.Path, r.Host) } +func Health(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + res := app.PCA.QueryHealth(APIVersion) + j, er := json.Marshal(res) + if er != nil { + WriteErrorResponse(w, 400, er.Error()) + return + } + WriteJSONResponse(w, string(j), r.URL.Path, r.Host) +} + func LocalNodes(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { value := r.URL.Query().Get("authtoken") if value != app.AuthToken.Value { diff --git a/app/cmd/rpc/server.go b/app/cmd/rpc/server.go index 2c1493272..c63ae3d66 100644 --- a/app/cmd/rpc/server.go +++ b/app/cmd/rpc/server.go @@ -87,6 +87,7 @@ type Routes []Route func GetRoutes() Routes { routes := Routes{ Route{Name: "AppVersion", Method: "GET", Path: "/v1", HandlerFunc: Version}, + Route{Name: "Health", Method: "GET", Path: "/v1/health", HandlerFunc: Health}, Route{Name: "Challenge", Method: "POST", Path: "/v1/client/challenge", HandlerFunc: Challenge}, Route{Name: "ChallengeCORS", Method: "OPTIONS", Path: "/v1/client/challenge", HandlerFunc: Challenge}, Route{Name: "HandleDispatch", Method: "POST", Path: "/v1/client/dispatch", HandlerFunc: Dispatch}, diff --git a/app/query.go b/app/query.go index 7d02af299..e40d7f1c4 100644 --- a/app/query.go +++ b/app/query.go @@ -29,6 +29,37 @@ const ( txHeightQuery = "tx.height=%d" ) +type HealthResponse struct { + Version string `json:"version"` + IsStarting bool `json:"is_starting"` + IsCatchingUp bool `json:"is_catching_up"` + Height int64 `json:"height"` +} + +func (app PocketCoreApp) QueryHealth(version string) (res HealthResponse) { + res = HealthResponse{ + Version: version, + IsStarting: true, + IsCatchingUp: false, + Height: 0, + } + res.Height = app.LastBlockHeight() + _, err := app.NewContext(res.Height) + + if err != nil { + return + } + + status, sErr := app.pocketKeeper.TmNode.ConsensusReactorStatus() + if sErr != nil { + return + } + + res.IsStarting = false + res.IsCatchingUp = status.IsCatchingUp + return +} + // zero for height = latest func (app PocketCoreApp) QueryBlock(height *int64) (blockJSON []byte, err error) { tmClient := app.GetClient() diff --git a/doc/specs/rpc-spec.yaml b/doc/specs/rpc-spec.yaml index 286dae637..8f95eacc4 100644 --- a/doc/specs/rpc-spec.yaml +++ b/doc/specs/rpc-spec.yaml @@ -17,6 +17,8 @@ servers: tags: - name: version description: Version of the Pocket API + - name: health + description: Health of the Pocket Node - name: client description: Dispatch and relay services - name: query @@ -35,6 +37,18 @@ paths: schema: type: string example: 0.0.1 + /health: + get: + tags: + - health + summary: Get current node version, height, starting and running state + responses: + '200': + description: Health status of the node + content: + application/json: + schema: + $ref: '#/components/schemas/Health' /client/dispatch: post: tags: @@ -1464,6 +1478,17 @@ components: properties: address: type: string + Health: + type: object + properties: + version: + type: string + height: + type: integer + is_starting: + type: boolean + is_catching_up: + type: boolean Chain: type: object properties: