diff --git a/codex/codex.nim b/codex/codex.nim index 4fc0f922..2f5cf860 100644 --- a/codex/codex.nim +++ b/codex/codex.nim @@ -266,7 +266,7 @@ proc new*( erasure = Erasure.new(store, leoEncoderProvider, leoDecoderProvider) codexNode = CodexNodeRef.new(switch, store, engine, erasure, discovery) restServer = RestServerRef.new( - codexNode.initRestApi(config), + codexNode.initRestApi(config, repoStore), initTAddress(config.apiBindAddress , config.apiPort), bufferSize = (1024 * 64), maxRequestBodySize = int.high) diff --git a/codex/rest/api.nim b/codex/rest/api.nim index deea019b..2f1b192c 100644 --- a/codex/rest/api.nim +++ b/codex/rest/api.nim @@ -34,7 +34,7 @@ import ../conf import ../contracts import ../manifest import ../streams/asyncstreamwrapper -import ../stores/blockstore +import ../stores import ./coders import ./json @@ -106,7 +106,7 @@ proc retrieveCid( if not stream.isNil: await stream.close() -proc initDataApi(node: CodexNodeRef, router: var RestRouter) = +proc initDataApi(node: CodexNodeRef, repoStore: RepoStore, router: var RestRouter) = router.rawApi( MethodPost, "/api/codex/v1/data") do ( @@ -183,6 +183,17 @@ proc initDataApi(node: CodexNodeRef, router: var RestRouter) = await node.retrieveCid(cid.get(), local = false, resp=resp) + router.api( + MethodGet, + "/api/codex/v1/space") do () -> RestApiResponse: + let json = % RestRepoStore( + totalBlocks: repoStore.totalBlocks, + quotaMaxBytes: repoStore.quotaMaxBytes, + quotaUsedBytes: repoStore.quotaUsedBytes, + quotaReservedBytes: repoStore.quotaReservedBytes + ) + return RestApiResponse.response($json, contentType="application/json") + proc initSalesApi(node: CodexNodeRef, router: var RestRouter) = router.api( MethodGet, @@ -456,10 +467,10 @@ proc initDebugApi(node: CodexNodeRef, conf: CodexConf, router: var RestRouter) = trace "Excepting processing request", exc = exc.msg return RestApiResponse.error(Http500) -proc initRestApi*(node: CodexNodeRef, conf: CodexConf): RestRouter = +proc initRestApi*(node: CodexNodeRef, conf: CodexConf, repoStore: RepoStore): RestRouter = var router = RestRouter.init(validate) - initDataApi(node, router) + initDataApi(node, repoStore, router) initSalesApi(node, router) initPurchasingApi(node, router) initDebugApi(node, conf, router) diff --git a/codex/rest/json.nim b/codex/rest/json.nim index c7892aa7..078a0124 100644 --- a/codex/rest/json.nim +++ b/codex/rest/json.nim @@ -60,6 +60,12 @@ type RestNodeId* = object id*: NodeId + RestRepoStore* = object + totalBlocks* {.serialize.}: uint + quotaMaxBytes* {.serialize.}: uint + quotaUsedBytes* {.serialize.}: uint + quotaReservedBytes* {.serialize.}: uint + proc init*(_: type RestContent, cid: Cid, manifest: Manifest): RestContent = RestContent( cid: cid, diff --git a/openapi.yaml b/openapi.yaml index e3eeaa36..b3ac2df5 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -245,6 +245,22 @@ components: type: boolean description: "Indicates if content is protected by erasure-coding" + Space: + type: object + properties: + totalBlocks: + type: number + description: "Number of blocks stored by the node" + quotaMaxBytes: + type: number + description: "Maximum storage space used by the node" + quotaUsedBytes: + type: number + description: "Amount of storage space currently in use" + quotaReservedBytes: + type: number + description: "Amount of storage space reserved" + servers: - url: "http://localhost:8080/api/codex/v1" @@ -371,8 +387,7 @@ paths: required: true schema: $ref: "#/components/schemas/Cid" - description: File to be downloaded. - + description: "File to be downloaded." responses: "200": description: Retrieved content specified by CID @@ -388,6 +403,22 @@ paths: "500": description: Well it was bad-bad + "/space": + get: + summary: "Gets a summary of the storage space allocation of the node." + tags: [ Data ] + operationId: space + responses: + "200": + description: "Summary of storage allocation" + content: + application/json: + schema: + $ref: "#/components/schemas/Space" + + "500": + description: "It's not working as planned" + "/sales/slots": get: summary: "Returns active slots" diff --git a/tests/integration/codexclient.nim b/tests/integration/codexclient.nim index 285d365f..dbddc204 100644 --- a/tests/integration/codexclient.nim +++ b/tests/integration/codexclient.nim @@ -56,6 +56,16 @@ proc list*(client: CodexClient): ?!seq[RestContent] = let json = ? parseJson(response.body).catch seq[RestContent].fromJson(json) +proc space*(client: CodexClient): ?!RestRepoStore = + let url = client.baseurl & "/space" + let response = client.http.get(url) + + if response.status != "200 OK": + return failure(response.status) + + let json = ? parseJson(response.body).catch + RestRepoStore.fromJson(json) + proc requestStorageRaw*( client: CodexClient, cid: Cid, diff --git a/tests/integration/testIntegration.nim b/tests/integration/testIntegration.nim index 8a1d7108..999182e0 100644 --- a/tests/integration/testIntegration.nim +++ b/tests/integration/testIntegration.nim @@ -44,6 +44,16 @@ twonodessuite "Integration tests", debug1 = false, debug2 = false: let cid2 = client1.upload("some other contents").get check cid1 != cid2 + test "node shows used and available space": + discard client1.upload("some file contents").get + discard client1.postAvailability(size=12.u256, duration=2.u256, minPrice=3.u256, maxCollateral=4.u256).get + let space = client1.space().tryGet() + check: + space.totalBlocks == 2.uint + space.quotaMaxBytes == 8589934592.uint + space.quotaUsedBytes == 65518.uint + space.quotaReservedBytes == 12.uint + test "node allows local file downloads": let content1 = "some file contents" let content2 = "some other contents"