Skip to content

Commit 3b7c781

Browse files
authored
feat: cleanup repository before starting up IPFS (#722)
* feat: cleanup repository before starting up ipfs License: MIT Signed-off-by: Henrique Dias <[email protected]> * feat: ask to clean if they differ License: MIT Signed-off-by: Henrique Dias <[email protected]> * feat: run ipfs rpeo fsck * feat: handle econnrefused License: MIT Signed-off-by: Henrique Dias <[email protected]> * revert: package-lock License: MIT Signed-off-by: Henrique Dias <[email protected]> * fix: typo Co-Authored-By: hacdias <[email protected]> * fix: trim addr Co-Authored-By: hacdias <[email protected]> * feat: better func names * feat: update config before starting License: MIT Signed-off-by: Henrique Dias <[email protected]> * feat: initialize default config on store * fix: reduce default keysize Co-Authored-By: hacdias <[email protected]> * 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 <[email protected]> * fix: add quotes
1 parent 6636e6d commit 3b7c781

File tree

5 files changed

+156
-100
lines changed

5 files changed

+156
-100
lines changed

src/index.js

Lines changed: 18 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { app, dialog, shell } from 'electron'
2-
import { store, createDaemon } from './utils'
1+
import { app, dialog } from 'electron'
2+
import { store, createDaemon, showErrorMessage } from './utils'
33
import startupMenubar from './menubar'
44
import registerHooks from './hooks'
55

@@ -14,65 +14,6 @@ if (!app.requestSingleInstanceLock()) {
1414
process.exit(1)
1515
}
1616

17-
const issueTemplate = (e) => `Please describe what you were doing when this error happened.
18-
19-
**Specifications**
20-
21-
- **OS**: ${process.platform}
22-
- **IPFS Desktop Version**: ${app.getVersion()}
23-
- **Electron Version**: ${process.versions.electron}
24-
- **Chrome Version**: ${process.versions.chrome}
25-
26-
**Error**
27-
28-
\`\`\`
29-
${e.stack}
30-
\`\`\`
31-
`
32-
33-
function handleError (e) {
34-
const option = dialog.showMessageBox({
35-
type: 'error',
36-
title: 'IPFS Desktop has shutdown',
37-
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.',
38-
buttons: [
39-
'Close',
40-
'Report the error to the developers',
41-
'Restart the app'
42-
],
43-
cancelId: 0
44-
})
45-
46-
if (option === 1) {
47-
shell.openExternal(`https://github.com/ipfs-shipyard/ipfs-desktop/issues/new?body=${encodeURI(issueTemplate(e))}`)
48-
} else if (option === 2) {
49-
app.relaunch()
50-
}
51-
52-
app.exit(1)
53-
}
54-
55-
async function setupConnection () {
56-
let config = store.get('config')
57-
let updateCfg = false
58-
59-
if (config === null) {
60-
config = { type: 'go' }
61-
updateCfg = true
62-
}
63-
64-
const ipfsd = await createDaemon(config)
65-
66-
// createDaemon has changed the config object,
67-
// but didn't add the repo variable.
68-
if (updateCfg) {
69-
config.path = ipfsd.repoPath
70-
store.set('config', config)
71-
}
72-
73-
return ipfsd
74-
}
75-
7617
async function run () {
7718
try {
7819
await app.whenReady()
@@ -81,23 +22,33 @@ async function run () {
8122
app.exit(1)
8223
}
8324

25+
let config = store.get('ipfsConfig')
26+
8427
try {
8528
// Initial context object
8629
let ctx = {
87-
ipfsd: await setupConnection()
30+
ipfsd: await createDaemon(config)
31+
}
32+
33+
/// Update the path if it was blank previously.
34+
// This way we use the default path when it is
35+
// not set.
36+
if (config.path === '') {
37+
config.path = ctx.ipfsd.repoPath
38+
store.set('ipfsConfig', config)
8839
}
8940

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

9344
// Register hooks
9445
await registerHooks(ctx)
95-
96-
if (!store.get('seenWelcome')) {
97-
// TODO: open WebUI on Welcome screen
98-
}
9946
} catch (e) {
100-
handleError(e)
47+
if (e.message === 'exit') {
48+
app.exit(1)
49+
} else {
50+
showErrorMessage(e)
51+
}
10152
}
10253
}
10354

src/utils/daemon.js

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,104 @@
11
import IPFSFactory from 'ipfsd-ctl'
22
import logger from './logger'
3+
import { showConnFailureErrorMessage } from './errors'
4+
import { join } from 'path'
5+
import fs from 'fs-extra'
6+
import { spawnSync } from 'child_process'
7+
import findExecutable from 'ipfsd-ctl/src/utils/find-ipfs-executable'
38

4-
export default async function createDaemon (opts) {
5-
opts.type = opts.type || 'go'
6-
opts.path = opts.path || ''
7-
opts.flags = opts.flags || ['--migrate=true', '--routing=dhtclient']
8-
opts.keysize = opts.keysize || 4096
9+
function repoFsck (path) {
10+
const exec = findExecutable('go', join(__dirname, '..'))
11+
spawnSync(exec, ['repo', 'fsck'], {
12+
env: {
13+
...process.env,
14+
IPFS_PATH: path
15+
}
16+
})
17+
}
918

10-
if (opts.type !== 'go') {
11-
throw new Error(`${opts.type} connection is not supported yet`)
19+
async function configure (ipfsd) {
20+
const cfgFile = join(ipfsd.repoPath, 'config')
21+
const cfg = await fs.readJSON(cfgFile)
22+
23+
let origins = []
24+
try {
25+
origins = cfg.API.HTTPHeaders['Access-Control-Allow-Origin']
26+
} catch (e) {
27+
logger.warn(e)
1228
}
1329

14-
const factory = IPFSFactory.create({ type: opts.type })
30+
if (!Array.isArray(origins)) {
31+
origins = []
32+
}
33+
34+
if (!origins.includes('webui://-')) origins.push('webui://-')
35+
if (!origins.includes('https://webui.ipfs.io')) origins.push('https://webui.ipfs.io')
36+
37+
cfg.API.HTTPHeaders['Access-Control-Allow-Origin'] = origins
38+
cfg.API.HTTPHeaders['Access-Control-Allow-Methods'] = ['PUT', 'GET', 'POST']
39+
40+
await fs.writeJSON(cfgFile, cfg)
41+
}
42+
43+
async function spawn ({ type, path, keysize }) {
44+
const factory = IPFSFactory.create({ type: type })
1545

16-
const ipfsd = await new Promise((resolve, reject) => {
46+
return new Promise((resolve, reject) => {
1747
factory.spawn({
1848
disposable: false,
1949
defaultAddrs: true,
20-
repoPath: opts.path
50+
repoPath: path
2151
}, (e, ipfsd) => {
2252
if (e) return reject(e)
2353
if (ipfsd.initialized) {
2454
return resolve(ipfsd)
2555
}
2656

2757
ipfsd.init({
28-
directory: opts.path,
29-
keysize: opts.keysize
58+
directory: path,
59+
keysize: keysize
3060
}, e => {
3161
if (e) return reject(e)
3262
resolve(ipfsd)
3363
})
3464
})
3565
})
66+
}
3667

37-
if (!ipfsd.started) {
38-
await new Promise((resolve, reject) => {
39-
ipfsd.start(opts.flags, err => {
40-
if (err) {
41-
return reject(err)
42-
}
68+
async function start (ipfsd, { flags }) {
69+
await new Promise((resolve, reject) => {
70+
ipfsd.start(flags, err => {
71+
if (err) {
72+
return reject(err)
73+
}
4374

44-
resolve()
45-
})
75+
resolve()
4676
})
77+
})
78+
}
79+
80+
export default async function (opts) {
81+
const ipfsd = await spawn(opts)
82+
await configure(ipfsd)
83+
84+
if (!ipfsd.started) {
85+
await start(ipfsd, opts)
4786
}
4887

49-
let origins = []
5088
try {
51-
origins = await ipfsd.api.config.get('API.HTTPHeaders.Access-Control-Allow-Origin')
89+
await ipfsd.api.id()
5290
} catch (e) {
53-
logger.warn(e)
54-
}
91+
if (!e.message.includes('ECONNREFUSED')) {
92+
throw e
93+
}
5594

56-
if (!origins.includes('webui://-')) origins.push('webui://-')
57-
if (!origins.includes('https://webui.ipfs.io')) origins.push('https://webui.ipfs.io')
95+
if (!showConnFailureErrorMessage(ipfsd.repoPath, ipfsd.apiAddr)) {
96+
throw new Error('exit')
97+
}
5898

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

62103
return ipfsd
63104
}

src/utils/errors.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { app, dialog, shell } from 'electron'
2+
3+
const issueTemplate = (e) => `Please describe what you were doing when this error happened.
4+
5+
**Specifications**
6+
7+
- **OS**: ${process.platform}
8+
- **IPFS Desktop Version**: ${app.getVersion()}
9+
- **Electron Version**: ${process.versions.electron}
10+
- **Chrome Version**: ${process.versions.chrome}
11+
12+
**Error**
13+
14+
\`\`\`
15+
${e.stack}
16+
\`\`\`
17+
`
18+
19+
export function showErrorMessage (e) {
20+
const option = dialog.showMessageBox({
21+
type: 'error',
22+
title: 'IPFS Desktop has shutdown',
23+
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.',
24+
buttons: [
25+
'Close',
26+
'Report the error to the developers',
27+
'Restart the app'
28+
],
29+
cancelId: 0
30+
})
31+
32+
if (option === 1) {
33+
shell.openExternal(`https://github.com/ipfs-shipyard/ipfs-desktop/issues/new?body=${encodeURI(issueTemplate(e))}`)
34+
} else if (option === 2) {
35+
app.relaunch()
36+
}
37+
38+
app.exit(1)
39+
}
40+
41+
export function showConnFailureErrorMessage (path, addr) {
42+
const option = dialog.showMessageBox({
43+
type: 'warning',
44+
title: 'IPFS Desktop',
45+
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?`,
46+
buttons: [
47+
'No, just quit',
48+
'Yes, run "ipfs repo fsck"'
49+
],
50+
cancelId: 0
51+
})
52+
53+
return option === 1
54+
}

src/utils/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import createDaemon from './daemon'
22
import logo from './logo'
3+
import { showConnFailureErrorMessage, showErrorMessage } from './errors'
34
import store from './store'
45
import logger from './logger'
56
import i18n from './i18n'
@@ -9,5 +10,7 @@ export {
910
logo,
1011
store,
1112
logger,
12-
i18n
13+
i18n,
14+
showErrorMessage,
15+
showConnFailureErrorMessage
1316
}

src/utils/store.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ import Store from 'electron-store'
22

33
const store = new Store()
44

5-
if (store.get('version') !== 4) {
5+
if (store.get('version') !== 5) {
66
store.clear()
7-
store.set('seenWelcome', false)
8-
store.set('config', null)
9-
store.set('version', 4)
7+
8+
// default config
9+
store.set('ipfsConfig', {
10+
type: 'go',
11+
path: '',
12+
flags: ['--migrate=true', '--routing=dhtclient'],
13+
keysize: 2048
14+
})
15+
16+
store.set('version', 5)
1017
}
1118

1219
export default store

0 commit comments

Comments
 (0)