diff --git a/.web-extension-id b/.web-extension-id new file mode 100644 index 0000000..5f3f5dc --- /dev/null +++ b/.web-extension-id @@ -0,0 +1,3 @@ +# This file was created by https://github.com/mozilla/web-ext +# Your auto-generated extension ID for addons.mozilla.org is: +{c63fe9b6-dd6c-4267-8ac8-ed4c89480fcc} \ No newline at end of file diff --git a/icons/ipfs-offline.svg b/icons/offline.svg similarity index 100% rename from icons/ipfs-offline.svg rename to icons/offline.svg diff --git a/icons/ipfs.svg b/icons/online.svg similarity index 100% rename from icons/ipfs.svg rename to icons/online.svg diff --git a/manifest.json b/manifest.json index 4a5b981..8b33399 100644 --- a/manifest.json +++ b/manifest.json @@ -1,21 +1,21 @@ { "manifest_version": 2, - "name": "IPFS in the browser", - "version": "1.0", - "description": "Adds window.ipfs to all pages and starts a daemon in the background", + "name": "webext-js-ipfs", + "version": "0.0.1", + "description": "", "icons": { - "48": "icons/ipfs.svg" + "48": "icons/online.svg" }, "background": { - "scripts": ["dist/start-daemon.js"] + "scripts": ["dist/bundle.js"] }, "browser_action": { - "default_icon": "icons/ipfs-offline.svg" + "default_icon": "icons/offline.svg" }, "content_scripts": [ { "matches": ["http://*/*", "https://*/*"], - "js": ["page-script.js"], + "js": ["src/content-script.js"], "run_at": "document_start" } ], diff --git a/testpage.html b/misc/test-page/index.html similarity index 89% rename from testpage.html rename to misc/test-page/index.html index 97244f2..48e00a0 100644 --- a/testpage.html +++ b/misc/test-page/index.html @@ -70,9 +70,12 @@

Cat content

$catOutput.innerText = '' console.log('got some data') console.log(res) - res.on('data', (data) => { - $catOutput.innerText = $catOutput.innerText + data - }) + // TODO should return a stream, but can't get the stream to be passed down + // correctly... + // res.on('data', (data) => { + // $catOutput.innerText = $catOutput.innerText + data + // }) + $catOutput.innerText = res }) }, false) } diff --git a/package.json b/package.json index 7b6c6b9..f8c52d5 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,26 @@ { - "name": "js-ipfs-in-the-browser", - "version": "1.0.0", - "description": "", - "main": "page-script.js", + "name": "webext-js-ipfs", + "version": "0.0.0", + "description": "Bundle js-ipfs into a WebExtension so that it is accessible over window.IPFS", + "main": "src/index.js", "dependencies": { - "babel-preset-es2015": "^6.22.0", - "babelify": "^7.3.0", - "browserify": "^14.1.0", - "ipfs": "^0.22.0", - "setimmediate": "^1.0.5" + "bl": "^1.2.1", + "browserify-zlib-next": "^1.0.1", + "ipfs": "^0.23.1", + "safe-buffer": "^5.0.1" }, "devDependencies": { - "web-ext": "^1.8.1" + "web-ext": "^1.9.1", + "webpack": "^2.5.1" }, "scripts": { - "test": "mocha", - "build": "browserify start-daemon.js -o dist/start-daemon.js", + "build": "webpack", "package": "web-ext build", "sign": "web-ext sign" }, - "keywords": [], - "author": "", - "license": "ISC" + "keywords": [ + "IPFS", + "WebExtension" + ], + "license": "MIT" } diff --git a/readme.md b/readme.md index 15169b3..a9be21e 100644 --- a/readme.md +++ b/readme.md @@ -1,11 +1,13 @@ -## js-ipfs in the browser +## webext-js-ipfs + +> Run js-ipfs inside a WebExtension ### Running locally * `npm install` * `npm run build` * Open `about:debugging` in Firefox and point it to the manifest.json in this repository -* Now try the test page: https://ipfs.io/ipfs/QmcVc8eQiWkR23wMKiQjipLRSjCxR1FEj4TL99aY89J83d +* Now try the test page: https://ipfs.io/ipfs/QmNnMpP1yJbcwREZHTPAjxFYgoNk5pGaudbHHq1t2ahDrb ### Packaging diff --git a/spawn-node.js b/spawn-node.js deleted file mode 100644 index e2f93dc..0000000 --- a/spawn-node.js +++ /dev/null @@ -1,35 +0,0 @@ -const IPFS = require('ipfs') -const multiaddr = require('multiaddr') -const series = require('async/series') - -function spawnNode (options, callback) { - options.path = options.path || '/ipfd/tmp/' + Math.random() - const node = new IPFS(options) - series([ - (cb) => node.init({ emptyRepo: true, bits: 2048 }, (err) => { - if (err) () => {} - cb() - }), - (cb) => { - node.config.get((err, config) => { - if (err) { return cb(err) } - - if (!multiaddr.isMultiaddr(multiaddr(options.signalAddr))) { - return cb(new Error('non valid signalAddr, needs to be a multiaddr')) - } - - const signalDomain = 'star-signal.cloud.ipfs.team' - const wstarMultiaddr = `/libp2p-webrtc-star/dns/${signalDomain}/wss/ipfs/${config.Identity.PeerID}` - - config.Addresses.Swarm = [ wstarMultiaddr ] - config.Discovery.MDNS.Enabled = false - - node.config.replace(config, cb) - }) - }, - (cb) => node.load(cb), - (cb) => node.goOnline(cb) - ], (err) => callback(err, node)) -} - -module.exports = spawnNode diff --git a/page-script.js b/src/content-script.js similarity index 74% rename from page-script.js rename to src/content-script.js index ddefcc8..9e1f9a6 100644 --- a/page-script.js +++ b/src/content-script.js @@ -5,7 +5,11 @@ const myPort = browser.runtime.connect({name: 'port-from-cs'}) const makeCall = (method, args, cb) => { const listener = (m) => { - let {err, res} = m + let { err, res } = m + if (res.on !== undefined) { + console.log('I think I got a stream') + console.log(res) + } cb(err, cloneInto(res, window, {cloneFunctions: true})) myPort.onMessage.removeListener(listener) } @@ -19,7 +23,4 @@ const ipfs = { cat: (args, callback) => { makeCall('cat', args, callback) } } -window.wrappedJSObject.ipfs = cloneInto( - ipfs, - window, - {cloneFunctions: true}) +window.wrappedJSObject.ipfs = cloneInto(ipfs, window, {cloneFunctions: true}) diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..c564558 --- /dev/null +++ b/src/index.js @@ -0,0 +1,83 @@ +/* global browser */ + +const IPFS = require('ipfs') +const bl = require('bl') + +browser.browserAction.setIcon({path: '/icons/ipfs-offline.svg'}) + +const repoPath = 'ipfs-webext-' + Math.random() + +const node = new IPFS({ + repo: repoPath, + config: { + Addresses: { + Swarm: [ + '/libp2p-webrtc-star/dns4/star-signal.cloud.ipfs.team/wss' + ] + } + } +}) + +node.on('err', (err) => { + console.log('Error spawning the node', err) + browser.browserAction.setIcon({ path: '/icons/offline.svg' }) +}) + +node.on('ready', () => { + window.ipfs = node + + browser.browserAction.setIcon({ path: '/icons/online.svg' }) + + setInterval(() => { + if (node.isOnline()) { + node.swarm.peers((err, peers) => { + if (err) { + console.log('Error on swarm.peers', err) + } + + const nPeers = peers.length.toString() + browser.browserAction.setBadgeText({nPeers}) + }) + } + }, 1000) + + browser.browserAction.onClicked.addListener(() => { + if (node.isOnline()) { + node.stop(() => { + browser.browserAction.setIcon({path: '/icons/offline.svg'}) + browser.browserAction.setBadgeText({text: 'Offline'}) + }) + } else { + node.start(() => { + browser.browserAction.setIcon({path: '/icons/online.svg'}) + }) + } + }) + + const methods = { + 'id': (args, send) => { + node.id((err, id) => send({err: err, res: id})) + }, + 'add': (args, send) => { + node.files.add(Buffer.from(args), (err, res) => send({err: err, res: res})) + }, + 'cat': (args, send) => { + node.files.cat(args, (err, stream) => { + if (err) { + send({ err: err }) + } + stream.pipe(bl((err, data) => send({ err: err, res: data }))) + }) + } + } + + browser.runtime.onConnect.addListener((port) => { + port.onMessage.addListener((m) => { + if (methods[m.method] !== undefined) { + methods[m.method](m.args, (res) => port.postMessage(res)) + } else { + throw new Error('Method ' + m.method + ' is currently not exposed') + } + }) + }) +}) diff --git a/start-daemon.js b/start-daemon.js deleted file mode 100644 index 75951f2..0000000 --- a/start-daemon.js +++ /dev/null @@ -1,69 +0,0 @@ -/* global browser */ -require('setimmediate') // TODO js-ipfs fails without this in the ID call -const spawn = require('./spawn-node.js') -browser.browserAction.setIcon({path: '/icons/ipfs-offline.svg'}) - -spawn({}, (err, ipfsNode) => { - if (err) throw err - - browser.browserAction.setIcon({path: '/icons/ipfs.svg'}) - - setInterval(() => { - if (ipfsNode.isOnline().isOnline) { - ipfsNode.swarm.peers((err, peers) => { - if (err) throw err - console.log(peers) - const text = peers.length.toString() - browser.browserAction.setBadgeText({text}) - }) - } - }, 1000) - - browser.browserAction.onClicked.addListener(() => { - if (ipfsNode.isOnline().isOnline) { - ipfsNode.goOffline(() => { - browser.browserAction.setIcon({path: '/icons/ipfs-offline.svg'}) - browser.browserAction.setBadgeText({text: 'Offline'}) - }) - } else { - ipfsNode.goOnline(() => { - browser.browserAction.setIcon({path: '/icons/ipfs.svg'}) - }) - } - }) - - window.ipfs = ipfsNode - const methods = { - 'id': (args, send) => { - console.log('method#id') - ipfsNode.id((err, id) => { - send({err, res: id}) - }) - }, - 'add': (args, send) => { - ipfsNode.files.add(new Buffer(args), (err, res) => { - send({err, res}) - }) - }, - 'cat': (args, send) => { - ipfsNode.files.cat(args, (err, res) => { - send({err, res}) - }) - } - } - - browser.runtime.onConnect.addListener((port) => { - port.onMessage.addListener((m) => { - if (methods[m.method] !== undefined) { - console.log('can make this call') - methods[m.method](m.args, (res) => { - console.log('made it and responding!') - console.log(res) - port.postMessage(res) - }) - } else { - throw new Error('Method ' + m.method + ' is currently not exposed') - } - }) - }) -}) diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..e1c29f1 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,16 @@ +module.exports = { + entry: './src/index.js', + output: { + filename: 'dist/bundle.js' + }, + node: { + fs: 'empty', + net: 'empty', + tls: 'empty' + }, + resolve: { + alias: { + zlib: 'browserify-zlib-next' + } + } +}