From 05de260bffab8c37473f48d4cf6b5347c7e45cb1 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Thu, 9 Aug 2018 19:10:29 +0100 Subject: [PATCH] feat: can start own ipfs node now mfs is in js-ipfs fixes #15 --- README.md | 72 +++++++++++++++++++++++++++++++--------- package.json | 8 ++--- src/create.js | 53 +++++++++++++++++++++++++++++ src/index.js | 16 --------- src/mfs.js | 12 +++---- tests/blob-store-test.js | 13 ++++---- 6 files changed, 126 insertions(+), 48 deletions(-) create mode 100644 src/create.js delete mode 100644 src/index.js diff --git a/README.md b/README.md index 539aae5..5b2f0a9 100644 --- a/README.md +++ b/README.md @@ -23,35 +23,75 @@ npm install ipfs-blob-store ## Usage -`ipfs-blob-store` today requires a running [IPFS daemon](https://github.com/ipfs/go-ipfs/) to talk to over HTTP. This module will be able to be entirely self-contained once [js-ipfs](https://github.com/ipfs/js-ipfs) is complete. - `ipfs-blob-store` uses the [IPFS Files API](#) to create the abstraction of a mutable filesystem over snapshots of Merkle DAGs (per mutation). You'll need to use the Files API directly to get the `/ipfs/Qm...` address of the filesystem root so that other IPFS nodes can retrieve it. +It requires an IPFS node to run - you can either specify a host/port combination to connect to a remote daemon, pass an instance of [`ipfs`](https://www.npmjs.com/package/ipfs) or nothing at all to have the blob store manage it's own IPFS node. + +### Self-managed IPFS node + +```JavaScript +const ipfsBlobStore = require('ipfs-blob-store') + +const store = await ipfsBlobStore() + +store.exists('/my-file.txt', (error, exists) => { + // ... +}) +``` + +### Pre-configured IPFS node + +```JavaScript +const ipfsBlobStore = require('ipfs-blob-store') +const IPFS = require('ipfs') + +const node = new IPFS({ + // some config here +}) + +node.once('ready', () => { + const store = await ipfsBlobStore({ + ipfs: node + }) + + store.exists('/my-file.txt', (error, exists) => { + // ... + }) +}) +``` + +### Remote IPFS daemon + ```JavaScript -var ipfsBlobStore = require('ipfs-blob-store') +const ipfsBlobStore = require('ipfs-blob-store') + +const store = await ipfsBlobStore({ + host: '127.0.0.1', + port: 5001 +}) + +store.exists('/my-file.txt', (error, exists) => { + // ... +}) +``` +### Options + +```JavaScript var options = { + ipfs: null, // an instance of ipfs or ipfs-api port: 5001, // default value host: '127.0.0.1', // default value baseDir: '/', // default value flush: true // default value } -var store = ipfsBlobStore(options) - -var ws = store.createWriteStream({ - key: 'some/path/file.txt' -}) +const store = await ipfsBlobStore(options) +``` -ws.write('hello world\n') -ws.end(function() { - var rs = store.createReadStream({ - key: 'some/path/file.txt' - }) +### API - rs.pipe(process.stdout) -}) -``` +See [abstract-blob-store](https://www.npmjs.com/package/abstract-blob-store) for the blob store API. ## Contribute diff --git a/package.json b/package.json index 5366daf..c504149 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ipfs-blob-store", "version": "1.2.0", "description": "An abstract-blob-store compatible implementation built using IPFS as the storage backend", - "main": "src/index.js", + "main": "src/create.js", "scripts": { "test": "node tests/blob-store-test.js", "lint": "aegir lint" @@ -32,8 +32,6 @@ "devDependencies": { "abstract-blob-store": "^3.3.2", "aegir": "^15.0.0", - "go-ipfs-dep": "~0.4.13", - "ipfsd-ctl": "~0.39.0", "pre-commit": "^1.2.2", "tape": "^4.8.0" }, @@ -43,6 +41,8 @@ ], "dependencies": { "debug": "^3.1.0", - "ipfs-api": "^23.0.0" + "ipfs": "~0.31.4", + "ipfs-api": "^23.0.0", + "promisify-es6": "^1.0.3" } } diff --git a/src/create.js b/src/create.js new file mode 100644 index 0000000..0cf1120 --- /dev/null +++ b/src/create.js @@ -0,0 +1,53 @@ +'use strict' + +const promisify = require('promisify-es6') +const IPFS = require('ipfs') +const remote = require('ipfs-api') +const mfs = require('./mfs') +const log = require('debug')('ipfs:blob-store:create') +const defaultOptions = { + ipfs: null, + flush: true, + baseDir: '/' +} + +module.exports = promisify((opts, callback) => { + if (typeof opts === 'function') { + callback = opts + opts = defaultOptions + } + + const options = Object.assign({}, defaultOptions, opts) + + if (options.ipfs) { + log('Using pre-configured IPFS instance') + return setImmediate(() => callback(null, mfs(options))) + } + + if (options.host && options.port) { + log(`Connecting to remote IPFS at ${options.host}:${options.port}`) + options.ipfs = remote(options.host, options.port) + + return setImmediate(() => callback(null, mfs(options))) + } + + log(`Starting an IPFS instance`) + callback = once(callback) + + options.ipfs = new IPFS() + options.ipfs.once('ready', () => callback(null, mfs(options))) + options.ipfs.once('error', (error) => callback(error)) +}) + +function once (cb) { + let called = false + + return function () { + if (called) { + return + } + + called = true + cb.apply(null, arguments) + } +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index ba0f724..0000000 --- a/src/index.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -const ipfsAPI = require('ipfs-api') -const mfs = require('./mfs') - -module.exports = function (options) { - if (!options) options = {} - - options.host = options.host || '127.0.0.1' - options.port = options.port || 5001 - - var ipfsCtl = ipfsAPI(options.host, options.port) - - options.ipfsCtl = ipfsCtl - return mfs(options) -} diff --git a/src/mfs.js b/src/mfs.js index 39669b3..fe1cc9c 100644 --- a/src/mfs.js +++ b/src/mfs.js @@ -12,8 +12,8 @@ module.exports = function (options) { store.baseDir += '/' } - var ipfsCtl = options.ipfsCtl - store.ipfsCtl = ipfsCtl + var ipfs = options.ipfs + store.ipfs = ipfs if (typeof options.flush === 'boolean' && options.flush === false) { // let it as it is } else { @@ -34,7 +34,7 @@ module.exports = function (options) { size += buffer.length }) - ipfsCtl.files.write(writePath, bufferStream, { + ipfs.files.write(writePath, bufferStream, { create: true, parents: true, flush: options.flush @@ -60,7 +60,7 @@ module.exports = function (options) { const readPath = normalisePath(store.baseDir + opts.key) log(`read ${readPath}`) - const readableStream = ipfsCtl.files.readReadableStream(readPath) + const readableStream = ipfs.files.readReadableStream(readPath) readableStream.on('error', (error) => { if (error.toString().indexOf('does not exist') > -1 || error.toString().indexOf('Not a directory') > -1) { @@ -79,7 +79,7 @@ module.exports = function (options) { const statPath = normalisePath(store.baseDir + opts.key) log(`stat ${statPath}`) - ipfsCtl.files.stat(statPath, {}, (err) => { + ipfs.files.stat(statPath, {}, (err) => { if (err) { if (err.code === 0) { return cb(null, false) @@ -100,7 +100,7 @@ module.exports = function (options) { const rmPath = normalisePath(store.baseDir + opts.key) log(`rm ${rmPath}`) - ipfsCtl.files.rm(rmPath, cb) + ipfs.files.rm(rmPath, cb) } return store diff --git a/tests/blob-store-test.js b/tests/blob-store-test.js index ca76d5e..db1cbb9 100644 --- a/tests/blob-store-test.js +++ b/tests/blob-store-test.js @@ -1,6 +1,6 @@ const test = require('tape') const abstractBlobTests = require('abstract-blob-store/tests') -const ipfsBlobStore = require('../src') +const ipfsBlobStore = require('../') const DaemonFactory = require('ipfsd-ctl') const df = DaemonFactory.create() @@ -21,14 +21,15 @@ df.spawn({ }) var common = { - setup: function (t, cb) { + setup: async function (t, cb) { var options = { baseDir: '/tests/', - port: 13000 + port: 13000, + host: '127.0.0.1' } - var store = ipfsBlobStore(options) + var store = await ipfsBlobStore(options) - store.ipfsCtl.files.mkdir(options.baseDir, { p: true }, (err) => { + store.ipfs.files.mkdir(options.baseDir, { p: true }, (err) => { if (err) { return console.error(err) } @@ -36,7 +37,7 @@ var common = { }) }, teardown: function (t, store, blob, cb) { - store.ipfsCtl.files.rm(store.baseDir, { recursive: true }, (err) => { + store.ipfs.files.rm(store.baseDir, { recursive: true }, (err) => { if (err) { return cb(err) }