diff --git a/package.json b/package.json index 2e7e8a40..9a97d23c 100644 --- a/package.json +++ b/package.json @@ -56,12 +56,13 @@ "dependencies": { "async": "^2.6.0", "base32.js": "~0.1.0", + "big.js": "^5.0.3", "cids": "~0.5.2", - "interface-datastore": "~0.4.2", "datastore-core": "~0.4.0", "datastore-fs": "~0.4.2", "datastore-level": "~0.7.0", "debug": "^3.1.0", + "interface-datastore": "~0.4.2", "ipfs-block": "~0.6.1", "level-js": "timkuijsten/level.js#idbunwrapper", "leveldown": "^1.7.2", @@ -69,7 +70,8 @@ "lodash.get": "^4.4.2", "lodash.has": "^4.5.2", "lodash.set": "^4.3.2", - "multiaddr": "^3.0.1" + "multiaddr": "^3.0.1", + "pull-stream": "^3.6.1" }, "license": "MIT", "contributors": [ diff --git a/src/blockstore.js b/src/blockstore.js index a07644dd..1fb5a58c 100644 --- a/src/blockstore.js +++ b/src/blockstore.js @@ -8,6 +8,7 @@ const Block = require('ipfs-block') const setImmediate = require('async/setImmediate') const reject = require('async/reject') const CID = require('cids') +const pull = require('pull-stream') /** * Transform a raw buffer to a base32 encoded key. @@ -49,6 +50,21 @@ function maybeWithSharding (filestore, options, callback) { function createBaseStore (store) { return { + /** + * Query the store. + * + * @param {object} query + * @param {function(Error, Array)} callback + * @return {void} + */ + query (query, callback) { + pull( + store.query(query), + pull.collect((err, list) => { + callback(err, list) + }) + ) + }, /** * Get a single block by CID. * diff --git a/src/index.js b/src/index.js index 8c5bcfcc..a4bb65ee 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,8 @@ const each = require('async/each') const assert = require('assert') const path = require('path') const debug = require('debug') +const Big = require('big.js') +const pull = require('pull-stream') const backends = require('./backends') const version = require('./version') @@ -17,6 +19,8 @@ const defaultOptions = require('./default-options') const log = debug('repo') +const noLimit = Number.MAX_SAFE_INTEGER + const lockers = { memory: require('./lock-memory'), fs: require('./lock') @@ -200,6 +204,72 @@ class IpfsRepo { exists (callback) { this.version.exists(callback) } + + /** + * Get repo status. + * + * @param {function(Error, Object)} callback + * @return {void} + */ + stat (callback) { + waterfall([ + (cb) => cb(null, { + repoPath: this.path, + repoSize: new Big(0), + storageMax: new Big(noLimit) + }), + (stats, cb) => this.version.get((err, version) => { + stats.version = version + cb(err, stats) + }), + (stats, cb) => this.blocks.query({}, (err, list) => { + list = list || [] + stats.numObjects = new Big(list.length) + + list.forEach(block => { + stats.repoSize = stats.repoSize + .plus(block.value.byteLength) + .plus(block.key._buf.byteLength) + }) + + cb(err, stats) + }), + (stats, cb) => getSize(this.datastore, (err, size) => { + stats.repoSize = stats.repoSize.plus(size) + cb(err, stats) + }), + (stats, cb) => getSize(this.keys, (err, size) => { + stats.repoSize = stats.repoSize.plus(size) + cb(err, stats) + }), + (stats, cb) => this.config.get('Datastore.StorageMax', (err, max) => { + if (!err) { + stats.storageMax = new Big(max) + } + + cb(null, stats) + }) + ], callback) + } +} + +function getSize (queryFn, callback) { + pull( + queryFn.query({}), + pull.collect((err, list) => { + if (err) return callback(err, 0) + + let val = new Big(0) + + list.forEach(block => { + val = val + .plus(block.value.byteLength) + .plus(block.key._buf.byteLength) + }) + + callback(null, val) + }) + ) } module.exports = IpfsRepo diff --git a/test/repo-test.js b/test/repo-test.js index 8d7c2d0f..33c26852 100644 --- a/test/repo-test.js +++ b/test/repo-test.js @@ -99,4 +99,19 @@ module.exports = (repo) => { }) }) }) + + describe('stat', () => { + it('get stats', (done) => { + repo.stat((err, stats) => { + expect(err).to.not.exist() + expect(stats).to.exist() + expect(stats).to.have.property('numObjects') + expect(stats).to.have.property('version') + expect(stats).to.have.property('repoPath') + expect(stats).to.have.property('repoSize') + expect(stats).to.have.property('storageMax') + done() + }) + }) + }) }