Skip to content

Commit

Permalink
Merge pull request #395 from tableflip/feat/embedded-ipfs-config
Browse files Browse the repository at this point in the history
Allows embedded IPFS node to be configured
  • Loading branch information
lidel authored Mar 22, 2018
2 parents 1f63d26 + ae4b179 commit 19a6922
Show file tree
Hide file tree
Showing 14 changed files with 562 additions and 260 deletions.
18 changes: 17 additions & 1 deletion add-on/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@
},
"description": "A generic placeholder for error notification (notify_inlineErrorMsg)"
},
"notify_startIpfsNodeErrorTitle": {
"message": "Failed to start IPFS node",
"description": "System notification title displayed when starting an IPFS node fails (notify_startIpfsNodeErrorTitle)"
},
"notify_stopIpfsNodeErrorTitle": {
"message": "Failed to stop IPFS node",
"description": "System notification title displayed when stopping an IPFS node fails (notify_stopIpfsNodeErrorTitle)"
},
"option_header_nodeType" : {
"message": "IPFS Node",
"description": "A section header on the Preferences screen (option_header_nodeType)"
Expand All @@ -181,13 +189,21 @@
"message": "External: Connect to an IPFS daemon over HTTP. \n\n Embedded: Run an IPFS node in your browser via ipfs-js *Experimental*",
"description": "An option description on the Preferences screen (option_ipfsNodeType_description)"
},
"option_ipfsNodeConfig_title": {
"message": "IPFS Node Config",
"description": "An option title on the Preferences screen (option_ipfsNodeConfig_title)"
},
"option_ipfsNodeConfig_description": {
"message": "Configuration for the embedded IPFS node. Must be valid JSON.",
"description": "An option description on the Preferences screen (option_ipfsNodeConfig_description)"
},
"option_ipfsNodeType_external": {
"message": "External",
"description": "An option on the Preferences screen (option_ipfsNodeType_external)"
},
"option_ipfsNodeType_embedded": {
"message": "Embedded",
"description": "An option on the Preferences screeni (option_ipfsNodeType_embedded)"
"description": "An option on the Preferences screen (option_ipfsNodeType_embedded)"
},
"option_header_gateways": {
"message": "Gateways",
Expand Down
13 changes: 5 additions & 8 deletions add-on/src/lib/ipfs-client/embedded.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
'use strict'

const Ipfs = require('ipfs')
const { optionDefaults } = require('../options')

let node = null

exports.init = function init () {
exports.init = function init (opts) {
console.log('[ipfs-companion] Embedded ipfs init')

node = new Ipfs({
config: {
Addresses: {
Swarm: []
}
}
})
node = new Ipfs(
JSON.parse(opts.ipfsNodeConfig || optionDefaults.ipfsNodeConfig)
)

if (node.isOnline()) {
return Promise.resolve(node)
Expand Down
8 changes: 6 additions & 2 deletions add-on/src/lib/ipfs-client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const external = require('./external')
const embedded = require('./embedded')

let client = null
let client

async function initIpfsClient (opts) {
await destroyIpfsClient()
Expand All @@ -19,7 +19,11 @@ async function initIpfsClient (opts) {

async function destroyIpfsClient () {
if (client && client.destroy) {
return client.destroy()
try {
await client.destroy()
} finally {
client = null
}
}
}

Expand Down
102 changes: 70 additions & 32 deletions add-on/src/lib/ipfs-companion.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ module.exports = async function init () {
const options = await browser.storage.local.get(optionDefaults)
runtime = await createRuntimeChecks(browser)
state = initState(options)
ipfs = await initIpfsClient(state)
notify = createNotifier(getState)

// It's ok for this to fail, node might be unavailable or mis-configured
try {
ipfs = await initIpfsClient(state)
} catch (err) {
console.error('[ipfs-companion] Failed to init IPFS client', err)
notify(
'notify_startIpfsNodeErrorTitle',
err.name === 'ValidationError' ? err.details[0].message : err.message
)
}

copier = createCopier(getState, notify)
dnsLink = createDnsLink(getState)
ipfsPathValidator = createIpfsPathValidator(getState, dnsLink)
Expand Down Expand Up @@ -385,6 +396,7 @@ module.exports = async function init () {
}

async function getSwarmPeerCount () {
if (!ipfs) return offlinePeerCount
try {
const peerInfos = await ipfs.swarm.peers()
return peerInfos.length
Expand All @@ -395,7 +407,7 @@ module.exports = async function init () {
}

async function getRepoStats () {
if (!ipfs.stats || !ipfs.stats.repo) return {}
if (!ipfs || !ipfs.stats || !ipfs.stats.repo) return {}
try {
const repoStats = await ipfs.stats.repo()
return repoStats
Expand Down Expand Up @@ -515,46 +527,72 @@ module.exports = async function init () {
}

async function onStorageChange (changes, area) {
let shouldRestartIpfsClient = false

for (let key in changes) {
let change = changes[key]
if (change.oldValue !== change.newValue) {
// debug info
// console.info(`Storage key "${key}" in namespace "${area}" changed. Old value was "${change.oldValue}", new value is "${change.newValue}".`)
if (key === 'ipfsNodeType') {
state.ipfsNodeType = change.newValue
ipfs = await initIpfsClient(state)
apiStatusUpdate()
} else if (key === 'ipfsApiUrl') {
const change = changes[key]
if (change.oldValue === change.newValue) continue

// debug info
// console.info(`Storage key "${key}" in namespace "${area}" changed. Old value was "${change.oldValue}", new value is "${change.newValue}".`)
switch (key) {
case 'ipfsNodeType':
case 'ipfsNodeConfig':
shouldRestartIpfsClient = true
state[key] = change.newValue
break
case 'ipfsApiUrl':
state.apiURL = new URL(change.newValue)
state.apiURLString = state.apiURL.toString()
ipfs = await initIpfsClient(state)
apiStatusUpdate()
} else if (key === 'ipfsApiPollMs') {
shouldRestartIpfsClient = true
break
case 'ipfsApiPollMs':
setApiStatusUpdateInterval(change.newValue)
} else if (key === 'customGatewayUrl') {
break
case 'customGatewayUrl':
state.gwURL = new URL(change.newValue)
state.gwURLString = state.gwURL.toString()
} else if (key === 'publicGatewayUrl') {
break
case 'publicGatewayUrl':
state.pubGwURL = new URL(change.newValue)
state.pubGwURLString = state.pubGwURL.toString()
} else if (key === 'useCustomGateway') {
break
case 'useCustomGateway':
state.redirect = change.newValue
} else if (key === 'linkify') {
state.linkify = change.newValue
} else if (key === 'catchUnhandledProtocols') {
state.catchUnhandledProtocols = change.newValue
} else if (key === 'displayNotifications') {
state.displayNotifications = change.newValue
} else if (key === 'automaticMode') {
state.automaticMode = change.newValue
} else if (key === 'dnslink') {
state.dnslink = change.newValue
} else if (key === 'preloadAtPublicGateway') {
state.preloadAtPublicGateway = change.newValue
} else if (key === 'ipfsProxy') {
state.ipfsProxy = change.newValue
}
break
case 'linkify':
case 'catchUnhandledProtocols':
case 'displayNotifications':
case 'automaticMode':
case 'dnslink':
case 'preloadAtPublicGateway':
case 'ipfsProxy':
state[key] = change.newValue
break
}
}

if (shouldRestartIpfsClient) {
try {
await destroyIpfsClient()
} catch (err) {
console.error('[ipfs-companion] Failed to destroy IPFS client', err)
notify('notify_stopIpfsNodeErrorTitle', err.message)
} finally {
ipfs = null
}

try {
ipfs = await initIpfsClient(state)
} catch (err) {
console.error('[ipfs-companion] Failed to init IPFS client', err)
notify(
'notify_startIpfsNodeErrorTitle',
err.name === 'ValidationError' ? err.details[0].message : err.message
)
}

apiStatusUpdate()
}
}

Expand Down
12 changes: 0 additions & 12 deletions add-on/src/lib/is-js-ipfs-enabled.js

This file was deleted.

2 changes: 1 addition & 1 deletion add-on/src/lib/notifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const browser = require('webextension-polyfill')

function createNotifier (getState) {
return (titleKey, messageKey, messageParam) => {
const title = browser.i18n.getMessage(titleKey)
const title = browser.i18n.getMessage(titleKey) || titleKey
let message
if (messageKey.startsWith('notify_')) {
message = messageParam ? browser.i18n.getMessage(messageKey, messageParam) : browser.i18n.getMessage(messageKey)
Expand Down
7 changes: 7 additions & 0 deletions add-on/src/lib/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

const optionDefaults = Object.freeze({
ipfsNodeType: 'external',
ipfsNodeConfig: JSON.stringify({
config: {
Addresses: {
Swarm: []
}
}
}, null, 2),
publicGatewayUrl: 'https://ipfs.io',
useCustomGateway: true,
automaticMode: true,
Expand Down
1 change: 1 addition & 0 deletions add-on/src/lib/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function initState (options) {
// to minimize performance impact on overall browsing experience
state.peerCount = offlinePeerCount
state.ipfsNodeType = options.ipfsNodeType
state.ipfsNodeConfig = options.ipfsNodeConfig
state.pubGwURL = new URL(options.publicGatewayUrl)
state.pubGwURLString = state.pubGwURL.toString()
state.redirect = options.useCustomGateway
Expand Down
17 changes: 13 additions & 4 deletions add-on/src/options/forms/ipfs-node-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@

const browser = require('webextension-polyfill')
const html = require('choo/html')
const isJsIpfsEnabled = require('../../lib/is-js-ipfs-enabled')

function ipfsNodeForm ({ ipfsNodeType, onOptionChange }) {
if (!isJsIpfsEnabled()) return null

function ipfsNodeForm ({ ipfsNodeType, ipfsNodeConfig, onOptionChange }) {
const onIpfsNodeTypeChange = onOptionChange('ipfsNodeType')
const onIpfsNodeConfigChange = onOptionChange('ipfsNodeConfig')

return html`
<form>
Expand All @@ -34,6 +32,17 @@ function ipfsNodeForm ({ ipfsNodeType, onOptionChange }) {
</option>
</select>
</div>
${ipfsNodeType === 'embedded' ? html`
<div>
<label for="ipfsNodeConfig">
<dl>
<dt>${browser.i18n.getMessage('option_ipfsNodeConfig_title')}</dt>
<dd>${browser.i18n.getMessage('option_ipfsNodeConfig_description')}</dd>
</dl>
</label>
<textarea id="ipfsNodeConfig" rows="4" onchange=${onIpfsNodeConfigChange}>${ipfsNodeConfig}</textarea>
</div>
` : null}
</fieldset>
</form>
`
Expand Down
4 changes: 3 additions & 1 deletion add-on/src/options/options.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ div:hover {
div:hover > label > dl > dd {
color: #333;
}
input, select {
input, textarea, select {
flex: 1;
padding: .5em;
font-family: monospace;
Expand All @@ -63,6 +63,7 @@ input, select {
input[type=text],
input[type=url],
input[type=number],
textarea,
select {
-webkit-transition: all 0.30s ease-in-out;
-moz-transition: all 0.30s ease-in-out;
Expand All @@ -71,6 +72,7 @@ select {
input[type=text]:focus,
input[type=url]:focus,
input[type=number]:focus,
textarea:focus,
select:focus {
outline: none;
border: 1px solid #3E9398;
Expand Down
1 change: 1 addition & 0 deletions add-on/src/options/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = function optionsPage (state, emit) {
<div>
${ipfsNodeForm({
ipfsNodeType: state.options.ipfsNodeType,
ipfsNodeConfig: state.options.ipfsNodeConfig,
onOptionChange
})}
${gatewaysForm({
Expand Down
3 changes: 1 addition & 2 deletions add-on/src/popup/browser-action/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
const browser = require('webextension-polyfill')
const html = require('choo/html')
const logo = require('../logo')
const isJsIpfsEnabled = require('../../lib/is-js-ipfs-enabled')()

module.exports = function header ({ ipfsNodeType, onToggleNodeType, isIpfsOnline }) {
return html`
Expand All @@ -18,7 +17,7 @@ module.exports = function header ({ ipfsNodeType, onToggleNodeType, isIpfsOnline
})}
</div>
<h1 class="f5 mt2 mb2 tc white normal">IPFS node</h1>
<div class="pt1 ${isJsIpfsEnabled ? '' : 'dn'}">
<div class="pt1">
<div class="flex flex-row justify-center mb2">
<label for="node" class="mdc-switch-label w-40 tr f7 white" title="${browser.i18n.getMessage('panel_headerIpfsNodeEmbeddedTitle')}">
${browser.i18n.getMessage('panel_headerIpfsNodeEmbedded')}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@
"choo": "6.8.0",
"doc-sniff": "1.0.1",
"file-type": "7.6.0",
"ipfs": "0.27.7",
"ipfs-api": "18.1.1",
"ipfs": "0.28.2",
"ipfs-api": "18.2.0",
"ipfs-css": "0.2.0",
"ipfs-postmsg-proxy": "2.8.4",
"is-ipfs": "0.3.2",
Expand Down
Loading

0 comments on commit 19a6922

Please sign in to comment.