Skip to content

Commit

Permalink
feat: cleanup repository before starting up IPFS (#722)
Browse files Browse the repository at this point in the history
* feat: cleanup repository before starting up ipfs

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>

* feat: ask to clean if they differ

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>

* feat: run ipfs rpeo fsck

* feat: handle econnrefused

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>

* revert: package-lock

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>

* fix: typo

Co-Authored-By: hacdias <hacdias@gmail.com>

* fix: trim addr

Co-Authored-By: hacdias <hacdias@gmail.com>

* feat: better func names

* feat: update config before starting

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>

* feat: initialize default config on store

* fix: reduce default keysize

Co-Authored-By: hacdias <hacdias@gmail.com>

* feat: change ipfsCOnfig object name

* fix: change store v

* fix: do not need to change origins on IPFS config

* revert prev commit

* dialog as soon as econnrefused

* revert pkg lock

* revert pkg lock

* fix: better error msg

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>

* fix: add quotes
  • Loading branch information
hacdias committed Dec 8, 2018
1 parent 6636e6d commit 3b7c781
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 100 deletions.
85 changes: 18 additions & 67 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { app, dialog, shell } from 'electron'
import { store, createDaemon } from './utils'
import { app, dialog } from 'electron'
import { store, createDaemon, showErrorMessage } from './utils'
import startupMenubar from './menubar'
import registerHooks from './hooks'

Expand All @@ -14,65 +14,6 @@ if (!app.requestSingleInstanceLock()) {
process.exit(1)
}

const issueTemplate = (e) => `Please describe what you were doing when this error happened.
**Specifications**
- **OS**: ${process.platform}
- **IPFS Desktop Version**: ${app.getVersion()}
- **Electron Version**: ${process.versions.electron}
- **Chrome Version**: ${process.versions.chrome}
**Error**
\`\`\`
${e.stack}
\`\`\`
`

function handleError (e) {
const option = dialog.showMessageBox({
type: 'error',
title: 'IPFS Desktop has shutdown',
message: 'IPFS Desktop has shutdown because of an error. You can restart the app or report the error to the developers, which requires a GitHub account.',
buttons: [
'Close',
'Report the error to the developers',
'Restart the app'
],
cancelId: 0
})

if (option === 1) {
shell.openExternal(`https://github.com/ipfs-shipyard/ipfs-desktop/issues/new?body=${encodeURI(issueTemplate(e))}`)
} else if (option === 2) {
app.relaunch()
}

app.exit(1)
}

async function setupConnection () {
let config = store.get('config')
let updateCfg = false

if (config === null) {
config = { type: 'go' }
updateCfg = true
}

const ipfsd = await createDaemon(config)

// createDaemon has changed the config object,
// but didn't add the repo variable.
if (updateCfg) {
config.path = ipfsd.repoPath
store.set('config', config)
}

return ipfsd
}

async function run () {
try {
await app.whenReady()
Expand All @@ -81,23 +22,33 @@ async function run () {
app.exit(1)
}

let config = store.get('ipfsConfig')

try {
// Initial context object
let ctx = {
ipfsd: await setupConnection()
ipfsd: await createDaemon(config)
}

/// Update the path if it was blank previously.
// This way we use the default path when it is
// not set.
if (config.path === '') {
config.path = ctx.ipfsd.repoPath
store.set('ipfsConfig', config)
}

// Initialize windows. These can add properties to context
await startupMenubar(ctx)

// Register hooks
await registerHooks(ctx)

if (!store.get('seenWelcome')) {
// TODO: open WebUI on Welcome screen
}
} catch (e) {
handleError(e)
if (e.message === 'exit') {
app.exit(1)
} else {
showErrorMessage(e)
}
}
}

Expand Down
97 changes: 69 additions & 28 deletions src/utils/daemon.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,104 @@
import IPFSFactory from 'ipfsd-ctl'
import logger from './logger'
import { showConnFailureErrorMessage } from './errors'
import { join } from 'path'
import fs from 'fs-extra'
import { spawnSync } from 'child_process'
import findExecutable from 'ipfsd-ctl/src/utils/find-ipfs-executable'

export default async function createDaemon (opts) {
opts.type = opts.type || 'go'
opts.path = opts.path || ''
opts.flags = opts.flags || ['--migrate=true', '--routing=dhtclient']
opts.keysize = opts.keysize || 4096
function repoFsck (path) {
const exec = findExecutable('go', join(__dirname, '..'))
spawnSync(exec, ['repo', 'fsck'], {
env: {
...process.env,
IPFS_PATH: path
}
})
}

if (opts.type !== 'go') {
throw new Error(`${opts.type} connection is not supported yet`)
async function configure (ipfsd) {
const cfgFile = join(ipfsd.repoPath, 'config')
const cfg = await fs.readJSON(cfgFile)

let origins = []
try {
origins = cfg.API.HTTPHeaders['Access-Control-Allow-Origin']
} catch (e) {
logger.warn(e)
}

const factory = IPFSFactory.create({ type: opts.type })
if (!Array.isArray(origins)) {
origins = []
}

if (!origins.includes('webui://-')) origins.push('webui://-')
if (!origins.includes('https://webui.ipfs.io')) origins.push('https://webui.ipfs.io')

cfg.API.HTTPHeaders['Access-Control-Allow-Origin'] = origins
cfg.API.HTTPHeaders['Access-Control-Allow-Methods'] = ['PUT', 'GET', 'POST']

await fs.writeJSON(cfgFile, cfg)
}

async function spawn ({ type, path, keysize }) {
const factory = IPFSFactory.create({ type: type })

const ipfsd = await new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
factory.spawn({
disposable: false,
defaultAddrs: true,
repoPath: opts.path
repoPath: path
}, (e, ipfsd) => {
if (e) return reject(e)
if (ipfsd.initialized) {
return resolve(ipfsd)
}

ipfsd.init({
directory: opts.path,
keysize: opts.keysize
directory: path,
keysize: keysize
}, e => {
if (e) return reject(e)
resolve(ipfsd)
})
})
})
}

if (!ipfsd.started) {
await new Promise((resolve, reject) => {
ipfsd.start(opts.flags, err => {
if (err) {
return reject(err)
}
async function start (ipfsd, { flags }) {
await new Promise((resolve, reject) => {
ipfsd.start(flags, err => {
if (err) {
return reject(err)
}

resolve()
})
resolve()
})
})
}

export default async function (opts) {
const ipfsd = await spawn(opts)
await configure(ipfsd)

if (!ipfsd.started) {
await start(ipfsd, opts)
}

let origins = []
try {
origins = await ipfsd.api.config.get('API.HTTPHeaders.Access-Control-Allow-Origin')
await ipfsd.api.id()
} catch (e) {
logger.warn(e)
}
if (!e.message.includes('ECONNREFUSED')) {
throw e
}

if (!origins.includes('webui://-')) origins.push('webui://-')
if (!origins.includes('https://webui.ipfs.io')) origins.push('https://webui.ipfs.io')
if (!showConnFailureErrorMessage(ipfsd.repoPath, ipfsd.apiAddr)) {
throw new Error('exit')
}

await ipfsd.api.config.set('API.HTTPHeaders.Access-Control-Allow-Origin', origins)
await ipfsd.api.config.set('API.HTTPHeaders.Access-Control-Allow-Methods', ['PUT', 'GET', 'POST'])
repoFsck(ipfsd.repoPath)
await start(ipfsd, opts)
}

return ipfsd
}
54 changes: 54 additions & 0 deletions src/utils/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { app, dialog, shell } from 'electron'

const issueTemplate = (e) => `Please describe what you were doing when this error happened.
**Specifications**
- **OS**: ${process.platform}
- **IPFS Desktop Version**: ${app.getVersion()}
- **Electron Version**: ${process.versions.electron}
- **Chrome Version**: ${process.versions.chrome}
**Error**
\`\`\`
${e.stack}
\`\`\`
`

export function showErrorMessage (e) {
const option = dialog.showMessageBox({
type: 'error',
title: 'IPFS Desktop has shutdown',
message: 'IPFS Desktop has shutdown because of an error. You can restart the app or report the error to the developers, which requires a GitHub account.',
buttons: [
'Close',
'Report the error to the developers',
'Restart the app'
],
cancelId: 0
})

if (option === 1) {
shell.openExternal(`https://github.com/ipfs-shipyard/ipfs-desktop/issues/new?body=${encodeURI(issueTemplate(e))}`)
} else if (option === 2) {
app.relaunch()
}

app.exit(1)
}

export function showConnFailureErrorMessage (path, addr) {
const option = dialog.showMessageBox({
type: 'warning',
title: 'IPFS Desktop',
message: `IPFS Desktop failed to connect to an existing ipfs api at ${addr}. This can happen if you run 'ipfs daemon' manually and it has not shutdown cleanly. Would you like to try running 'ipfs repo fsck' to remove the lock and api files from ${path} and try again?`,
buttons: [
'No, just quit',
'Yes, run "ipfs repo fsck"'
],
cancelId: 0
})

return option === 1
}
5 changes: 4 additions & 1 deletion src/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import createDaemon from './daemon'
import logo from './logo'
import { showConnFailureErrorMessage, showErrorMessage } from './errors'
import store from './store'
import logger from './logger'
import i18n from './i18n'
Expand All @@ -9,5 +10,7 @@ export {
logo,
store,
logger,
i18n
i18n,
showErrorMessage,
showConnFailureErrorMessage
}
15 changes: 11 additions & 4 deletions src/utils/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ import Store from 'electron-store'

const store = new Store()

if (store.get('version') !== 4) {
if (store.get('version') !== 5) {
store.clear()
store.set('seenWelcome', false)
store.set('config', null)
store.set('version', 4)

// default config
store.set('ipfsConfig', {
type: 'go',
path: '',
flags: ['--migrate=true', '--routing=dhtclient'],
keysize: 2048
})

store.set('version', 5)
}

export default store

0 comments on commit 3b7c781

Please sign in to comment.