diff --git a/gui/EasyDEX-GUI b/gui/EasyDEX-GUI index 7399c05..0092ac5 160000 --- a/gui/EasyDEX-GUI +++ b/gui/EasyDEX-GUI @@ -1 +1 @@ -Subproject commit 7399c05db6464596edf4c242f5a3a57445c70ded +Subproject commit 0092ac50871b6964e6462f3ba68ac7d1cd8c1421 diff --git a/gui/app-closing.html b/gui/app-closing.html index acbde0e..ec60212 100644 --- a/gui/app-closing.html +++ b/gui/app-closing.html @@ -20,6 +20,5 @@
App is closing. Please wait...
- \ No newline at end of file diff --git a/main.js b/main.js index f1c20f9..055ea6b 100644 --- a/main.js +++ b/main.js @@ -54,6 +54,7 @@ const appBasicInfo = { app.setName(appBasicInfo.name); app.setVersion(appBasicInfo.version); +shepherd.binFixRights(); shepherd.createIguanaDirs(); const appSessionHash = md5(Date.now()); @@ -75,45 +76,8 @@ shepherd.writeLog(`app started in ${(appConfig.dev ? 'dev mode' : ' user mode')} shepherd.setConfKMD(); -// kill rogue iguana copies on start if (appConfig.killIguanaOnStart) { - let iguanaGrep; - - switch (osPlatform) { - case 'darwin': - iguanaGrep = "ps -p $(ps -A | grep -m1 iguana | awk '{print $1}') | grep -i iguana"; - break; - case 'linux': - iguanaGrep = 'ps -p $(pidof iguana) | grep -i iguana'; - break; - case 'win32': - iguanaGrep = 'tasklist'; - break; - } - - exec(iguanaGrep, function(error, stdout, stderr) { - if (stdout.indexOf('iguana') > -1) { - const pkillCmd = osPlatform === 'win32' ? 'taskkill /f /im iguana.exe' : 'pkill -15 iguana'; - - console.log('found another iguana process(es)'); - shepherd.writeLog('found another iguana process(es)'); - - exec(pkillCmd, function(error, stdout, stderr) { - console.log(`${pkillCmd} is issued`); - shepherd.writeLog(`${pkillCmd} is issued`); - - if (error !== null) { - console.log(`${pkillCmd} exec error: ${error}`); - shepherd.writeLog(`${pkillCmd} exec error: ${error}`); - }; - }); - } - - if (error !== null) { - console.log(`${iguanaGrep} exec error: ${error}`); - shepherd.writeLog(`${iguanaGrep} exec error: ${error}`); - }; - }); + shepherd.killRogueProcess('iguana'); } guiapp.use(function(req, res, next) { @@ -126,8 +90,8 @@ guiapp.use(function(req, res, next) { }); // preload.js -const _setImmediate = setImmediate, - _clearImmediate = clearImmediate; +const _setImmediate = setImmediate; +const _clearImmediate = clearImmediate; process.once('loaded', () => { global.setImmediate = _setImmediate; @@ -150,10 +114,10 @@ process.once('loaded', () => { guiapp.use(bodyParser.json({ limit: '50mb' })); // support json encoded bodies guiapp.use(bodyParser.urlencoded({ limit: '50mb', - extended: true + extended: true, })); // support encoded bodies -guiapp.get('/', function (req, res) { +guiapp.get('/', function(req, res) { res.send('Agama app server'); }); @@ -168,6 +132,7 @@ let mainWindow; let loadingWindow; let appCloseWindow; let closeAppAfterLoading = false; +const _zcashParamsExist = shepherd.zcashParamsExist(); module.exports = guiapp; let iguanaIcon; @@ -251,7 +216,7 @@ function createLoadingWindow() { // loadingWindow.webContents.openDevTools() // if window closed we kill iguana proc - loadingWindow.on('hide', function () { + loadingWindow.on('hide', function() { // our app does not have multiwindow - so we dereference the window object instead of // putting them into an window_arr loadingWindow = null; @@ -337,10 +302,13 @@ function createWindow(status) { if (appConfig.v2) { shepherd.writeLog('show edex gui'); mainWindow.appConfig = appConfig; + mainWindow.appConfigSchema = shepherd.appConfigSchema; mainWindow.appBasicInfo = appBasicInfo; mainWindow.appSessionHash = appSessionHash; mainWindow.assetChainPorts = require('./routes/ports.js'); - mainWindow.zcashParamsExist = shepherd.zcashParamsExist(); + mainWindow.zcashParamsExist = _zcashParamsExist; + mainWindow.iguanaIcon = iguanaIcon; + mainWindow.testLocation = shepherd.testLocation; if (appConfig.dev) { mainWindow.loadURL('http://127.0.0.1:3000'); @@ -421,16 +389,16 @@ function createWindow(status) { shepherd.writeLog(result); resolve(result); - }, 2000) + }, 2000); }) } const HideMainWindow = function() { return new Promise(function(resolve, reject) { + const result = 'Hiding Main Window: done'; + console.log('Exiting App...'); mainWindow = null; - - const result = 'Hiding Main Window: done'; console.log(result); resolve(result); }); @@ -445,9 +413,10 @@ function createWindow(status) { const QuitApp = function() { return new Promise(function(resolve, reject) { + const result = 'Quiting App: done'; + KillPm2(); // required for normal app quit in iguana-less mode app.quit(); - const result = 'Quiting App: done'; console.log(result); resolve(result); }); @@ -468,13 +437,13 @@ function createWindow(status) { if (!Object.keys(shepherd.coindInstanceRegistry).length) { closeApp(); } else { + createAppCloseWindow(); shepherd.quitKomodod(1000); _appClosingInterval = setInterval(function() { if (!Object.keys(shepherd.coindInstanceRegistry).length) { closeApp(); } }, 1000); - createAppCloseWindow(); } } @@ -485,7 +454,7 @@ function createWindow(status) { } } -app.on('window-all-closed', function () { +app.on('window-all-closed', function() { //if (os.platform() !== 'win32') { ig.kill(); } // in osx apps stay active in menu bar until explictly closed or quitted by CMD Q // so we do not kill the app --> for the case user clicks again on the iguana icon @@ -497,7 +466,7 @@ app.on('window-all-closed', function () { // Emitted before the application starts closing its windows. // Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. -app.on('before-quit', function (event) { +app.on('before-quit', function(event) { console.log('before-quit'); if (mainWindow === null && loadingWindow != null) { // mainWindow not intitialised and loadingWindow not dereferenced // loading window is still open @@ -512,7 +481,7 @@ app.on('before-quit', function (event) { // Emitted when all windows have been closed and the application will quit. // Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. -app.on('will-quit', function (event) { +app.on('will-quit', function(event) { if (mainWindow === null && loadingWindow != null) { // loading window is still open console.log('will-quit while loading window active'); @@ -522,37 +491,38 @@ app.on('will-quit', function (event) { // Emitted when the application is quitting. // Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. -app.on('quit', function (event) { +app.on('quit', function(event) { if (mainWindow === null && loadingWindow != null) { console.log('quit while loading window active'); event.preventDefault(); } }) -app.on('activate', function () { +app.on('activate', function() { if (mainWindow === null) { // createWindow('open'); } }); function formatBytes(bytes, decimals) { - if (bytes === 0) + if (bytes === 0) { return '0 Bytes'; - - const k = 1000, - dm = decimals + 1 || 3, - sizes = [ - 'Bytes', - 'KB', - 'MB', - 'GB', - 'TB', - 'PB', - 'EB', - 'ZB', - 'YB' - ], - i = Math.floor(Math.log(bytes) / Math.log(k)); + } + + const k = 1000; + const dm = decimals + 1 || 3; + const sizes = [ + 'Bytes', + 'KB', + 'MB', + 'GB', + 'TB', + 'PB', + 'EB', + 'ZB', + 'YB' + ]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; } \ No newline at end of file diff --git a/make-patch.sh b/make-patch.sh index 978f7a8..25a98fa 100755 --- a/make-patch.sh +++ b/make-patch.sh @@ -17,6 +17,7 @@ mkdir patch/gui/EasyDEX-GUI/react cp -R gui/EasyDEX-GUI/react/build patch/gui/EasyDEX-GUI/react/build cp -R gui/EasyDEX-GUI/assets patch/gui/EasyDEX-GUI cp gui/index.html patch/gui/index.html +cp gui/app-closing.html patch/gui/app-closing.html cp ./main.js patch cp ./version patch cp -R routes patch/routes diff --git a/private/mainmenu.js b/private/mainmenu.js index 07cbbdf..dd8780d 100644 --- a/private/mainmenu.js +++ b/private/mainmenu.js @@ -1,7 +1,7 @@ -const {Menu} = require('electron'), - electron = require('electron'), - app = electron.app, - {shell} = require('electron'); +const {Menu} = require('electron'); +const electron = require('electron'); +const app = electron.app; +const {shell} = require('electron'); const template = [ { @@ -86,7 +86,7 @@ const template = [ } ] }, - { + /*{ role: 'help', label: 'Support', submenu: [ @@ -121,7 +121,7 @@ const template = [ } } ] - } + }*/ ] if (process.platform === 'darwin') { diff --git a/routes/appConfig.js b/routes/appConfig.js new file mode 100644 index 0000000..b0f4422 --- /dev/null +++ b/routes/appConfig.js @@ -0,0 +1,152 @@ +const appConfig = { + config: { + edexGuiOnly: true, + iguanaGuiOnly: false, + manualIguanaStart: false, + skipBasiliskNetworkCheck: true, + minNotaries: 8, + host: '127.0.0.1', + agamaPort: 17777, + iguanaCorePort: 7778, + maxDescriptors: { + darwin: 90000, + linux: 1000000, + }, + killIguanaOnStart: true, + dev: false, + v2: true, + useBasiliskInstance: true, + debug: false, + cli: { + passthru: true, + default: true, + }, + iguanaLessMode: true, + roundValues: false, + dataDir: '', + }, + schema: { + edexGuiOnly: { + display: false, + type: 'boolean', + displayName: 'EDEX GUI only', + }, + iguanaGuiOnly: { + display: false, + type: 'boolean', + displayName: 'Iguana GUI only', + }, + manualIguanaStart: { + display: false, + type: 'boolean', + displayName: 'Manual Iguana Start', + }, + skipBasiliskNetworkCheck: { + display: false, + type: 'boolean', + displayName: 'Skip Basilisk Network Check', + }, + minNotaries: { + display: false, + type: 'number', + displayName: 'Minimum notaries count', + info: 'Minimum number of notaries to connect to on startup', + }, + host: { + display: true, + type: 'string', + displayName: 'Hostname', + info: 'Application hostname', + }, + agamaPort: { + display: true, + type: 'number', + displayName: 'Agama Port', + info: 'Agama HTTP port. Required to run GUI.', + }, + iguanaCorePort: { + display: true, + type: 'number', + displayName: 'Iguana Core Port', + info: 'Default Iguana Core Port. Change it if you have conflicts with other applications.', + }, + maxDescriptors: { + display: false, + displayName: 'Max Descriptors per Process', + darwin: { + display: true, + displayName: 'MacOS (Darwin)', + type: 'number', + }, + linux: { + display: true, + displayName: 'Linux', + type: 'number', + }, + }, + killIguanaOnStart: { + display: true, + displayName: 'Kill Iguana Core Processes on Startup', + info: 'Kill any rogue Iguana Core processes during app startup', + type: 'boolean', + }, + dev: { + display: true, + displayName: 'Developer mode', + info: 'Enable developer mode.', + type: 'boolean', + }, + v2: { + display: false, + type: 'boolean', + }, + useBasiliskInstance: { + display: true, + displayName: 'Iguana Core Basilisk Instance', + info: 'Enable dedicated Iguana Core instance to handle all Basilisk network requests', + type: 'boolean', + }, + debug: { + display: true, + displayName: 'Debug', + info: 'Enable debug output', + type: 'boolean', + }, + cli: { + display: true, + displayName: 'Direct BitcoinRPC passthru interface', + info: 'Enable direct BitcoinRPC passthru interface. It will bypass Iguana Core and send requests directly to Bitcoin JSON server.', + passthru: { + display: true, + displayName: 'Enable Direct Passthru', + type: 'boolean', + }, + default: { + display: true, + displayName: 'Enable CLI passthru', + info: 'Enable komodo-cli passthru. This allows you to send CLI compatible commands directly from UI to komodo-cli.', + type: 'boolean', + }, + }, + iguanaLessMode: { + display: true, + displayName: 'Enable Native Only mode', + info: 'Limited to only Komodo native mode to speed up loading and reduce hardware resources consumption.', + type: 'boolean', + }, + roundValues: { + display: true, + displayName: 'Enable amount rounding', + info: 'Round \"dust\" amounts to save screen space.', + type: 'boolean', + }, + dataDir: { + display: true, + displayName: 'Komodo data directory', + info: 'The data directory is the location where Komodo data files are stored, including the wallet data file.', + type: 'folder', + }, + }, +}; + +module.exports = appConfig; \ No newline at end of file diff --git a/routes/cache.js b/routes/cache.js index ec0ca7d..91379ae 100644 --- a/routes/cache.js +++ b/routes/cache.js @@ -1,6 +1,6 @@ -const fs = require('fs-extra'), - request = require('request'), - async = require('async'); +const fs = require('fs-extra'); +const request = require('request'); +const async = require('async'); var cache = {}; var inMemCache; @@ -33,18 +33,18 @@ cache.get = function(req, res, next) { fs.readFile(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`, 'utf8', function (err, data) { if (err) { const errorObj = { - 'msg': 'error', - 'result': err + msg: 'error', + result: err, }; res.end(JSON.stringify(errorObj)); - } else { + } else { // deprecated try { - const parsedJSON = JSON.parse(data), - successObj = { - 'msg': 'success', - 'result': parsedJSON - }; + const parsedJSON = JSON.parse(data); + const successObj = { + msg: 'success', + result: parsedJSON, + }; inMemCache = parsedJSON; res.end(JSON.stringify(successObj)); @@ -59,8 +59,8 @@ cache.get = function(req, res, next) { fs.writeFile(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`, data.substring(0, errorPos[errorPos.length - 1]), function(err) { const successObj = { - 'msg': 'success', - 'result': data.substring(0, errorPos[errorPos.length - 1]) + msg: 'success', + result: data.substring(0, errorPos[errorPos.length - 1]), }; inMemCache = JSON.parse(data.substring(0, errorPos[errorPos.length - 1])); @@ -72,24 +72,24 @@ cache.get = function(req, res, next) { }); } else { const errorObj = { - 'msg': 'error', - 'result': `no file with handle ${pubkey}` + msg: 'error', + result: `no file with handle ${pubkey}`, }; res.end(JSON.stringify(errorObj)); } } else { const successObj = { - 'msg': 'success', - 'result': inMemCache + msg: 'success', + result: inMemCache, }; res.end(JSON.stringify(successObj)); } } else { const errorObj = { - 'msg': 'error', - 'result': 'no pubkey provided' + msg: 'error', + result: 'no pubkey provided', }; res.end(JSON.stringify(errorObj)); @@ -104,15 +104,15 @@ cache.groomGet = function(req, res, next) { fs.readFile(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`, 'utf8', function (err, data) { if (err) { const errorObj = { - 'msg': 'error', - 'result': err + msg: 'error', + result: err, }; res.end(JSON.stringify(errorObj)); } else { const successObj = { - 'msg': 'success', - 'result': data ? JSON.parse(data) : '' + msg: 'success', + result: data ? JSON.parse(data) : '', }; res.end(JSON.stringify(successObj)); @@ -120,16 +120,16 @@ cache.groomGet = function(req, res, next) { }); } else { const errorObj = { - 'msg': 'error', - 'result': `no file with name ${_filename}` + msg: 'error', + result: `no file with name ${_filename}`, }; res.end(JSON.stringify(errorObj)); } } else { const errorObj = { - 'msg': 'error', - 'result': 'no file name provided' + msg: 'error', + result: 'no file name provided', }; res.end(JSON.stringify(errorObj)); @@ -146,15 +146,15 @@ cache.groomDelete = function(req, res, next) { fs.unlink(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`, function(err) { if (err) { const errorObj = { - 'msg': 'error', - 'result': err + msg: 'error', + result: err, }; res.end(JSON.stringify(errorObj)); } else { const successObj = { - 'msg': 'success', - 'result': 'deleted' + msg: 'success', + result: 'deleted', }; res.end(JSON.stringify(successObj)); @@ -162,16 +162,16 @@ cache.groomDelete = function(req, res, next) { }); } else { const errorObj = { - 'msg': 'error', - 'result': `no file with name ${_filename}` + msg: 'error', + result: `no file with name ${_filename}`, }; res.end(JSON.stringify(errorObj)); } } else { const errorObj = { - 'msg': 'error', - 'result': 'no file name provided' + msg: 'error', + result: 'no file name provided', }; res.end(JSON.stringify(errorObj)); @@ -179,8 +179,8 @@ cache.groomDelete = function(req, res, next) { } cache.groomPost = function(req, res) { - const _filename = req.body.filename, - _payload = req.body.payload; + const _filename = req.body.filename; + const _payload = req.body.payload; if (!cacheCallInProgress) { cacheCallInProgress = true; @@ -188,8 +188,8 @@ cache.groomPost = function(req, res) { if (_filename) { if (!_payload) { const errorObj = { - 'msg': 'error', - 'result': 'no payload provided' + msg: 'error', + result: 'no payload provided', }; res.end(JSON.stringify(errorObj)); @@ -201,16 +201,16 @@ cache.groomPost = function(req, res) { fs.writeFile(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`, _payload, function (err) { if (err) { const errorObj = { - 'msg': 'error', - 'result': err + msg: 'error', + result: err, }; cacheCallInProgress = false; res.end(JSON.stringify(errorObj)); } else { const successObj = { - 'msg': 'success', - 'result': 'done' + msg: 'success', + result: 'done', }; cacheCallInProgress = false; @@ -220,33 +220,33 @@ cache.groomPost = function(req, res) { } } else { const errorObj = { - 'msg': 'error', - 'result': 'no file name provided' + msg: 'error', + result: 'no file name provided', }; res.end(JSON.stringify(errorObj)); } } else { const errorObj = { - 'msg': 'error', - 'result': 'another job is in progress' + msg: 'error', + result: 'another job is in progress', }; res.end(JSON.stringify(errorObj)); } } -var cacheCallInProgress = false, - cacheGlobLifetime = 600; // sec +var cacheCallInProgress = false; +const cacheGlobLifetime = 600; // sec // TODO: reset calls' states on new /cache call start var mock = require('./mock'); -var callStack = {}, -checkCallStack = function() { - var total = 0; +var callStack = {}; +const checkCallStack = function() { + let total = 0; - for (var coin in callStack) { + for (let coin in callStack) { total =+ callStack[coin]; } @@ -254,13 +254,13 @@ checkCallStack = function() { cache.dumpCacheBeforeExit(); cacheCallInProgress = false; cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'done', - 'resp': 'success' - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'done', + resp: 'success', + }, + }, }); } }; @@ -284,45 +284,45 @@ cache.one = function(req, res, next) { if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${req.query.pubkey}.json`)) { let _data = fs.readFileSync(`${cache.iguanaDir}/shepherd/cache-${req.query.pubkey}.json`, 'utf8'); + if (_data) { - inMemCache = JSON.parse(_data); - _data = _data.replace('waiting', 'failed'); - cache.dumpCacheBeforeExit(); + inMemCache = JSON.parse(_data); + _data = _data.replace('waiting', 'failed'); + cache.dumpCacheBeforeExit(); } } // TODO: add check to allow only one cache call/sequence in progress cacheCallInProgress = true; - var sessionKey = req.query.userpass, - coin = req.query.coin, - address = req.query.address, - addresses = req.query.addresses && req.query.addresses.indexOf(':') > -1 ? req.query.addresses.split(':') : null, - pubkey = req.query.pubkey, - mock = req.query.mock, - skipTimeout = req.query.skip, - callsArray = req.query.calls.split(':'), - iguanaCorePort = req.query.port ? req.query.port : cache.appConfig.iguanaCorePort, - errorObj = { - 'msg': 'error', - 'result': 'error' - }, - outObj = {}, - pubkey, - writeCache = function(timeStamp) { - if (timeStamp) { - outObj.timestamp = timeStamp; - } + let sessionKey = req.query.userpass; + let coin = req.query.coin; + let address = req.query.address; + let addresses = req.query.addresses && req.query.addresses.indexOf(':') > -1 ? req.query.addresses.split(':') : null; + let pubkey = req.query.pubkey; + let mock = req.query.mock; + let skipTimeout = req.query.skip; + let callsArray = req.query.calls.split(':'); + let iguanaCorePort = req.query.port ? req.query.port : cache.appConfig.iguanaCorePort; + let errorObj = { + msg: 'error', + result: 'error', + }; + let outObj = {}; + const writeCache = function(timeStamp) { + if (timeStamp) { + outObj.timestamp = timeStamp; + } - inMemCache = outObj; - }, - checkTimestamp = function(dateToCheck) { - var currentEpochTime = new Date(Date.now()) / 1000, - secondsElapsed = Number(currentEpochTime) - Number(dateToCheck / 1000); + inMemCache = outObj; + }; + const checkTimestamp = function(dateToCheck) { + const currentEpochTime = new Date(Date.now()) / 1000; + const secondsElapsed = Number(currentEpochTime) - Number(dateToCheck / 1000); - return Math.floor(secondsElapsed); - }, - internalError = false; + return Math.floor(secondsElapsed); + }; + let internalError = false; inMemPubkey = pubkey; callStack[coin] = 1; @@ -331,8 +331,8 @@ cache.one = function(req, res, next) { if (!sessionKey) { const errorObj = { - 'msg': 'error', - 'result': 'no session key provided' + msg: 'error', + result: 'no session key provided', }; res.end(JSON.stringify(errorObj)); @@ -341,8 +341,8 @@ cache.one = function(req, res, next) { if (!pubkey) { const errorObj = { - 'msg': 'error', - 'result': 'no pubkey provided' + msg: 'error', + result: 'no pubkey provided', }; res.end(JSON.stringify(errorObj)); @@ -352,7 +352,8 @@ cache.one = function(req, res, next) { console.log('cache-one call started'); function fixJSON(data) { - if (data && data.length) { + if (data && + data.length) { try { const parsedJSON = JSON.parse(data); @@ -375,12 +376,14 @@ cache.one = function(req, res, next) { } } - if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`) && coin !== 'all') { + if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`) && + coin !== 'all') { if (inMemCache) { console.log('cache one from mem'); outObj = inMemCache; } else { const _file = fs.readFileSync(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`, 'utf8'); + console.log('cache one from disk'); outObj = fixJSON(_file); } @@ -402,28 +405,28 @@ cache.one = function(req, res, next) { } res.end(JSON.stringify({ - 'msg': 'success', - 'result': 'call is initiated' + msg: 'success', + result: 'call is initiated', })); if (!internalError) { cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress' - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + }, + }, }); function execDEXRequests(coin, address) { let dexUrls = { - 'listunspent': `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/listunspent?userpass=${sessionKey}&symbol=${coin}&address=${address}`, - 'listtransactions': `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/listtransactions?userpass=${sessionKey}&count=100&skip=0&symbol=${coin}&address=${address}`, - 'getbalance': `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/getbalance?userpass=${sessionKey}&symbol=${coin}&address=${address}`, - 'refresh': `http://${cache.appConfig.host}:${iguanaCorePort}/api/basilisk/refresh?userpass=${sessionKey}&symbol=${coin}&address=${address}` - }, - _dexUrls = {}; + listunspent: `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/listunspent?userpass=${sessionKey}&symbol=${coin}&address=${address}`, + listtransactions: `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/listtransactions?userpass=${sessionKey}&count=100&skip=0&symbol=${coin}&address=${address}`, + getbalance: `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/getbalance?userpass=${sessionKey}&symbol=${coin}&address=${address}`, + refresh: `http://${cache.appConfig.host}:${iguanaCorePort}/api/basilisk/refresh?userpass=${sessionKey}&symbol=${coin}&address=${address}` + }; + let _dexUrls = {}; for (let a = 0; a < callsArray.length; a++) { _dexUrls[callsArray[a]] = dexUrls[callsArray[a]]; @@ -455,41 +458,42 @@ cache.one = function(req, res, next) { async.forEachOf(_dexUrls, function(dexUrl, key) { var tooEarly = false; + if (outObj.basilisk[coin][address][key] && outObj.basilisk[coin][address][key].timestamp && (!skipTimeout && checkTimestamp(outObj.basilisk[coin][address][key].timestamp) < cacheGlobLifetime)) { tooEarly = true; outObj.basilisk[coin][address][key].status = 'done'; cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'method': key, - 'coin': coin, - 'address': address, - 'status': 'done', - 'resp': 'too early' - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + method: key, + coin: coin, + address: address, + status: 'done', + resp: 'too early', + }, + }, + }, }); } if (!tooEarly) { cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'method': key, - 'coin': coin, - 'address': address, - 'status': 'in progress' - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + method: key, + coin: coin, + address: address, + status: 'in progress', + }, + }, + }, }); outObj.basilisk[coin][address][key].status = 'in progress'; request({ @@ -500,25 +504,25 @@ cache.one = function(req, res, next) { response.statusCode && response.statusCode === 200) { cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'method': key, - 'coin': coin, - 'address': address, - 'status': 'done', - 'resp': body - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + method: key, + coin: coin, + address: address, + status: 'done', + resp: body, + }, + }, + }, }); // basilisk balance fallback const _parsedJSON = JSON.parse(body); if (key === 'getbalance' && - coin === 'KMD'/* && + coin === 'KMD'/* && ((_parsedJSON && _parsedJSON.balance === 0) || body === [])*/) { console.log('fallback to kmd explorer ======>'); request({ @@ -559,15 +563,15 @@ cache.one = function(req, res, next) { callStack[coin]--; console.log(`${coin} _stack len ${callStack[coin]}`); cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'currentStackLength': callStack[coin] - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + currentStackLength: callStack[coin], + }, + }, + }, }); checkCallStack(); @@ -583,15 +587,15 @@ cache.one = function(req, res, next) { callStack[coin]--; console.log(`${coin} _stack len ${callStack[coin]}`); cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'currentStackLength': callStack[coin] - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + currentStackLength: callStack[coin], + }, + }, + }, }); checkCallStack(); writeCache(); @@ -602,15 +606,15 @@ cache.one = function(req, res, next) { callStack[coin]--; console.log(`${coin} _stack len ${callStack[coin]}`); cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'currentStackLength': callStack[coin] - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + currentStackLength: callStack[coin], + }, + }, + }, }); checkCallStack(); } @@ -619,18 +623,18 @@ cache.one = function(req, res, next) { function parseAddresses(coin, addrArray) { cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'method': 'getaddressesbyaccount', - 'coin': coin, - 'status': 'done', - 'resp': addrArray - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + method: 'getaddressesbyaccount', + coin: coin, + status: 'done', + resp: addrArray, + }, + }, + }, }); outObj.basilisk[coin].addresses = addrArray; console.log(addrArray); @@ -649,15 +653,15 @@ cache.one = function(req, res, next) { console.log(`${coin} stack len ${callStack[coin]}`); cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'totalStackLength': callStack[coin] - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + totalStackLength: callStack[coin], + }, + }, + }, }); async.each(outObj.basilisk[coin].addresses, function(address) { execDEXRequests(coin, address); @@ -687,17 +691,17 @@ cache.one = function(req, res, next) { // update all available coin addresses if (!address) { cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'method': 'getaddressesbyaccount', - 'coin': coin, - 'status': 'in progress' - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + method: 'getaddressesbyaccount', + coin: coin, + status: 'in progress', + }, + }, + }, }); if (coin === 'all') { @@ -711,17 +715,17 @@ cache.one = function(req, res, next) { response.statusCode === 200) { console.log(JSON.parse(body).basilisk); cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'method': 'allcoins', - 'status': 'done', - 'resp': body - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + method: 'allcoins', + status: 'done', + resp: body, + }, + }, + }, }); body = JSON.parse(body); // basilisk coins @@ -737,17 +741,17 @@ cache.one = function(req, res, next) { writeCache(); cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'method': 'getaddressesbyaccount', - 'coin': coin, - 'status': 'in progress' - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + method: 'getaddressesbyaccount', + coin: coin, + status: 'in progress', + }, + }, + }, }); getAddresses(coin); @@ -776,36 +780,36 @@ cache.one = function(req, res, next) { console.log(`${coin} stack len ${callStack[coin]}`); cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-one', - 'status': 'in progress', - 'iguanaAPI': { - 'totalStackLength': callStack[coin], - 'currentStackLength': callStack[coin] - } - } - } + message: { + shepherd: { + method: 'cache-one', + status: 'in progress', + iguanaAPI: { + totalStackLength: callStack[coin], + currentStackLength: callStack[coin], + }, + }, + }, }); execDEXRequests(coin, address); } } else { cache.io.emit('messages', { - 'message': { - 'shepherd': { - 'method': 'cache-all', - 'status': 'done', - 'resp': 'internal error' - } - } + message: { + shepherd: { + method: 'cache-all', + status: 'done', + resp: 'internal error', + }, + }, }); cacheCallInProgress = false; } } else { res.end(JSON.stringify({ - 'msg': 'error', - 'result': 'another call is in progress already' + msg: 'error', + result: 'another call is in progress already', })); } }; diff --git a/routes/shepherd.js b/routes/shepherd.js index 4060c7c..ba81c5d 100644 --- a/routes/shepherd.js +++ b/routes/shepherd.js @@ -17,14 +17,18 @@ const electron = require('electron'), async = require('async'), rimraf = require('rimraf'), portscanner = require('portscanner'), + aes256 = require('nodejs-aes256'), AdmZip = require('adm-zip'), remoteFileSize = require('remote-file-size'), - Promise = require('bluebird'); + Promise = require('bluebird'), + {shell} = require('electron'), + { execFile } = require('child_process'); const fixPath = require('fix-path'); var ps = require('ps-node'), setconf = require('../private/setconf.js'), assetChainPorts = require('./ports.js'), + _appConfig = require('./appConfig.js'), shepherd = express.Router(), iguanaInstanceRegistry = {}, coindInstanceRegistry = {}, @@ -33,9 +37,10 @@ var ps = require('ps-node'), guiLog = {}, rpcConf = {}; +shepherd.appConfig = _appConfig.config; + // IGUANA FILES AND CONFIG SETTINGS -var iguanaConfsDirSrc = path.join(__dirname, '../assets/deps/confs'), - CorsProxyBin = path.join(__dirname, '../node_modules/corsproxy/bin/corsproxy'); +var iguanaConfsDirSrc = path.join(__dirname, '../assets/deps/confs'); // SETTING OS DIR TO RUN IGUANA FROM // SETTING APP ICON FOR LINUX AND WINDOWS @@ -44,12 +49,13 @@ if (os.platform() === 'darwin') { var iguanaBin = path.join(__dirname, '../assets/artifacts.supernet.org/latest/osx/iguana'), iguanaDir = `${process.env.HOME}/Library/Application Support/iguana`, iguanaConfsDir = `${iguanaDir}/confs`, - komododBin = path.join(__dirname, '../assets/artifacts.supernet.org/latest/osx/komodod'), - komodocliBin = path.join(__dirname, '../assets/artifacts.supernet.org/latest/osx/komodo-cli'), - komodoDir = `${process.env.HOME}/Library/Application Support/Komodo`, + komododBin = path.join(__dirname, '../assets/bin/osx/komodod'), + komodocliBin = path.join(__dirname, '../assets/bin/osx/komodo-cli'), + komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`, zcashdBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcashd', zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli', - zcashDir = `${process.env.HOME}/Library/Application Support/ZcashParams`; + zcashDir = `${process.env.HOME}/Library/Application Support/Zcash`, + zcashParamsDir = `${process.env.HOME}/Library/Application Support/ZcashParams`; } if (os.platform() === 'linux') { @@ -57,10 +63,10 @@ if (os.platform() === 'linux') { iguanaDir = `${process.env.HOME}/.iguana`, iguanaConfsDir = `${iguanaDir}/confs`, iguanaIcon = path.join(__dirname, '/assets/icons/agama_icons/128x128.png'), - komododBin = path.join(__dirname, '../assets/artifacts.supernet.org/latest/linux/komodod'), - komodocliBin = path.join(__dirname, '../assets/artifacts.supernet.org/latest/linux/komodo-cli'), - komodoDir = `${process.env.HOME}/.komodo`; - zcashDir = `${process.env.HOME}/.zcash-params`; + komododBin = path.join(__dirname, '../assets/bin/linux64/komodod'), + komodocliBin = path.join(__dirname, '../assets/bin/linux64/komodo-cli'), + komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`, + zcashParamsDir = `${process.env.HOME}/.zcash-params`; } if (os.platform() === 'win32') { @@ -76,46 +82,107 @@ if (os.platform() === 'win32') { komododBin = path.normalize(komododBin), komodocliBin = path.join(__dirname, '../assets/artifacts.supernet.org/latest/windows/komodo-cli.exe'), komodocliBin = path.normalize(komodocliBin), - komodoDir = `${process.env.APPDATA}/Komodo`, + komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`, komodoDir = path.normalize(komodoDir); - zcashDir = `${process.env.APPDATA}/ZcashParams`; - zcashDir = path.normalize(zcashDir); + zcashParamsDir = `${process.env.APPDATA}/ZcashParams`; + zcashParamsDir = path.normalize(zcashParamsDir); } -shepherd.appConfig = { - "edexGuiOnly": true, - "iguanaGuiOnly": false, - "manualIguanaStart": false, - "skipBasiliskNetworkCheck": true, - "minNotaries": 8, - "host": "127.0.0.1", - "agamaPort": 17777, - "iguanaCorePort": 7778, - "maxDescriptors": { - "darwin": 90000, - "linux": 1000000 - }, - "killIguanaOnStart": true, - "dev": false, - "v2": true, - "useBasiliskInstance": true, - "debug": false, - "cli": { - "passthru": true, - "default": true - }, - "iguanaLessMode": true, - "roundValues": false, -}; - +shepherd.appConfigSchema = _appConfig.schema; shepherd.defaultAppConfig = Object.assign({}, shepherd.appConfig); shepherd.coindInstanceRegistry = coindInstanceRegistry; +shepherd.testLocation = function(path) { + return new Promise(function(resolve, reject) { + fs.lstat(path, (err, stats) => { + if (err) { + console.log('error testing path ' + path); + resolve(-1); + } else { + if (stats.isDirectory()) { + resolve(true); + } else { + console.log('error testing path ' + path + ' not a folder'); + resolve(false); + } + } + }); + }); +} + +// osx and linux +shepherd.binFixRights = function() { + const osPlatform = os.platform(); + const _bins = [ + iguanaBin, + komododBin, + komodocliBin + ]; + + if (osPlatform === 'darwin' || + osPlatform === 'linux') { + for (let i = 0; i < _bins.length; i++) { + _fs.stat(_bins[i], function(err, stat) { + if (!err) { + if (parseInt(stat.mode.toString(8), 10) !== 100775) { + console.log(`${_bins[i]} fix permissions`); + fsnode.chmodSync(_bins[i], '0775'); + } + } else { + console.log(`error: ${_bins[i]} not found`); + } + }); + } + } +} + +shepherd.killRogueProcess = function(processName) { + // kill rogue process copies on start + let processGrep; + const osPlatform = os.platform(); + + switch (osPlatform) { + case 'darwin': + processGrep = "ps -p $(ps -A | grep -m1 " + processName + " | awk '{print $1}') | grep -i " + processName; + break; + case 'linux': + processGrep = 'ps -p $(pidof ' + processName + ') | grep -i ' + processName; + break; + case 'win32': + processGrep = 'tasklist'; + break; + } + + exec(processGrep, function(error, stdout, stderr) { + if (stdout.indexOf(processName) > -1) { + const pkillCmd = osPlatform === 'win32' ? 'taskkill /f /im ' + processName + '.exe' : 'pkill -15 ' + processName; + + console.log(`found another ${processName} process(es)`); + shepherd.writeLog(`found another ${processName} process(es)`); + + exec(pkillCmd, function(error, stdout, stderr) { + console.log(`${pkillCmd} is issued`); + shepherd.writeLog(`${pkillCmd} is issued`); + + if (error !== null) { + console.log(`${pkillCmd} exec error: ${error}`); + shepherd.writeLog(`${pkillCmd} exec error: ${error}`); + }; + }); + } + + if (error !== null) { + console.log(`${processGrep} exec error: ${error}`); + shepherd.writeLog(`${processGrep} exec error: ${error}`); + }; + }); +} + shepherd.zcashParamsExist = function() { - if (fs.existsSync(zcashDir) && - fs.existsSync(`${zcashDir}/sprout-proving.key`) && - fs.existsSync(`${zcashDir}/sprout-verifying.key`)) { + if (_fs.existsSync(zcashParamsDir) && + _fs.existsSync(`${zcashParamsDir}/sprout-proving.key`) && + _fs.existsSync(`${zcashParamsDir}/sprout-verifying.key`)) { console.log('zcashparams exist'); return true; } @@ -127,7 +194,7 @@ shepherd.zcashParamsExist = function() { shepherd.readVersionFile = function() { // read app version const rootLocation = path.join(__dirname, '../'); - const localVersionFile = fs.readFileSync(rootLocation + 'version', 'utf8'); + const localVersionFile = fs.readFileSync(`${rootLocation}version`, 'utf8'); return localVersionFile; } @@ -168,15 +235,168 @@ shepherd.createIguanaDirs = function() { if (!fs.existsSync(`${iguanaDir}/shepherd`)) { fs.mkdirSync(`${iguanaDir}/shepherd`); - if (fs.existsSync(iguanaDir)) { + if (fs.existsSync(`${iguanaDir}/shepherd`)) { console.log(`created shepherd folder at ${iguanaDir}/shepherd`); shepherd.writeLog(`create shepherd folder at ${iguanaDir}/shepherd`); } } else { - console.log('shepherd folder already exists'); + console.log('iguana/shepherd folder already exists'); + } + + if (!fs.existsSync(`${iguanaDir}/shepherd/pin`)) { + fs.mkdirSync(`${iguanaDir}/shepherd/pin`); + + if (fs.existsSync(`${iguanaDir}/shepherd/pin`)) { + console.log(`created pin folder at ${iguanaDir}/shepherd/pin`); + shepherd.writeLog(`create pin folder at ${iguanaDir}/shepherd/pin`); + } + } else { + console.log('shepherd/pin folder already exists'); } } +/* + * type: POST + * params: none + */ +shepherd.post('/encryptkey', function(req, res, next) { + if (req.body.key && + req.body.string && + req.body.pubkey) { + const encryptedString = aes256.encrypt(req.body.key, req.body.string); + + // test pin security + // - at least 1 char in upper case + // - at least 1 digit + // - at least one special character + // - min length 8 + + const _pin = req.body.key; + const _pinTest = _pin.match('^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{8}$'); + + fs.writeFile(`${iguanaDir}/shepherd/pin/${req.body.pubkey}.pin`, encryptedString, function (err) { + if (err) { + console.log('error writing pin file'); + } + + const returnObj = { + msg: 'success', + result: encryptedString, + }; + + res.end(JSON.stringify(returnObj)); + }); + } else { + let errorObj = { + msg: 'error', + result: '', + }; + const _paramsList = [ + 'key', + 'string', + 'pubkey' + ]; + let _errorParamsList = []; + + for (let i = 0; i < _paramsList.length; i++) { + if (!req.query[_paramsList[i]]) { + _errorParamsList.push(_paramsList[i]); + } + } + + errorObj.result = `missing param ${_errorParamsList.join(', ')}`; + res.end(JSON.stringify(errorObj)); + } +}); + +shepherd.post('/decryptkey', function(req, res, next) { + if (req.body.key && + req.body.pubkey) { + if (fs.existsSync(`${iguanaDir}/shepherd/pin/${req.body.pubkey}.pin`)) { + fs.readFile(`${iguanaDir}/shepherd/pin/${req.body.pubkey}.pin`, 'utf8', function (err, data) { + if (err) { + const errorObj = { + msg: 'error', + result: err, + }; + + res.end(JSON.stringify(errorObj)); + } else { + const encryptedKey = aes256.decrypt(req.body.key, data); + // test if stored encrypted passphrase is decrypted correctly + // if not then the key is wrong + const _regexTest = encryptedKey.match(/^[0-9a-zA-Z ]+$/g); + let returnObj; + + if (!_regexTest) { + returnObj = { + msg: 'error', + result: 'wrong key', + }; + } else { + returnObj = { + msg: 'success', + result: encryptedKey, + }; + } + + res.end(JSON.stringify(returnObj)); + } + }); + } else { + const errorObj = { + msg: 'error', + result: `file ${req.query.pubkey}.pin doesnt exist`, + }; + + res.end(JSON.stringify(errorObj)); + } + } else { + const errorObj = { + msg: 'error', + result: 'missing key or pubkey param', + }; + + res.end(JSON.stringify(errorObj)); + } +}); + +shepherd.get('/getpinlist', function(req, res, next) { + if (fs.existsSync(`${iguanaDir}/shepherd/pin`)) { + fs.readdir(`${iguanaDir}/shepherd/pin`, function(err, items) { + let _pins = []; + + for (let i = 0; i < items.length; i++) { + if (items[i].substr(items[i].length - 4, 4) === '.pin') { + _pins.push(items[i].substr(0, items[i].length - 4)); + } + } + + if (!items.length) { + const errorObj = { + msg: 'error', + result: 'no pins', + }; + + res.end(JSON.stringify(errorObj)); + } else { + const successObj = { + msg: 'success', + result: _pins, + }; + + res.end(JSON.stringify(successObj)); + } + }); + } else { + const errorObj = { + msg: 'error', + result: 'pin folder doesnt exist', + }; + + res.end(JSON.stringify(errorObj)); + } +}); /** * Promise based download file method */ @@ -192,7 +412,7 @@ function downloadFile(configuration) { agentOptions: { keepAlive: true, keepAliveMsecs: 15000, - } + }, }); let out = fs.createWriteStream(configuration.localFile); @@ -224,17 +444,17 @@ function downloadFile(configuration) { } const remoteBinLocation = { - 'win32': 'https://artifacts.supernet.org/latest/windows/', - 'darwin': 'https://artifacts.supernet.org/latest/osx/', - 'linux': 'https://artifacts.supernet.org/latest/linux/', + win32: 'https://artifacts.supernet.org/latest/windows/', + darwin: 'https://artifacts.supernet.org/latest/osx/', + linux: 'https://artifacts.supernet.org/latest/linux/', }; const localBinLocation = { - 'win32': 'assets/bin/win64/', - 'darwin': 'assets/bin/osx/', - 'linux': 'assets/bin/linux64/', + win32: 'assets/bin/win64/', + darwin: 'assets/bin/osx/', + linux: 'assets/bin/linux64/', }; const latestBins = { - 'win32': [ + win32: [ 'iguana.exe', 'komodo-cli.exe', 'komodod.exe', @@ -248,7 +468,7 @@ const latestBins = { 'nanomsg.dll', 'pthreadvc2.dll', ], - 'darwin': [ + darwin: [ 'iguana', 'komodo-cli', 'komodod', @@ -257,7 +477,7 @@ const latestBins = { 'libnanomsg.5.0.0.dylib', 'libstdc++.6.dylib', // encode %2B ], - 'linux': [ + linux: [ 'iguana', 'komodo-cli', 'komodod', @@ -273,22 +493,21 @@ let binsToUpdate = []; */ shepherd.get('/update/bins/check', function(req, res, next) { const rootLocation = path.join(__dirname, '../'); - const successObj = { - 'msg': 'success', - 'result': 'bins', + msg: 'success', + result: 'bins', }; res.end(JSON.stringify(successObj)); const _os = os.platform(); - console.log('checking bins: ' + _os); + console.log(`checking bins: ${_os}`); cache.io.emit('patch', { - 'patch': { - 'type': 'bins-check', - 'status': 'progress', - 'message': 'checking bins: ' + _os, + patch: { + type: 'bins-check', + status: 'progress', + message: `checking bins: ${_os}`, }, }); // get list of bins/dlls that can be updated to the latest @@ -300,21 +519,21 @@ shepherd.get('/update/bins/check', function(req, res, next) { console.log('local file: ' + (rootLocation + localBinLocation[_os] + latestBins[_os][i]) + ' (' + localBinSize + ')'); if (remoteBinSize !== localBinSize) { - console.log(latestBins[_os][i] + ' can be updated'); + console.log(`${latestBins[_os][i]} can be updated`); binsToUpdate.push({ - 'name': latestBins[_os][i], - 'rSize': remoteBinSize, - 'lSize': localBinSize, + name: latestBins[_os][i], + rSize: remoteBinSize, + lSize: localBinSize, }); } if (i === latestBins[_os].length - 1) { cache.io.emit('patch', { - 'patch': { - 'type': 'bins-check', - 'status': 'done', - 'fileList': binsToUpdate, - } + patch: { + type: 'bins-check', + status: 'done', + fileList: binsToUpdate, + }, }); } }); @@ -330,11 +549,11 @@ shepherd.get('/update/bins', function(req, res, next) { const rootLocation = path.join(__dirname, '../'); const _os = os.platform(); const successObj = { - 'msg': 'success', - 'result': { - 'filesCount': binsToUpdate.length, - 'list': binsToUpdate, - } + msg: 'success', + result: { + filesCount: binsToUpdate.length, + list: binsToUpdate, + }, }; res.end(JSON.stringify(successObj)); @@ -342,44 +561,44 @@ shepherd.get('/update/bins', function(req, res, next) { for (let i = 0; i < binsToUpdate.length; i++) { downloadFile({ remoteFile: remoteBinLocation[_os] + binsToUpdate[i].name, - localFile: rootLocation + localBinLocation[_os] + 'patch/' + binsToUpdate[i].name, + localFile: `${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`, onProgress: function(received, total) { const percentage = (received * 100) / total; cache.io.emit('patch', { - 'msg': { - 'type': 'bins-update', - 'status': 'progress', - 'file': binsToUpdate[i].name, - 'bytesTotal': total, - 'bytesReceived': received, + msg: { + type: 'bins-update', + status: 'progress', + file: binsToUpdate[i].name, + bytesTotal: total, + bytesReceived: received, } }); - console.log(binsToUpdate[i].name + ' ' + percentage + '% | ' + received + ' bytes out of ' + total + ' bytes.'); + console.log(`${binsToUpdate[i].name} ${percentage}% | ${received} bytes out of ${total} bytes.`); } }) .then(function() { // verify that remote file is matching to DL'ed file - const localBinSize = fs.statSync(rootLocation + localBinLocation[_os] + 'patch/' + binsToUpdate[i].name).size; + const localBinSize = fs.statSync(`${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`).size; console.log('compare dl file size'); if (localBinSize === binsToUpdate[i].rSize) { cache.io.emit('patch', { - 'msg': { - 'type': 'bins-update', - 'file': binsToUpdate[i].name, - 'status': 'done', + msg: { + type: 'bins-update', + file: binsToUpdate[i].name, + status: 'done', } }); - console.log('file ' + binsToUpdate[i].name + ' succesfully downloaded'); + console.log(`file ${binsToUpdate[i].name} succesfully downloaded`); } else { cache.io.emit('patch', { - 'msg': { - 'type': 'bins-update', - 'file': binsToUpdate[i].name, - 'message': 'size mismatch', + msg: { + type: 'bins-update', + file: binsToUpdate[i].name, + message: 'size mismatch', } }); - console.log('error: ' + binsToUpdate[i].name + ' file size doesnt match remote!'); + console.log(`error: ${binsToUpdate[i].name} file size doesnt match remote!`); } }); } @@ -392,8 +611,8 @@ shepherd.get('/update/bins', function(req, res, next) { */ shepherd.get('/update/patch', function(req, res, next) { const successObj = { - 'msg': 'success', - 'result': 'dl started' + msg: 'success', + result: 'dl started' }; res.end(JSON.stringify(successObj)); @@ -411,15 +630,15 @@ shepherd.updateAgama = function() { const percentage = (received * 100) / total; if (Math.floor(percentage) % 5 === 0 || Math.floor(percentage) % 10 === 0) { - console.log('patch ' + percentage + '% | ' + received + ' bytes out of ' + total + ' bytes.'); + console.log(`patch ${percentage}% | ${received} bytes out of ${total} bytes.`); cache.io.emit('patch', { - 'msg': { - 'status': 'progress', - 'type': 'ui', - 'progress': percentage, - 'bytesTotal': total, - 'bytesReceived': received - } + msg: { + status: 'progress', + type: 'ui', + progress: percentage, + bytesTotal: total, + bytesReceived: received, + }, }); } } @@ -427,15 +646,15 @@ shepherd.updateAgama = function() { .then(function() { remoteFileSize('https://github.com/pbca26/dl-test/raw/master/patch.zip', function(err, remotePatchSize) { // verify that remote file is matching to DL'ed file - const localPatchSize = fs.statSync(rootLocation + 'patch.zip').size; + const localPatchSize = fs.statSync(`${rootLocation}patch.zip`).size; console.log('compare dl file size'); if (localPatchSize === remotePatchSize) { + const zip = new AdmZip(`${rootLocation}patch.zip`); + console.log('patch succesfully downloaded'); console.log('extracting contents'); - const zip = new AdmZip(rootLocation + 'patch.zip'); - if (shepherd.appConfig.dev) { if (!fs.existsSync(`${rootLocation}/patch`)) { fs.mkdirSync(`${rootLocation}/patch`); @@ -445,19 +664,19 @@ shepherd.updateAgama = function() { zip.extractAllTo(/*target path*/rootLocation + (shepherd.appConfig.dev ? '/patch' : ''), /*overwrite*/true); // TODO: extract files in chunks cache.io.emit('patch', { - 'msg': { - 'type': 'ui', - 'status': 'done' - } + msg: { + type: 'ui', + status: 'done', + }, }); - fs.unlink(rootLocation + 'patch.zip'); + fs.unlink(`${rootLocation}patch.zip`); } else { cache.io.emit('patch', { - 'msg': { - 'type': 'ui', - 'status': 'error', - 'message': 'size mismatch' - } + msg: { + type: 'ui', + status: 'error', + message: 'size mismatch', + }, }); console.log('patch file size doesnt match remote!'); } @@ -474,7 +693,7 @@ shepherd.get('/update/patch/check', function(req, res, next) { const rootLocation = path.join(__dirname, '../'); const options = { url: 'https://github.com/pbca26/dl-test/raw/master/version', - method: 'GET' + method: 'GET', }; request(options, function (error, response, body) { @@ -482,7 +701,7 @@ shepherd.get('/update/patch/check', function(req, res, next) { response.statusCode && response.statusCode === 200) { const remoteVersion = body.split('\n'); - const localVersionFile = fs.readFileSync(rootLocation + 'version', 'utf8'); + const localVersionFile = fs.readFileSync(`${rootLocation}version`, 'utf8'); let localVersion; if (localVersionFile.indexOf('\r\n') > -1) { @@ -493,26 +712,26 @@ shepherd.get('/update/patch/check', function(req, res, next) { if (remoteVersion[0] === localVersion[0]) { const successObj = { - 'msg': 'success', - 'result': 'latest' + msg: 'success', + result: 'latest', }; res.end(JSON.stringify(successObj)); } else { const successObj = { - 'msg': 'success', - 'result': 'update', - 'version': { - 'local': localVersion[0], - 'remote': remoteVersion[0], - } + msg: 'success', + result: 'update', + version: { + local: localVersion[0], + remote: remoteVersion[0], + }, }; res.end(JSON.stringify(successObj)); } } else { res.end({ - 'err': 'error getting update' + err: 'error getting update', }); } }); @@ -525,31 +744,35 @@ shepherd.get('/update/patch/check', function(req, res, next) { */ shepherd.get('/unpack', function(req, res, next) { const dlLocation = path.join(__dirname, '../'); - var zip = new AdmZip(dlLocation + 'patch.zip'); - zip.extractAllTo(/*target path*/dlLocation + '/patch/unpack', /*overwrite*/true); + const zip = new AdmZip(`${dlLocation}patch.zip`); + zip.extractAllTo(/*target path*/ `${dlLocation}/patch/unpack`, /*overwrite*/true); const successObj = { - 'msg': 'success', - 'result': 'unpack started' + msg: 'success', + result: 'unpack started', }; res.end(JSON.stringify(successObj)); }); +/* + * type: GET + * + */ shepherd.get('/coinslist', function(req, res, next) { if (fs.existsSync(`${iguanaDir}/shepherd/coinslist.json`)) { fs.readFile(`${iguanaDir}/shepherd/coinslist.json`, 'utf8', function (err, data) { if (err) { const errorObj = { - 'msg': 'error', - 'result': err + msg: 'error', + result: err, }; res.end(JSON.stringify(errorObj)); } else { const successObj = { - 'msg': 'success', - 'result': data ? JSON.parse(data) : '' + msg: 'success', + result: data ? JSON.parse(data) : '', }; res.end(JSON.stringify(successObj)); @@ -557,8 +780,8 @@ shepherd.get('/coinslist', function(req, res, next) { }); } else { const errorObj = { - 'msg': 'error', - 'result': 'coin list doesn\'t exist' + msg: 'error', + result: 'coin list doesn\'t exist', }; res.end(JSON.stringify(errorObj)); @@ -581,11 +804,11 @@ shepherd.post('/guilog', function(req, res, next) { guiLog[shepherd.appSessionHash][req.body.timestamp].response = req.body.response; } else { guiLog[shepherd.appSessionHash][req.body.timestamp] = { - 'function': req.body.function, - 'type': req.body.type, - 'url': req.body.url, - 'payload': req.body.payload, - 'status': req.body.status, + function: req.body.function, + type: req.body.type, + url: req.body.url, + payload: req.body.payload, + status: req.body.status, }; } @@ -595,14 +818,18 @@ shepherd.post('/guilog', function(req, res, next) { } const returnObj = { - 'msg': 'success', - 'result': 'gui log entry is added' + msg: 'success', + result: 'gui log entry is added', }; res.end(JSON.stringify(returnObj)); }); }); +/* + * type: GET + * params: type + */ shepherd.get('/getlog', function(req, res, next) { const logExt = req.query.type === 'txt' ? 'txt' : 'json'; @@ -610,15 +837,15 @@ shepherd.get('/getlog', function(req, res, next) { fs.readFile(`${iguanaDir}/shepherd/agamalog.${logExt}`, 'utf8', function (err, data) { if (err) { const errorObj = { - 'msg': 'error', - 'result': err + msg: 'error', + result: err, }; res.end(JSON.stringify(errorObj)); } else { const successObj = { - 'msg': 'success', - 'result': data ? JSON.parse(data) : '' + msg: 'success', + result: data ? JSON.parse(data) : '', }; res.end(JSON.stringify(successObj)); @@ -626,21 +853,25 @@ shepherd.get('/getlog', function(req, res, next) { }); } else { const errorObj = { - 'msg': 'error', - 'result': `agama.${logExt} doesnt exist` + msg: 'error', + result: `agama.${logExt} doesnt exist`, }; res.end(JSON.stringify(errorObj)); } }); +/* + * type: POST + * params: payload + */ shepherd.post('/coinslist', function(req, res, next) { const _payload = req.body.payload; if (!_payload) { const errorObj = { - 'msg': 'error', - 'result': 'no payload provided' + msg: 'error', + result: 'no payload provided', }; res.end(JSON.stringify(errorObj)); @@ -648,15 +879,15 @@ shepherd.post('/coinslist', function(req, res, next) { fs.writeFile(`${cache.iguanaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), function (err) { if (err) { const errorObj = { - 'msg': 'error', - 'result': err + msg: 'error', + result: err, }; res.end(JSON.stringify(errorObj)); } else { const successObj = { - 'msg': 'success', - 'result': 'done' + msg: 'success', + result: 'done', }; res.end(JSON.stringify(successObj)); @@ -675,13 +906,18 @@ shepherd.quitKomodod = function(timeout = 100) { const chain = key !== 'komodod' ? key : null; coindExitInterval[key] = setInterval(function() { - console.log('exec ' + komodocliBin + (chain ? ' -ac_name=' + chain : '') + ' stop'); - exec(komodocliBin + (chain ? ' -ac_name=' + chain : '') + ' stop', function(error, stdout, stderr) { + let _arg = []; + if (chain) { + _arg.push(`-ac_name=${chain}`); + } + _arg.push('stop'); + execFile(`${komodocliBin}`, _arg, function(error, stdout, stderr) { console.log(`stdout: ${stdout}`); console.log(`stderr: ${stderr}`); if (stdout.indexOf('EOF reached') > -1 || stderr.indexOf('EOF reached') > -1 || + (error && error.toString().indexOf('Command failed') > -1 && !stderr) || // win "special snowflake" case stdout.indexOf('connect to server: unknown (code -1)') > -1 || stderr.indexOf('connect to server: unknown (code -1)') > -1) { delete coindInstanceRegistry[key]; @@ -691,6 +927,7 @@ shepherd.quitKomodod = function(timeout = 100) { if (error !== null) { console.log(`exec error: ${error}`); } + shepherd.killRogueProcess('komodo-cli'); }); }, timeout); } @@ -708,7 +945,7 @@ shepherd.getConf = function(chain) { let parsedRpcConfig = { user: '', pass: '', - port: _port + port: _port, }; if (_match = _rpcConf.match(/rpcuser=\s*(.*)/)) { @@ -736,15 +973,15 @@ shepherd.getConf = function(chain) { shepherd.post('/cli', function(req, res, next) { if (!req.body.payload) { const errorObj = { - 'msg': 'error', - 'result': 'no payload provided' + msg: 'error', + result: 'no payload provided', }; res.end(JSON.stringify(errorObj)); } else if (!req.body.payload.cmd.match(/^[0-9a-zA-Z _\,\.\[\]"'/\\]+$/g)) { const errorObj = { - 'msg': 'error', - 'result': 'wrong cli string format' + msg: 'error', + result: 'wrong cli string format', }; res.end(JSON.stringify(errorObj)); @@ -760,15 +997,15 @@ shepherd.post('/cli', function(req, res, next) { if (_mode === 'default') { let _body = { - 'agent': 'bitcoinrpc', - 'method': _cmd + agent: 'bitcoinrpc', + method: _cmd, }; if (req.body.payload.params) { _body = { - 'agent': 'bitcoinrpc', - 'method': _cmd, - 'params': req.body.payload.params === ' ' ? [''] : req.body.payload.params + agent: 'bitcoinrpc', + method: _cmd, + params: req.body.payload.params === ' ' ? [''] : req.body.payload.params, }; } @@ -776,8 +1013,8 @@ shepherd.post('/cli', function(req, res, next) { url: `http://localhost:${rpcConf[req.body.payload.chain].port}`, method: 'POST', auth: { - 'user': rpcConf[req.body.payload.chain].user, - 'pass': rpcConf[req.body.payload.chain].pass + user: rpcConf[req.body.payload.chain].user, + pass: rpcConf[req.body.payload.chain].pass, }, body: JSON.stringify(_body) }; @@ -794,7 +1031,9 @@ shepherd.post('/cli', function(req, res, next) { } }); } else { - exec(komodocliBin + (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params, function(error, stdout, stderr) { + let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params; + _arg = _arg.trim().split(' '); + execFile(komodocliBin, _arg, function(error, stdout, stderr) { console.log(`stdout: ${stdout}`); console.log(`stderr: ${stderr}`); @@ -806,17 +1045,18 @@ shepherd.post('/cli', function(req, res, next) { if (stderr) { responseObj = { - 'msg': 'error', - 'result': stderr + msg: 'error', + result: stderr, }; } else { responseObj = { - 'msg': 'success', - 'result': stdout + msg: 'success', + result: stdout, }; } res.end(JSON.stringify(responseObj)); + shepherd.killRogueProcess('komodo-cli'); }); } } @@ -829,8 +1069,8 @@ shepherd.post('/cli', function(req, res, next) { shepherd.post('/appconf', function(req, res, next) { if (!req.body.payload) { const errorObj = { - 'msg': 'error', - 'result': 'no payload provided' + msg: 'error', + result: 'no payload provided', }; res.end(JSON.stringify(errorObj)); @@ -838,8 +1078,8 @@ shepherd.post('/appconf', function(req, res, next) { shepherd.saveLocalAppConf(req.body.payload); const successObj = { - 'msg': 'success', - 'result': 'config saved' + msg: 'success', + result: 'config saved', }; res.end(JSON.stringify(successObj)); @@ -854,8 +1094,8 @@ shepherd.post('/appconf/reset', function(req, res, next) { shepherd.saveLocalAppConf(shepherd.defaultAppConfig); const successObj = { - 'msg': 'success', - 'result': 'config saved' + msg: 'success', + result: 'config saved', }; res.end(JSON.stringify(successObj)); @@ -1073,8 +1313,8 @@ shepherd.getSyncOnlyForksInfo = function() { */ shepherd.get('/forks/info/start', function(req, res, next) { const successObj = { - 'msg': 'success', - 'result': 'started' + msg: 'success', + result: 'started', }; res.end(JSON.stringify(successObj)); @@ -1087,8 +1327,8 @@ shepherd.get('/forks/info/start', function(req, res, next) { */ shepherd.get('/forks/info/show', function(req, res, next) { const successObj = { - 'msg': 'success', - 'result': JSON.stringify(syncOnlyIguanaInstanceInfo) + msg: 'success', + result: JSON.stringify(syncOnlyIguanaInstanceInfo), }; res.end(JSON.stringify(successObj)); @@ -1113,8 +1353,8 @@ shepherd.get('/forks/restart', function(req, res, next) { pm2.disconnect(); const successObj = { - 'msg': 'success', - 'result': 'restarted' + msg: 'success', + result: 'restarted', }; shepherd.writeLog(`iguana fork pmid ${_pmid} restarted`); @@ -1142,8 +1382,8 @@ shepherd.get('/forks/stop', function(req, res, next) { pm2.disconnect(); const successObj = { - 'msg': 'success', - 'result': 'stopped' + msg: 'success', + result: 'stopped', }; shepherd.writeLog(`iguana fork pmid ${_pmid} stopped`); @@ -1159,8 +1399,8 @@ shepherd.get('/forks/stop', function(req, res, next) { */ shepherd.get('/forks', function(req, res, next) { const successObj = { - 'msg': 'success', - 'result': iguanaInstanceRegistry + msg: 'success', + result: iguanaInstanceRegistry, }; res.end(JSON.stringify(successObj)); @@ -1171,9 +1411,9 @@ shepherd.get('/forks', function(req, res, next) { * params: name */ shepherd.post('/forks', function(req, res, next) { - const mode = req.body.mode, - coin = req.body.coin, - port = shepherd.appConfig.iguanaCorePort; + const mode = req.body.mode; + const coin = req.body.coin; + const port = shepherd.appConfig.iguanaCorePort; portscanner.findAPortNotInUse(port, port + 100, '127.0.0.1', function(error, _port) { pm2.connect(true, function(err) { //start up pm2 god @@ -1194,23 +1434,23 @@ shepherd.post('/forks', function(req, res, next) { }, function(err, apps) { if (apps && apps[0] && apps[0].process && apps[0].process.pid) { iguanaInstanceRegistry[_port] = { - 'mode': mode, - 'coin': coin, - 'pid': apps[0].process && apps[0].process.pid, - 'pmid': apps[0].pm2_env.pm_id + mode: mode, + coin: coin, + pid: apps[0].process && apps[0].process.pid, + pmid: apps[0].pm2_env.pm_id, }; cache.setVar('iguanaInstances', iguanaInstanceRegistry); const successObj = { - 'msg': 'success', - 'result': _port + msg: 'success', + result: _port, }; res.end(JSON.stringify(successObj)); } else { const errorObj = { - 'msg': 'success', - 'error': 'iguana start error' + msg: 'success', + error: 'iguana start error', }; res.end(JSON.stringify(errorObj)); @@ -1227,11 +1467,11 @@ shepherd.post('/forks', function(req, res, next) { } pm2.disconnect(); // Disconnect from PM2 - if (err) { - shepherd.writeLog(`iguana fork error: ${err}`); - console.log(`iguana fork error: ${err}`); - throw err; - } + if (err) { + shepherd.writeLog(`iguana fork error: ${err}`); + console.log(`iguana fork error: ${err}`); + throw err; + } }); }); }); @@ -1272,9 +1512,9 @@ shepherd.get('/InstantDEX/allcoins', function(req, res, next) { }); } else { successObj = { - 'native': nativeCoindList, - 'basilisk': [], - 'full': [] + native: nativeCoindList, + basilisk: [], + full: [], }; res.end(JSON.stringify(successObj)); @@ -1285,7 +1525,7 @@ shepherd.get('/InstantDEX/allcoins', function(req, res, next) { * type: GET * */ -shepherd.get('/SuperNET/activehandle', function(req, res, next) { +shepherd.get('/SuperNET/activehandle', function(req, res, next) { // not finished // TODO: if only native return obj // else query main iguana instance and return combined response // http://localhost:7778/api/SuperNET/activehandle?userpass=tmpIgRPCUser@1234 @@ -1309,11 +1549,11 @@ shepherd.get('/SuperNET/activehandle', function(req, res, next) { }); } else { successObj = { - 'pubkey': 'nativeonly', - 'result': 'success', - 'handle': '', - 'status': Object.keys(coindInstanceRegistry).length ? 'unlocked' : 'locked', - 'duration': 2507830 + pubkey: 'nativeonly', + result: 'success', + handle: '', + status: Object.keys(coindInstanceRegistry).length ? 'unlocked' : 'locked', + duration: 2507830, }; res.end(JSON.stringify(successObj)); @@ -1385,6 +1625,19 @@ shepherd.post('/debuglog', function(req, res) { let _lastNLines = req.body.lastLines; let _location; + if (os.platform() === 'darwin') { + komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`; + } + + if (os.platform() === 'linux') { + komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`; + } + + if (os.platform() === 'win32') { + komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`; + komodoDir = path.normalize(komodoDir); + } + if (_herd === 'iguana') { _location = iguanaDir; } else if (_herd === 'komodo') { @@ -1395,18 +1648,19 @@ shepherd.post('/debuglog', function(req, res) { _location = `${komodoDir}/${_ac}`; } + console.log('komodo dir ' + komodoDir); shepherd.readDebugLog(`${_location}/debug.log`, _lastNLines) .then(function(result) { const _obj = { - 'msg': 'success', - 'result': result + msg: 'success', + result: result, }; res.end(JSON.stringify(_obj)); }, function(result) { const _obj = { - 'msg': 'error', - 'result': result + msg: 'error', + result: result, }; res.end(JSON.stringify(_obj)); @@ -1421,31 +1675,55 @@ shepherd.post('/herd', function(req, res) { console.log('======= req.body ======='); console.log(req.body); - herder(req.body.herd, req.body.options); + function testCoindPort() { + const _port = assetChainPorts[req.body.options.ac_name]; - const obj = { - 'msg': 'success', - 'result': 'result' - }; + portscanner.checkPortStatus(_port, '127.0.0.1', function(error, status) { + // Status is 'open' if currently in use or 'closed' if available + if (status === 'open') { + console.log(`komodod service start error at port ${_port}, reason: port is closed`); + shepherd.writeLog(`komodod service start error at port ${_port}, reason: port is closed`); + cache.io.emit('service', { + komodod: { + error: 'error starting ' + req.body.herd + ' ' + req.body.options.ac_name + ' daemon. Port ' + _port + ' is already taken!', + }, + }); - res.end(JSON.stringify(obj)); + const obj = { + msg: 'error', + result: 'error starting ' + req.body.herd + ' ' + req.body.options.ac_name + ' daemon. Port ' + _port + ' is already taken!', + }; + + res.status(500); + res.end(JSON.stringify(obj)); + } else { + herder(req.body.herd, req.body.options); + + const obj = { + msg: 'success', + result: 'result', + }; + + res.end(JSON.stringify(obj)); + } + }); + } if (req.body.herd === 'komodod') { - const _port = assetChainPorts[req.body.options.ac_name]; // check if komodod instance is already running + testCoindPort(); setTimeout(function() { - portscanner.checkPortStatus(_port, '127.0.0.1', function(error, status) { - // Status is 'open' if currently in use or 'closed' if available - if (status === 'closed') { - shepherd.writeLog(`komodod service start error at port ${_port}, reason: port is closed`); - cache.io.emit('service', { - 'komodod': { - 'error': 'start error' - } - }); - } - }); + testCoindPort(); }, 10000); + } else { + herder(req.body.herd, req.body.options); + + const obj = { + msg: 'success', + result: 'result', + }; + + res.end(JSON.stringify(obj)); } }); @@ -1470,9 +1748,9 @@ shepherd.post('/herdlist', function(req, res) { shepherd.writeLog(list[0].pid); const obj = { - 'herdname': req.body.herdname, - 'status': list[0].pm2_env.status, - 'pid': list[0].pid + herdname: req.body.herdname, + status: list[0].pm2_env.status, + pid: list[0].pid, }; res.end(JSON.stringify(obj)); @@ -1489,8 +1767,8 @@ shepherd.post('/slay', function(req, res) { slayer(req.body.slay); const obj = { - 'msg': 'success', - 'result': 'result' + msg: 'success', + result: 'result', }; res.end(JSON.stringify(obj)); @@ -1512,8 +1790,8 @@ shepherd.post('/setconf', function(req, res) { } const obj = { - 'msg': 'success', - 'result': 'result' + msg: 'success', + result: 'result', }; res.end(JSON.stringify(obj)); @@ -1534,8 +1812,8 @@ shepherd.post('/getconf', function(req, res) { shepherd.writeLog(confpath); const obj = { - 'msg': 'success', - 'result': confpath + msg: 'success', + result: confpath, }; res.end(JSON.stringify(obj)); @@ -1551,8 +1829,8 @@ shepherd.get('/kick', function(req, res, next) { if (!_coin) { const errorObj = { - 'msg': 'error', - 'result': 'no coin name provided' + msg: 'error', + result: 'no coin name provided', }; res.end(JSON.stringify(errorObj)); @@ -1560,85 +1838,85 @@ shepherd.get('/kick', function(req, res, next) { if (!_type) { const errorObj = { - 'msg': 'error', - 'result': 'no type provided' + msg: 'error', + result: 'no type provided', }; res.end(JSON.stringify(errorObj)); } const kickStartDirs = { - 'soft': [ + soft: [ { - 'name': 'DB/[coin]', - 'type': 'pattern', - 'match': 'balancecrc.' + name: 'DB/[coin]', + type: 'pattern', + match: 'balancecrc.', }, { - 'name': 'DB/[coin]/utxoaddrs', - 'type': 'file' + name: 'DB/[coin]/utxoaddrs', + type: 'file', }, { - 'name': 'DB/[coin]/accounts', - 'type': 'folder' + name: 'DB/[coin]/accounts', + type: 'folder', }, { - 'name': 'DB/[coin]/fastfind', - 'type': 'folder' + name: 'DB/[coin]/fastfind', + type: 'folder', }, { - 'name': 'tmp/[coin]', - 'type': 'folder' + name: 'tmp/[coin]', + type: 'folder', } ], - 'hard': [ + hard: [ { - 'name': 'DB/[coin]', - 'type': 'pattern', - 'match': 'balancecrc.' + name: 'DB/[coin]', + type: 'pattern', + match: 'balancecrc.', }, { - 'name': 'DB/[coin]/utxoaddrs', - 'type': 'file' + name: 'DB/[coin]/utxoaddrs', + type: 'file', }, { - 'name': 'DB/[coin]', - 'type': 'pattern', - 'match': 'utxoaddrs.' + name: 'DB/[coin]', + type: 'pattern', + match: 'utxoaddrs.', }, { - 'name': 'DB/[coin]/accounts', - 'type': 'folder' + name: 'DB/[coin]/accounts', + type: 'folder', }, { - 'name': 'DB/[coin]/fastfind', - 'type': 'folder' + name: 'DB/[coin]/fastfind', + type: 'folder', }, { - 'name': 'DB/[coin]/spends', - 'type': 'folder' + name: 'DB/[coin]/spends', + type: 'folder', }, { - 'name': 'tmp/[coin]', - 'type': 'folder' + name: 'tmp/[coin]', + type: 'folder', } ], - 'brutal': [ // delete all coin related data + brutal: [ // delete all coin related data { - 'name': 'DB/[coin]', - 'type': 'folder' + name: 'DB/[coin]', + type: 'folder', }, { - 'name': 'DB/purgeable/[coin]', - 'type': 'folder' + name: 'DB/purgeable/[coin]', + type: 'folder', }, { - 'name': 'DB/ro/[coin]', - 'type': 'folder' + name: 'DB/ro/[coin]', + type: 'folder', }, { - 'name': 'tmp/[coin]', - 'type': 'folder' + name: 'tmp/[coin]', + type: 'folder', } ] }; @@ -1651,7 +1929,7 @@ shepherd.get('/kick', function(req, res, next) { console.log('deleting ' + currentKickItem.type + (currentKickItem.match ? ' ' + currentKickItem.match : '') + ' ' + iguanaDir + '/' + currentKickItem.name.replace('[coin]', _coin)); if (currentKickItem.type === 'folder' || currentKickItem.type === 'file') { - rimraf(iguanaDir + '/' + currentKickItem.name.replace('[coin]', _coin), function(err) { + rimraf(`${iguanaDir}/${currentKickItem.name.replace('[coin]', _coin)}`, function(err) { if (err) { throw err; } @@ -1677,8 +1955,8 @@ shepherd.get('/kick', function(req, res, next) { } const successObj = { - 'msg': 'success', - 'result': 'kickstart: brutal is executed' + msg: 'success', + result: 'kickstart: brutal is executed', }; res.end(JSON.stringify(successObj)); @@ -1697,7 +1975,8 @@ shepherd.readDebugLog = function(fileLocation, lastNLines) { } else { console.log(`reading ${fileLocation}`); _fs.readFile(fileLocation, 'utf-8', function(err, data) { - if (err) throw err; + if (err) + throw err; const lines = data.trim().split('\n'); const lastLine = lines.slice(lines.length - lastNLines, lines.length).join('\n'); @@ -1772,22 +2051,22 @@ function herder(flock, data) { name: 'IGUANA', exec_mode : 'fork', args: [`-port=${shepherd.appConfig.iguanaCorePort}`], - cwd: iguanaDir //set correct iguana directory + cwd: iguanaDir // set correct iguana directory }, function(err, apps) { iguanaInstanceRegistry[shepherd.appConfig.iguanaCorePort] = { - 'mode': 'main', - 'coin': 'none', - 'pid': apps[0].process.pid, - 'pmid': apps[0].pm2_env.pm_id + mode: 'main', + coin: 'none', + pid: apps[0].process.pid, + pmid: apps[0].pm2_env.pm_id, }; shepherd.writeLog(`iguana core started at port ${shepherd.appConfig.iguanaCorePort} pid ${apps[0].process.pid}`); pm2.disconnect(); // Disconnect from PM2 - if (err) { - shepherd.writeLog(`iguana core port ${shepherd.appConfig.iguanaCorePort}`); - console.log(`iguana fork error: ${err}`); - throw err; - } + if (err) { + shepherd.writeLog(`iguana core port ${shepherd.appConfig.iguanaCorePort}`); + console.log(`iguana fork error: ${err}`); + throw err; + } }); }); } @@ -1833,11 +2112,11 @@ function herder(flock, data) { if (status === 'closed') { // start komodod via exec const _customParamDict = { - 'silent': '&', - 'reindex': '-reindex', - 'change': '-pubkey=', - 'datadir': '-datadir=', - 'rescan': '-rescan' + silent: '&', + reindex: '-reindex', + change: '-pubkey=', + datadir: '-datadir=', + rescan: '-rescan', }; let _customParam = ''; @@ -1849,15 +2128,21 @@ function herder(flock, data) { _customParam = ` ${_customParamDict[data.ac_custom_param]}${data.ac_custom_param_value}`; } + if (shepherd.appConfig.dataDir.length) { + _customParam = _customParam + ' -datadir=' + shepherd.appConfig.dataDir + '/' + data.ac_name; + } + console.log(`exec ${komododBin} ${data.ac_options.join(' ')}${_customParam}`); shepherd.writeLog(`exec ${komododBin} ${data.ac_options.join(' ')}${_customParam}`); const isChain = data.ac_name.match(/^[A-Z]*$/); const coindACParam = isChain ? ` -ac_name=${data.ac_name} ` : ''; - console.log('daemon param ' + data.ac_custom_param); + console.log(`daemon param ${data.ac_custom_param}`); coindInstanceRegistry[data.ac_name] = true; - exec(`${komododBin} ${coindACParam}${data.ac_options.join(' ')}${_customParam}`, { + let _arg = `${coindACParam}${data.ac_options.join(' ')}${_customParam}`; + _arg = _arg.trim().split(' '); + execFile(`${komododBin}`, _arg, { maxBuffer: 1024 * 10000 // 10 mb }, function(error, stdout, stderr) { shepherd.writeLog(`stdout: ${stdout}`); @@ -1869,8 +2154,8 @@ function herder(flock, data) { if (error.toString().indexOf('using -reindex') > -1) { cache.io.emit('service', { - 'komodod': { - 'error': 'run -reindex' + komodod: { + error: 'run -reindex', } }); } @@ -2219,9 +2504,9 @@ function setConf(flock) { } function getConf(flock) { - let komodoDir = '', - ZcashDir = '', - DaemonConfPath = ''; + let komodoDir = ''; + let ZcashDir = ''; + let DaemonConfPath = ''; console.log(flock); shepherd.writeLog(`getconf flock: ${flock}`); @@ -2271,35 +2556,35 @@ function formatBytes(bytes, decimals) { if (bytes === 0) return '0 Bytes'; - const k = 1000, - dm = (decimals + 1) || 3, - sizes = [ - 'Bytes', - 'KB', - 'MB', - 'GB', - 'TB', - 'PB', - 'EB', - 'ZB', - 'YB' - ], - i = Math.floor(Math.log(bytes) / Math.log(k)); + const k = 1000; + const dm = (decimals + 1) || 3; + const sizes = [ + 'Bytes', + 'KB', + 'MB', + 'GB', + 'TB', + 'PB', + 'EB', + 'ZB', + 'YB' + ]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; } shepherd.SystemInfo = function() { const os_data = { - 'totalmem_bytes': os.totalmem(), - 'totalmem_readable': formatBytes(os.totalmem()), - 'arch': os.arch(), - 'cpu': os.cpus()[0].model, - 'cpu_cores': os.cpus().length, - 'platform': os.platform(), - 'os_release': os.release(), - 'os_type': os.type() - }; + 'totalmem_bytes': os.totalmem(), + 'totalmem_readable': formatBytes(os.totalmem()), + 'arch': os.arch(), + 'cpu': os.cpus()[0].model, + 'cpu_cores': os.cpus().length, + 'platform': os.platform(), + 'os_release': os.release(), + 'os_type': os.type() + }; return os_data; }