From e030a366283f980464de986f5bd092fe2eb3a734 Mon Sep 17 00:00:00 2001 From: Oli Evans Date: Tue, 28 Nov 2017 12:37:40 +0000 Subject: [PATCH 1/4] Factor out creation of ipfs node --- add-on/src/lib/ipfs-client/embedded.js | 48 ++++++++++++++++++++++++++ add-on/src/lib/ipfs-client/external.js | 11 ++++++ add-on/src/lib/ipfs-client/index.js | 14 ++++++++ add-on/src/lib/ipfs-companion.js | 16 ++++----- add-on/src/lib/state.js | 1 + package.json | 6 ++-- 6 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 add-on/src/lib/ipfs-client/embedded.js create mode 100644 add-on/src/lib/ipfs-client/external.js create mode 100644 add-on/src/lib/ipfs-client/index.js diff --git a/add-on/src/lib/ipfs-client/embedded.js b/add-on/src/lib/ipfs-client/embedded.js new file mode 100644 index 000000000..fbdbc4abe --- /dev/null +++ b/add-on/src/lib/ipfs-client/embedded.js @@ -0,0 +1,48 @@ +const Ipfs = require('ipfs') +const IpfsApi = require('ipfs-api') + +let node = null + +exports.init = function init () { + console.log('[ipfs-companion] Embedded ipfs init') + + const ipfsJs = new Ipfs({ + config: { + Addresses: { + Swarm: [] + } + } + }) + + node = createApiAdaptor(ipfsJs) + + if (node.isOnline()) { + return Promise.resolve(node) + } + + return new Promise((resolve, reject) => { + // TODO: replace error listener after a 'ready' event. + node.once('error', (err) => reject(err)) + node.once('ready', () => resolve(node)) + }) +} + +// Create a minimal ipfsApi adaptor, so we can use an ipfsJs node in place of an ipfsApi. +function createApiAdaptor (ipfs) { + const ipfsApiLite = new Proxy(ipfs, { + get: (ipfs, prop) => { + if (prop === 'Buffer') { + return IpfsApi.Buffer + } + return ipfs[prop] + } + }) + return ipfsApiLite +} + +exports.destroy = async function () { + if (!node) return + + await node.stop() + node = null +} diff --git a/add-on/src/lib/ipfs-client/external.js b/add-on/src/lib/ipfs-client/external.js new file mode 100644 index 000000000..f18cb9de1 --- /dev/null +++ b/add-on/src/lib/ipfs-client/external.js @@ -0,0 +1,11 @@ +const IpfsApi = require('ipfs-api') + +exports.init = async function (opts) { + const url = new window.URL(opts.ipfsApiUrl) + const api = IpfsApi({host: url.hostname, port: url.port, procotol: url.protocol}) + return api +} + +exports.destroy = async function () { + return Promise.resolve() +} diff --git a/add-on/src/lib/ipfs-client/index.js b/add-on/src/lib/ipfs-client/index.js new file mode 100644 index 000000000..b1050a57c --- /dev/null +++ b/add-on/src/lib/ipfs-client/index.js @@ -0,0 +1,14 @@ +const embedded = require('./embedded') +const external = require('./external') + +let client = null + +exports.initIpfsClient = async function (opts) { + if (client) client.destroy() + if (opts.type === 'external') { + client = await external.init() + } else { + client = await embedded.init() + } + return client +} diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 0933619a4..dfebafd2d 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -5,10 +5,10 @@ const browser = require('webextension-polyfill') const { optionDefaults, storeMissingOptions } = require('./options') const { initState } = require('./state') const IsIpfs = require('is-ipfs') -const IpfsApi = require('ipfs-api') const { createIpfsPathValidator, safeIpfsPath, urlAtPublicGw } = require('./ipfs-path') const createDnsLink = require('./dns-link') const { createRequestModifier } = require('./ipfs-request') +const { initIpfsClient } = require('./ipfs-client') // INIT // =================================================================== @@ -23,8 +23,13 @@ module.exports = async function init () { try { const options = await browser.storage.local.get(optionDefaults) state = window.state = initState(options) - ipfs = window.ipfs = initIpfsApi(options.ipfsApiUrl) + + ipfs = window.ipfs = await initIpfsClient(state) + console.log('[ipfs-companion] ipfs init complete', ipfs) + + // Check for ipfs dns txt records dnsLink = createDnsLink(getState) + // is it an ipfs path? ipfsPathValidator = createIpfsPathValidator(getState, dnsLink) modifyRequest = createRequestModifier(getState, dnsLink, ipfsPathValidator) registerListeners() @@ -50,11 +55,6 @@ function getState () { return state } -function initIpfsApi (ipfsApiUrl) { - const url = new URL(ipfsApiUrl) - return IpfsApi({host: url.hostname, port: url.port, procotol: url.protocol}) -} - function registerListeners () { browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ['']}, ['blocking', 'requestHeaders']) browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ['']}, ['blocking']) @@ -599,7 +599,7 @@ function onStorageChange (changes, area) { if (key === 'ipfsApiUrl') { state.apiURL = new URL(change.newValue) state.apiURLString = state.apiURL.toString() - ipfs = window.ipfs = initIpfsApi(state.apiURLString) + ipfs = window.ipfs = initIpfsClient(state) apiStatusUpdate() } else if (key === 'ipfsApiPollMs') { setApiStatusUpdateInterval(change.newValue) diff --git a/add-on/src/lib/state.js b/add-on/src/lib/state.js index f14a3d28d..62241b961 100644 --- a/add-on/src/lib/state.js +++ b/add-on/src/lib/state.js @@ -5,6 +5,7 @@ function initState (options) { const state = {} // we store the most used values in optimized form // to minimize performance impact on overall browsing experience + state.ipfsNodeType = 'external' state.pubGwURL = new URL(options.publicGatewayUrl) state.pubGwURLString = state.pubGwURL.toString() state.redirect = options.useCustomGateway diff --git a/package.json b/package.json index bcf109488..9a44fde6e 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,11 @@ "build:copy": "run-s build:copy:*", "build:copy:src": "shx mkdir -p add-on/dist && shx cp -R add-on/src/* add-on/dist", "build:copy:wx-polyfill-lib": "shx cp node_modules/webextension-polyfill/dist/browser-polyfill.min.js add-on/dist/contentScripts/browser-polyfill.min.js", - "build:js": "browserify -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js ] -o add-on/dist/ipfs-companion-common.js", + "build:js": "browserify -p prundupify -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js ] -o add-on/dist/ipfs-companion-common.js", "build:minimize-dist": "shx rm -rf add-on/dist/lib", "build:bundle-extension": "web-ext build -s add-on/ -i src/ -a build/", "watch": "run-p watch:*", - "watch:js": "watchify add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js ] -o add-on/dist/ipfs-companion-common.js -v", + "watch:js": "watchify -p prundupify -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js ] -o add-on/dist/ipfs-companion-common.js -v", "test": "run-s test:*", "test:functional": "mocha test/functional/**/*.test.js", "lint": "run-s lint:*", @@ -68,9 +68,11 @@ }, "dependencies": { "choo": "6.6.0", + "ipfs": "^0.26.0", "ipfs-api": "17.1.3", "is-ipfs": "0.3.2", "lru_map": "0.3.3", + "prundupify": "^1.0.0", "webextension-polyfill": "0.1.2" } } From 7913c2eff45bbd1b41272737cf9340810fec8290 Mon Sep 17 00:00:00 2001 From: Oli Evans Date: Tue, 28 Nov 2017 13:07:38 +0000 Subject: [PATCH 2/4] Pass opts through to ipfs clients --- add-on/src/lib/ipfs-client/embedded.js | 2 ++ add-on/src/lib/ipfs-client/external.js | 11 ++++++----- add-on/src/lib/ipfs-client/index.js | 14 ++++++++++---- add-on/src/lib/ipfs-companion.js | 8 ++++---- add-on/src/lib/options.js | 1 + add-on/src/lib/state.js | 2 +- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/add-on/src/lib/ipfs-client/embedded.js b/add-on/src/lib/ipfs-client/embedded.js index fbdbc4abe..b640fb0b3 100644 --- a/add-on/src/lib/ipfs-client/embedded.js +++ b/add-on/src/lib/ipfs-client/embedded.js @@ -1,3 +1,5 @@ +'use strict' + const Ipfs = require('ipfs') const IpfsApi = require('ipfs-api') diff --git a/add-on/src/lib/ipfs-client/external.js b/add-on/src/lib/ipfs-client/external.js index f18cb9de1..aa13e84a7 100644 --- a/add-on/src/lib/ipfs-client/external.js +++ b/add-on/src/lib/ipfs-client/external.js @@ -1,11 +1,12 @@ +'use strict' +/* eslint-env browser */ + const IpfsApi = require('ipfs-api') exports.init = async function (opts) { - const url = new window.URL(opts.ipfsApiUrl) + console.log('[ipfs-companion] External ipfs init') + + const url = new URL(opts.ipfsApiUrl) const api = IpfsApi({host: url.hostname, port: url.port, procotol: url.protocol}) return api } - -exports.destroy = async function () { - return Promise.resolve() -} diff --git a/add-on/src/lib/ipfs-client/index.js b/add-on/src/lib/ipfs-client/index.js index b1050a57c..1e4667eea 100644 --- a/add-on/src/lib/ipfs-client/index.js +++ b/add-on/src/lib/ipfs-client/index.js @@ -1,14 +1,20 @@ +'use strict' + const embedded = require('./embedded') const external = require('./external') let client = null exports.initIpfsClient = async function (opts) { - if (client) client.destroy() - if (opts.type === 'external') { - client = await external.init() + if (client && client.destroy) { + await client.destroy() + } + + if (opts.ipfsNodeType === 'embedded') { + client = await embedded.init(opts) } else { - client = await embedded.init() + client = await external.init(opts) } + return client } diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index dfebafd2d..4161bc222 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -24,8 +24,8 @@ module.exports = async function init () { const options = await browser.storage.local.get(optionDefaults) state = window.state = initState(options) - ipfs = window.ipfs = await initIpfsClient(state) - console.log('[ipfs-companion] ipfs init complete', ipfs) + ipfs = window.ipfs = await initIpfsClient(options) + console.log('[ipfs-companion] ipfs init complete') // Check for ipfs dns txt records dnsLink = createDnsLink(getState) @@ -590,7 +590,7 @@ function updateAutomaticModeRedirectState (oldPeerCount, newPeerCount) { } } -function onStorageChange (changes, area) { +async function onStorageChange (changes, area) { for (let key in changes) { let change = changes[key] if (change.oldValue !== change.newValue) { @@ -599,7 +599,7 @@ function onStorageChange (changes, area) { if (key === 'ipfsApiUrl') { state.apiURL = new URL(change.newValue) state.apiURLString = state.apiURL.toString() - ipfs = window.ipfs = initIpfsClient(state) + ipfs = window.ipfs = await initIpfsClient(state) apiStatusUpdate() } else if (key === 'ipfsApiPollMs') { setApiStatusUpdateInterval(change.newValue) diff --git a/add-on/src/lib/options.js b/add-on/src/lib/options.js index 1ec63772e..91e711e76 100644 --- a/add-on/src/lib/options.js +++ b/add-on/src/lib/options.js @@ -1,6 +1,7 @@ 'use strict' const optionDefaults = Object.freeze({ + ipfsNodeType: 'external', publicGatewayUrl: 'https://ipfs.io', useCustomGateway: true, automaticMode: true, diff --git a/add-on/src/lib/state.js b/add-on/src/lib/state.js index 62241b961..b802c644d 100644 --- a/add-on/src/lib/state.js +++ b/add-on/src/lib/state.js @@ -5,7 +5,7 @@ function initState (options) { const state = {} // we store the most used values in optimized form // to minimize performance impact on overall browsing experience - state.ipfsNodeType = 'external' + state.ipfsNodeType = options.ipfsNodeType state.pubGwURL = new URL(options.publicGatewayUrl) state.pubGwURLString = state.pubGwURL.toString() state.redirect = options.useCustomGateway From 4f2eba9f479caae68efb72431618539b50f61f8f Mon Sep 17 00:00:00 2001 From: Oli Evans Date: Tue, 28 Nov 2017 15:44:19 +0000 Subject: [PATCH 3/4] Use global Buffer and ipfs.files --- add-on/src/lib/ipfs-client/embedded.js | 18 +----------------- add-on/src/lib/ipfs-companion.js | 2 +- add-on/src/popup/quick-upload.js | 2 +- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/add-on/src/lib/ipfs-client/embedded.js b/add-on/src/lib/ipfs-client/embedded.js index b640fb0b3..8e56036f2 100644 --- a/add-on/src/lib/ipfs-client/embedded.js +++ b/add-on/src/lib/ipfs-client/embedded.js @@ -1,14 +1,13 @@ 'use strict' const Ipfs = require('ipfs') -const IpfsApi = require('ipfs-api') let node = null exports.init = function init () { console.log('[ipfs-companion] Embedded ipfs init') - const ipfsJs = new Ipfs({ + node = new Ipfs({ config: { Addresses: { Swarm: [] @@ -16,8 +15,6 @@ exports.init = function init () { } }) - node = createApiAdaptor(ipfsJs) - if (node.isOnline()) { return Promise.resolve(node) } @@ -29,19 +26,6 @@ exports.init = function init () { }) } -// Create a minimal ipfsApi adaptor, so we can use an ipfsJs node in place of an ipfsApi. -function createApiAdaptor (ipfs) { - const ipfsApiLite = new Proxy(ipfs, { - get: (ipfs, prop) => { - if (prop === 'Buffer') { - return IpfsApi.Buffer - } - return ipfs[prop] - } - }) - return ipfsApiLite -} - exports.destroy = async function () { if (!node) return diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 4161bc222..61b189418 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -249,7 +249,7 @@ async function addFromURL (info) { const response = await fetch(srcUrl, fetchOptions) const reader = new FileReader() reader.onloadend = () => { - const buffer = ipfs.Buffer.from(reader.result) + const buffer = Buffer.from(reader.result) ipfs.add(buffer, uploadResultHandler) } reader.readAsArrayBuffer(await response.blob()) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 2a01e0734..4e4ba86d4 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -23,7 +23,7 @@ function quickUploadStore (state, emitter) { let reader = new FileReader() reader.onloadend = () => { const buffer = Buffer.from(reader.result) - bg.ipfs.add(buffer, (err, result) => { + bg.ipfs.files.add(buffer, (err, result) => { if (err || !result) { // keep upload tab and display error message in it state.message = `Unable to upload to IPFS API: ${err}` From 2033c83ad314f64da6e696e55e682d5376f8e2b5 Mon Sep 17 00:00:00 2001 From: Oli Evans Date: Tue, 28 Nov 2017 16:20:42 +0000 Subject: [PATCH 4/4] Remove notes --- add-on/src/lib/ipfs-companion.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 61b189418..4183a8991 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -23,13 +23,9 @@ module.exports = async function init () { try { const options = await browser.storage.local.get(optionDefaults) state = window.state = initState(options) - ipfs = window.ipfs = await initIpfsClient(options) console.log('[ipfs-companion] ipfs init complete') - - // Check for ipfs dns txt records dnsLink = createDnsLink(getState) - // is it an ipfs path? ipfsPathValidator = createIpfsPathValidator(getState, dnsLink) modifyRequest = createRequestModifier(getState, dnsLink, ipfsPathValidator) registerListeners()