-
Notifications
You must be signed in to change notification settings - Fork 16
Add check for already running daemon and custom ports for own daemon #92
Changes from 4 commits
e60983e
26d0b31
dad4672
8cd37a2
5cf8af4
5c7b08c
3ebcd63
ef552b6
4e27175
25db4a8
b6da83c
54b7481
6bcfeed
066a0b9
b8c1ff8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,11 +8,66 @@ import { app, dialog } from 'electron' | |
import pjson from '../package.json' | ||
import { get as getAppRoot } from 'app-root-dir' | ||
|
||
const CUSTOM_API_PORT = 5101 | ||
const CUSTOM_GATEWAY_PORT = 8180 | ||
const CUSTOM_SWARM_PORT = 4101 | ||
|
||
export let binaryPath = `${getAppRoot()}/go-ipfs/ipfs` | ||
let skipRepo = false | ||
|
||
export function skipRepoPath () { | ||
skipRepo = true | ||
} | ||
|
||
export function getRepoPath () { | ||
return pathJoin(app.getPath('userData'), '.ipfs') | ||
} | ||
|
||
export function setCustomBinaryPath (path) { | ||
binaryPath = path | ||
} | ||
|
||
export function executeIPFSCommand (command) { | ||
const env = skipRepo ? '' : `IPFS_PATH=${getRepoPath()}` | ||
return exec(`${env} ${binaryPath} ${command}`) | ||
} | ||
|
||
export function spawnIPFSCommand (command) { | ||
const options = skipRepo ? undefined : { env: { IPFS_PATH: getRepoPath() } } | ||
return spawn(binaryPath, [command], options) | ||
} | ||
|
||
/** | ||
* getPathIPFSBinary will return the IPFS default path | ||
* | ||
* Returns the version of the currently running API. | ||
* If no API is available returns `null`. | ||
* | ||
* Example: 'v0.4.14' | ||
* @returns Promise<string> | ||
*/ | ||
export function getPathIPFSBinary () { | ||
return `${getAppRoot()}/go-ipfs/ipfs` | ||
export function getAPIVersion () { | ||
return request({ | ||
// what if it's running on a different port? | ||
uri: 'http://localhost:5001/api/v0/version', | ||
headers: { 'User-Agent': `Orion/${pjson.version}` } | ||
}) | ||
.then(res => { | ||
/** | ||
* ApiVersionResult { | ||
* Version: "0.4.14", | ||
* Commit: "", | ||
* Repo: "6", | ||
* System: "amd64/linux", | ||
* Golang: "go1.10" | ||
* } | ||
*/ | ||
res = JSON.parse(res) | ||
return Promise.resolve(`v${res.Version}`) | ||
}) | ||
.catch(err => { | ||
console.error('API not available', err.message) | ||
return Promise.resolve(null) | ||
}) | ||
} | ||
|
||
/** | ||
|
@@ -21,19 +76,18 @@ export function getPathIPFSBinary () { | |
*/ | ||
export function startIPFSDaemon () { | ||
return new Promise((resolve, reject) => { | ||
const binaryPath = getPathIPFSBinary() | ||
const ipfsProcess = spawn(binaryPath, ['daemon']) | ||
const ipfsProcess = spawnIPFSCommand('daemon') | ||
|
||
// Prepare temporary file for logging: | ||
const tmpLog = tmpFileSync({keep: true}) | ||
const tmpLog = tmpFileSync({ keep: true }) | ||
const tmpLogPipe = createWriteStream(tmpLog.name) | ||
|
||
console.log(`Logging IPFS logs in: ${tmpLog.name}`) | ||
console.log(`Logging IPFS Daemon logs in: ${tmpLog.name}`) | ||
|
||
ipfsProcess.stdout.on('data', (data) => console.log(`IPFS: ${data}`)) | ||
ipfsProcess.stdout.on('data', (data) => console.log(`IPFS Daemon: ${data}`)) | ||
ipfsProcess.stdout.pipe(tmpLogPipe) | ||
|
||
ipfsProcess.stderr.on('data', (data) => console.log(`IPFS Error: ${data}`)) | ||
ipfsProcess.stderr.on('data', (data) => console.log(`IPFS Daemon Error: ${data}`)) | ||
ipfsProcess.stderr.pipe(tmpLogPipe) | ||
|
||
ipfsProcess.on('close', (exit) => { | ||
|
@@ -48,17 +102,19 @@ export function startIPFSDaemon () { | |
console.log(`IPFS Closed: ${exit}`) | ||
}) | ||
|
||
// Resolves the process after 1 second | ||
setTimeout(() => { resolve(ipfsProcess) }, 1 * 1000) | ||
resolve(ipfsProcess) | ||
}) | ||
} | ||
|
||
/** | ||
* isIPFSInitialised returns a boolean if the repository config file is present | ||
* in the default path (~/.ipfs/config) | ||
* in the default path (i.e. userData) | ||
* (~/.config/Electron/.ipfs/config) in development | ||
* | ||
* https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname | ||
*/ | ||
export function isIPFSInitialised () { | ||
const confFile = pathJoin(app.getPath('home'), '.ipfs', 'config') | ||
const confFile = pathJoin(getRepoPath(), 'config') | ||
return existsSync(confFile) | ||
} | ||
|
||
|
@@ -70,11 +126,9 @@ export function ensuresIPFSInitialised () { | |
if (isIPFSInitialised()) return Promise.resolve() | ||
console.log('Initialising IPFS repository...') | ||
return new Promise((resolve, reject) => { | ||
const binaryPath = getPathIPFSBinary() | ||
const ipfsProcess = spawn(binaryPath, ['init']) | ||
|
||
const ipfsProcess = spawnIPFSCommand('init') | ||
// Prepare temporary file for logging: | ||
const tmpLog = tmpFileSync({keep: true}) | ||
const tmpLog = tmpFileSync({ keep: true }) | ||
const tmpLogPipe = createWriteStream(tmpLog.name) | ||
|
||
console.log(`Logging IPFS init logs in: ${tmpLog.name}`) | ||
|
@@ -102,23 +156,32 @@ export function ensuresIPFSInitialised () { | |
} | ||
|
||
/** | ||
* Returns the multiAddr usable to connect to the local dameon via API | ||
* 5001 -> 5101 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add few lines about |
||
* 8080 -> 8180 | ||
* 4001 -> 4101 | ||
*/ | ||
export function getMultiAddrIPFSDaemon () { | ||
// Other option: ask the binary wich one to use | ||
// const binaryPath = getPathIPFSBinary() | ||
// const multiAddr = execSync(`${binaryPath} config Addresses.API`) | ||
return '/ip4/127.0.0.1/tcp/5001' | ||
export function ensureAddressesConfigured (customPorts = false) { | ||
let apiPort = '5001' | ||
let gatewayPort = '8080' | ||
let swarmPort = '4001' | ||
|
||
if (customPorts) { | ||
console.log('Setting custom addresses for API, Gateway and Swarm') | ||
apiPort = CUSTOM_API_PORT | ||
gatewayPort = CUSTOM_GATEWAY_PORT | ||
swarmPort = CUSTOM_SWARM_PORT | ||
} | ||
|
||
return executeIPFSCommand(`config Addresses.API /ip4/127.0.0.1/tcp/${apiPort}`) | ||
.then(() => executeIPFSCommand(`config Addresses.Gateway /ip4/127.0.0.1/tcp/${gatewayPort}`)) | ||
.then(() => executeIPFSCommand(`config Addresses.Swarm --json '["/ip4/0.0.0.0/tcp/${swarmPort}", "/ip6/::/tcp/${swarmPort}"]'`)) | ||
} | ||
|
||
/** | ||
* Set the multiAddr usable to connect to the local dameon via API. | ||
* It restores it to /ip4/127.0.0.1/tcp/5001 | ||
* returns a promise. | ||
* Returns the multiAddr usable to connect to the local dameon via API | ||
*/ | ||
export function setMultiAddrIPFSDaemon () { | ||
const binaryPath = getPathIPFSBinary() | ||
return exec(`${binaryPath} config Addresses.API /ip4/127.0.0.1/tcp/5001`) | ||
export function getApiMultiAddress () { | ||
return executeIPFSCommand(`config Addresses.API`) | ||
} | ||
|
||
/** | ||
|
@@ -127,8 +190,7 @@ export function setMultiAddrIPFSDaemon () { | |
* returns a promise | ||
*/ | ||
export function connectToCMD (strMultiddr) { | ||
const binaryPath = getPathIPFSBinary() | ||
return exec(`${binaryPath} swarm connect ${strMultiddr}`) | ||
return executeIPFSCommand(`swarm connect ${strMultiddr}`) | ||
} | ||
|
||
/** | ||
|
@@ -137,8 +199,7 @@ export function connectToCMD (strMultiddr) { | |
* returns a promise | ||
*/ | ||
export function addBootstrapAddr (strMultiddr) { | ||
const binaryPath = getPathIPFSBinary() | ||
return exec(`${binaryPath} bootstrap add ${strMultiddr}`) | ||
return executeIPFSCommand(`bootstrap add ${strMultiddr}`) | ||
} | ||
|
||
/** | ||
|
@@ -158,3 +219,22 @@ export function getSiderusPeers () { | |
return Promise.resolve(peers) | ||
}) | ||
} | ||
|
||
export function promiseRepoUnlocked (timeout = 30) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add docs here as well :) |
||
let iID // interval id | ||
let trial = 0 | ||
return new Promise((resolve, reject) => { | ||
iID = setInterval(() => { | ||
trial++ | ||
if (trial >= timeout) { | ||
clearInterval(iID) | ||
return reject('TIMEOUT') | ||
} | ||
|
||
return getApiMultiAddress().then(() => { | ||
clearInterval(iID) | ||
return resolve() | ||
}).catch(() => { }) // do nothing in case of errors | ||
}, 1000) // every second | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,14 @@ import './report' | |
import { | ||
startIPFSDaemon, | ||
ensuresIPFSInitialised, | ||
ensureAddressesConfigured, | ||
getSiderusPeers, | ||
connectToCMD, | ||
addBootstrapAddr | ||
addBootstrapAddr, | ||
promiseRepoUnlocked, | ||
getAPIVersion, | ||
setCustomBinaryPath, | ||
skipRepoPath | ||
} from './daemon' | ||
|
||
import { | ||
|
@@ -56,18 +61,57 @@ app.on('ready', () => { | |
}) | ||
// Set up crash reports. | ||
// Set up the needed stuff as the app launches. | ||
ensuresIPFSInitialised() | ||
.then(startIPFSDaemon) | ||
.then((process) => { | ||
console.log('IPFS Daemon started') | ||
global.IPFS_PROCESS = process | ||
loadingWindow.webContents.send('set-progress', { | ||
text: 'Initializing IPFS client...', | ||
percentage: 20 | ||
}) | ||
return Promise.resolve() | ||
}) | ||
|
||
getAPIVersion() | ||
.then(apiVersion => { | ||
let customPorts = false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't we move this in another function with a promise returning true/false based on the answer of the user? |
||
|
||
// An api is already available on port 5001 | ||
if (apiVersion !== null) { | ||
let alertMessage = 'An IPFS instance is already up!' | ||
alertMessage += '\n\nWould you like Orion to connect to the available node, instead of using its own?' | ||
|
||
if (apiVersion !== pjson.ipfsVersion) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice :) well done with this! |
||
alertMessage += `\n\nPlease note: Orion was design with IPFS ${pjson.ipfsVersion} in mind, ` | ||
alertMessage += `while the available API is running ${apiVersion}.` | ||
} | ||
|
||
const btnId = dialog.showMessageBox({ | ||
type: 'info', | ||
message: alertMessage, | ||
buttons: ['No', 'Yes'], | ||
cancelId: 0, | ||
defaultId: 1 | ||
}) | ||
if (btnId === 1) { | ||
// Use running node, skip starting the daemon | ||
// Set repository path to null so it's ignored and not passed as IPFS_PATH | ||
// Set binary path to `ipfs` to use the client's binary | ||
skipRepoPath() | ||
setCustomBinaryPath('ipfs') | ||
return Promise.resolve() | ||
} else { | ||
// Use our own daemon, but on different ports | ||
customPorts = true | ||
} | ||
} | ||
|
||
return ensuresIPFSInitialised() | ||
.then(() => ensureAddressesConfigured(customPorts)) | ||
.then(startIPFSDaemon) | ||
.then((process) => { | ||
console.log('IPFS Daemon: Starting') | ||
global.IPFS_PROCESS = process | ||
loadingWindow.webContents.send('set-progress', { | ||
text: 'Initializing the IPFS Daemon...', | ||
percentage: 20 | ||
}) | ||
return Promise.resolve() | ||
}) | ||
// Wait for the repo to be unlocked | ||
// (usually ipfs daemon needs some time before it releases the lock) | ||
.then(promiseRepoUnlocked) | ||
}) | ||
// Start the IPFS API Client | ||
.then(initIPFSClient) | ||
.then(client => { | ||
|
@@ -127,7 +171,14 @@ app.on('ready', () => { | |
}) | ||
// Catch errors | ||
.catch(err => { | ||
const message = typeof err === 'string' ? err : JSON.stringify(err) | ||
let message | ||
if (typeof err === 'string') { | ||
message = err | ||
} else if (err.message) { | ||
message = err.message | ||
} else { | ||
message = JSON.stringify(err) | ||
} | ||
dialog.showMessageBox({ type: 'warning', message }) | ||
app.quit() | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add some documentation to this method at least, as it will run cmd :D