Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows embedded IPFS node to be configured #395

Merged
merged 8 commits into from
Mar 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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