diff --git a/.gitignore b/.gitignore index c548c9f1d6..d7223c6b87 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ docs # Logs logs *.log +# npm pack +*.tgz coverage diff --git a/src/cli/commands/daemon.js b/src/cli/commands/daemon.js index 4f4b87e980..570cd56a86 100644 --- a/src/cli/commands/daemon.js +++ b/src/cli/commands/daemon.js @@ -1,6 +1,7 @@ 'use strict' const os = require('os') +const toUri = require('multiaddr-to-uri') const { getRepoPath, print, ipfsPathHelp } = require('../utils') module.exports = { @@ -44,8 +45,8 @@ module.exports = { const repoPath = getRepoPath() // Required inline to reduce startup time - const HttpApi = require('../../http') - const api = new HttpApi({ + const Daemon = require('../../cli/daemon') + const daemon = new Daemon({ silent: argv.silent, repo: process.env.IPFS_PATH, offline: argv.offline, @@ -60,7 +61,16 @@ module.exports = { }) try { - await api.start() + await daemon.start() + daemon._httpApi._apiServers.forEach(apiServer => { + print(`API listening on ${apiServer.info.ma.toString()}`) + }) + daemon._httpApi._gatewayServers.forEach(gatewayServer => { + print(`Gateway (read only) listening on ${gatewayServer.info.ma.toString()}`) + }) + daemon._httpApi._apiServers.forEach(apiServer => { + print(`Web UI available at ${toUri(apiServer.info.ma)}/webui`) + }) } catch (err) { if (err.code === 'ENOENT' && err.message.match(/uninitialized/i)) { print('Error: no initialized ipfs repo found in ' + repoPath) @@ -74,7 +84,7 @@ module.exports = { const cleanup = async () => { print(`Received interrupt signal, shutting down...`) - await api.stop() + await daemon.stop() process.exit(0) } diff --git a/src/cli/daemon.js b/src/cli/daemon.js new file mode 100644 index 0000000000..879432018b --- /dev/null +++ b/src/cli/daemon.js @@ -0,0 +1,96 @@ +'use strict' + +const debug = require('debug') + +const IPFS = require('../core') +const HttpApi = require('../http') +const WStar = require('libp2p-webrtc-star') +const TCP = require('libp2p-tcp') +const MulticastDNS = require('libp2p-mdns') +const WS = require('libp2p-websockets') +const Bootstrap = require('libp2p-bootstrap') +const promisify = require('promisify-es6') + +class Daemon { + constructor (options) { + this._options = options || {} + this._log = debug('ipfs:daemon') + this._log.error = debug('ipfs:daemon:error') + + if (process.env.IPFS_MONITORING) { + // Setup debug metrics collection + const prometheusClient = require('prom-client') + const prometheusGcStats = require('prometheus-gc-stats') + const collectDefaultMetrics = prometheusClient.collectDefaultMetrics + collectDefaultMetrics({ timeout: 5000 }) + prometheusGcStats(prometheusClient.register)() + } + } + + async start () { + this._log('starting') + + const libp2p = { modules: {} } + + // Attempt to use any of the WebRTC versions available globally + let electronWebRTC + let wrtc + try { + electronWebRTC = require('electron-webrtc')() + } catch (err) { + this._log('failed to load optional electron-webrtc dependency') + } + try { + wrtc = require('wrtc') + } catch (err) { + this._log('failed to load optional webrtc dependency') + } + + if (wrtc || electronWebRTC) { + const using = wrtc ? 'wrtc' : 'electron-webrtc' + this._log(`Using ${using} for webrtc support`) + const wstar = new WStar({ wrtc: (wrtc || electronWebRTC) }) + libp2p.modules.transport = [TCP, WS, wstar] + libp2p.modules.peerDiscovery = [MulticastDNS, Bootstrap, wstar.discovery] + } + + // start the daemon + const ipfsOpts = Object.assign({ init: false }, this._options, { start: true, libp2p }) + const ipfs = new IPFS(ipfsOpts) + + await new Promise((resolve, reject) => { + ipfs.once('error', err => { + this._log('error starting core', err) + err.code = 'ENOENT' + reject(err) + }) + ipfs.once('start', resolve) + }) + + this._ipfs = ipfs + + // start HTTP servers (if API or Gateway is enabled in options) + const httpApi = new HttpApi(ipfs, ipfsOpts) + this._httpApi = await httpApi.start() + + // for the CLI to know the where abouts of the API + if (this._httpApi._apiServers.length) { + await promisify(ipfs._repo.apiAddr.set)(this._httpApi._apiServers[0].info.ma) + } + + this._log('started') + return this + } + + async stop () { + this._log('stopping') + await Promise.all([ + this._httpApi && this._httpApi.stop(), + this._ipfs && this._ipfs.stop() + ]) + this._log('stopped') + return this + } +} + +module.exports = Daemon diff --git a/src/http/index.js b/src/http/index.js index 0091378353..b46448ef8a 100644 --- a/src/http/index.js +++ b/src/http/index.js @@ -4,16 +4,8 @@ const Hapi = require('hapi') const Pino = require('hapi-pino') const debug = require('debug') const multiaddr = require('multiaddr') -const promisify = require('promisify-es6') -const toUri = require('multiaddr-to-uri') const toMultiaddr = require('uri-to-multiaddr') -const IPFS = require('../core') -const WStar = require('libp2p-webrtc-star') -const TCP = require('libp2p-tcp') -const MulticastDNS = require('libp2p-mdns') -const WS = require('libp2p-websockets') -const Bootstrap = require('libp2p-bootstrap') const errorHandler = require('./error-handler') const LOG = 'ipfs:http-api' const LOG_ERROR = 'ipfs:http-api:error' @@ -48,7 +40,8 @@ function serverCreator (serverAddrs, createServer, ipfs) { } class HttpApi { - constructor (options) { + constructor (ipfs, options) { + this._ipfs = ipfs this._options = options || {} this._log = debug(LOG) this._log.error = debug(LOG_ERROR) @@ -66,44 +59,7 @@ class HttpApi { async start () { this._log('starting') - const libp2p = { modules: {} } - - // Attempt to use any of the WebRTC versions available globally - let electronWebRTC - let wrtc - try { - electronWebRTC = require('electron-webrtc')() - } catch (err) { - this._log('failed to load optional electron-webrtc dependency') - } - try { - wrtc = require('wrtc') - } catch (err) { - this._log('failed to load optional webrtc dependency') - } - - if (wrtc || electronWebRTC) { - const using = wrtc ? 'wrtc' : 'electron-webrtc' - this._log(`Using ${using} for webrtc support`) - const wstar = new WStar({ wrtc: (wrtc || electronWebRTC) }) - libp2p.modules.transport = [TCP, WS, wstar] - libp2p.modules.peerDiscovery = [MulticastDNS, Bootstrap, wstar.discovery] - } - - // start the daemon - const ipfsOpts = Object.assign({ init: false }, this._options, { start: true, libp2p }) - const ipfs = new IPFS(ipfsOpts) - - await new Promise((resolve, reject) => { - ipfs.once('error', err => { - this._log('error starting core', err) - err.code = 'ENOENT' - reject(err) - }) - ipfs.once('start', resolve) - }) - - this._ipfs = ipfs + const ipfs = this._ipfs const config = await ipfs.config.get() config.Addresses = config.Addresses || {} @@ -111,23 +67,9 @@ class HttpApi { const apiAddrs = config.Addresses.API this._apiServers = await serverCreator(apiAddrs, this._createApiServer, ipfs) - // for the CLI to know the where abouts of the API - if (this._apiServers.length) { - await promisify(ipfs._repo.apiAddr.set)(this._apiServers[0].info.ma) - } - const gatewayAddrs = config.Addresses.Gateway this._gatewayServers = await serverCreator(gatewayAddrs, this._createGatewayServer, ipfs) - this._apiServers.forEach(apiServer => { - ipfs._print('API listening on %s', apiServer.info.ma) - }) - this._gatewayServers.forEach(gatewayServer => { - ipfs._print('Gateway (read only) listening on %s', gatewayServer.info.ma) - }) - this._apiServers.forEach(apiServer => { - ipfs._print('Web UI available at %s', toUri(apiServer.info.ma) + '/webui') - }) this._log('started') return this } @@ -208,8 +150,7 @@ class HttpApi { const stopServers = servers => Promise.all((servers || []).map(s => s.stop())) await Promise.all([ stopServers(this._apiServers), - stopServers(this._gatewayServers), - this._ipfs && this._ipfs.stop() + stopServers(this._gatewayServers) ]) this._log('stopped') return this diff --git a/test/gateway/index.js b/test/gateway/index.js index c5f9bd0be6..f8836700fb 100644 --- a/test/gateway/index.js +++ b/test/gateway/index.js @@ -5,7 +5,7 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const API = require('../../src/http') +const Daemon = require('../../src/cli/daemon') const loadFixture = require('aegir/fixtures') const os = require('os') const path = require('path') @@ -33,7 +33,7 @@ describe('HTTP Gateway', function () { this.timeout(60 * 1000) const repoPath = path.join(os.tmpdir(), '/ipfs-' + hat()) - http.api = new API({ + http.api = new Daemon({ repo: repoPath, init: true, config: { @@ -60,7 +60,7 @@ describe('HTTP Gateway', function () { await http.api.start() - gateway = http.api._gatewayServers[0] + gateway = http.api._httpApi._gatewayServers[0] // QmbQD7EMEL1zeebwBsWEfA3ndgSS6F7S6iTuwuqasPgVRi await http.api._ipfs.add([ diff --git a/test/http-api/inject/bitswap.js b/test/http-api/inject/bitswap.js index 9ac273c947..303831884b 100644 --- a/test/http-api/inject/bitswap.js +++ b/test/http-api/inject/bitswap.js @@ -12,7 +12,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) before(async function () { diff --git a/test/http-api/inject/block.js b/test/http-api/inject/block.js index a121fc315d..e247dc9225 100644 --- a/test/http-api/inject/block.js +++ b/test/http-api/inject/block.js @@ -13,7 +13,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) describe('/block/put', () => { diff --git a/test/http-api/inject/bootstrap.js b/test/http-api/inject/bootstrap.js index 5015b3d40f..5c791f2861 100644 --- a/test/http-api/inject/bootstrap.js +++ b/test/http-api/inject/bootstrap.js @@ -11,7 +11,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] return api.inject({ method: 'GET', url: '/api/v0/bootstrap/add/default' diff --git a/test/http-api/inject/config.js b/test/http-api/inject/config.js index 2388ed4264..f361037569 100644 --- a/test/http-api/inject/config.js +++ b/test/http-api/inject/config.js @@ -17,7 +17,7 @@ module.exports = (http) => { before(() => { updatedConfig = () => JSON.parse(fs.readFileSync(configPath, 'utf8')) - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) after(() => { diff --git a/test/http-api/inject/dag.js b/test/http-api/inject/dag.js index 62eca02574..605cb076dd 100644 --- a/test/http-api/inject/dag.js +++ b/test/http-api/inject/dag.js @@ -33,7 +33,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) describe('/dag/get', () => { diff --git a/test/http-api/inject/dht.js b/test/http-api/inject/dht.js index 04cb501227..455a4bb8b1 100644 --- a/test/http-api/inject/dht.js +++ b/test/http-api/inject/dht.js @@ -12,7 +12,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) describe('/findpeer', () => { diff --git a/test/http-api/inject/dns.js b/test/http-api/inject/dns.js index 010797f57a..18f0ec5925 100644 --- a/test/http-api/inject/dns.js +++ b/test/http-api/inject/dns.js @@ -8,7 +8,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) it('resolve ipfs.io dns', async () => { diff --git a/test/http-api/inject/files.js b/test/http-api/inject/files.js index 8ff819dfce..aaf4f10dd5 100644 --- a/test/http-api/inject/files.js +++ b/test/http-api/inject/files.js @@ -13,7 +13,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) describe('/add', () => { diff --git a/test/http-api/inject/id.js b/test/http-api/inject/id.js index ff8922f312..f3f7160375 100644 --- a/test/http-api/inject/id.js +++ b/test/http-api/inject/id.js @@ -8,7 +8,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) it('get the id', async () => { diff --git a/test/http-api/inject/name.js b/test/http-api/inject/name.js index 699ed0a07b..1e723c3749 100644 --- a/test/http-api/inject/name.js +++ b/test/http-api/inject/name.js @@ -16,7 +16,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) it('should publish a record', async function () { diff --git a/test/http-api/inject/object.js b/test/http-api/inject/object.js index 952cf1ab7d..662474e0b0 100644 --- a/test/http-api/inject/object.js +++ b/test/http-api/inject/object.js @@ -17,7 +17,7 @@ module.exports = (http) => { let api before('api', () => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) describe('/new', () => { diff --git a/test/http-api/inject/pin.js b/test/http-api/inject/pin.js index db1ec00b6c..ceb11ae400 100644 --- a/test/http-api/inject/pin.js +++ b/test/http-api/inject/pin.js @@ -37,7 +37,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) describe('rm', () => { diff --git a/test/http-api/inject/ping.js b/test/http-api/inject/ping.js index 3ed712c483..ef8cd3bee4 100644 --- a/test/http-api/inject/ping.js +++ b/test/http-api/inject/ping.js @@ -13,7 +13,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) it('returns 400 if both n and count are provided', async () => { diff --git a/test/http-api/inject/pubsub.js b/test/http-api/inject/pubsub.js index 3e88d40434..86cec4f078 100644 --- a/test/http-api/inject/pubsub.js +++ b/test/http-api/inject/pubsub.js @@ -16,7 +16,7 @@ module.exports = (http) => { const topicNotSubscribed = 'somethingRandom' before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) describe('/sub', () => { diff --git a/test/http-api/inject/resolve.js b/test/http-api/inject/resolve.js index 1d70206e8a..21baf6ba71 100644 --- a/test/http-api/inject/resolve.js +++ b/test/http-api/inject/resolve.js @@ -12,7 +12,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) it('should resolve a path and return a base2 encoded CID', async () => { diff --git a/test/http-api/inject/version.js b/test/http-api/inject/version.js index 9a83821ebe..d1cb6a1577 100644 --- a/test/http-api/inject/version.js +++ b/test/http-api/inject/version.js @@ -9,7 +9,7 @@ module.exports = (http) => { let api before(() => { - api = http.api._apiServers[0] + api = http.api._httpApi._apiServers[0] }) it('get the version', async () => { diff --git a/test/http-api/routes.js b/test/http-api/routes.js index 0fa6fdf7df..d79572dc18 100644 --- a/test/http-api/routes.js +++ b/test/http-api/routes.js @@ -6,7 +6,7 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') chai.use(dirtyChai) const hat = require('hat') -const API = require('../../src/http/index') +const Daemon = require('../../src/cli/daemon') const promisify = require('promisify-es6') const ncp = promisify(require('ncp').ncp) const path = require('path') @@ -22,7 +22,7 @@ describe('HTTP API', () => { let http = {} const startHttpAPI = async (config) => { - http.api = new API({ + http.api = new Daemon({ repo: repoTests, pass: hat(), config,