diff --git a/package.json b/package.json index 2dfd09d..91a4482 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "graceful-fs": "^4.1.11", "md5": "^2.2.1", "mkdirp": "^0.5.1", + "nodejs-aes256": "^1.0.1", "pm2": "^2.4.3", "portscanner": "^2.1.1", "ps-node": "^0.1.5", diff --git a/routes/shepherd.js b/routes/shepherd.js index dd72c6d..f9fd409 100644 --- a/routes/shepherd.js +++ b/routes/shepherd.js @@ -18,6 +18,7 @@ const electron = require('electron'), rimraf = require('rimraf'), portscanner = require('portscanner'), AdmZip = require('adm-zip'), + remoteFileSize = require('remote-file-size'), Promise = require('bluebird'); const fixPath = require('fix-path'); @@ -157,7 +158,11 @@ function downloadFile(configuration) { let req = request({ method: 'GET', - uri: configuration.remoteFile + uri: configuration.remoteFile, + agentOptions: { + keepAlive: true, + keepAliveMsecs: 15000 + } }); let out = fs.createWriteStream(configuration.localFile); @@ -188,14 +193,174 @@ 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/' +}; +const localBinLocation = { + 'win32': 'assets/bin/win64/', + 'darwin': 'assets/bin/osx/', + 'linux': 'assets/bin/linux64/' +}; +const latestBins = { + 'win32': [ + 'iguana.exe', + 'komodo-cli.exe', + 'komodod.exe', + 'libcrypto-1_1.dll', + 'libcurl-4.dll', + 'libcurl.dll', + 'libgcc_s_sjlj-1.dll', + 'libnanomsg.dll', + 'libssl-1_1.dll', + 'libwinpthread-1.dll', + 'nanomsg.dll', + 'pthreadvc2.dll' + ], + 'darwin': [ + 'iguana', + 'komodo-cli', + 'komodod', + 'libgcc_s.1.dylib', + 'libgomp.1.dylib', + 'libnanomsg.5.0.0.dylib', + 'libstdc++.6.dylib' // encode %2B + ], + 'linux': [ + 'iguana', + 'komodo-cli', + 'komodod' + ] +}; + +let binsToUpdate = []; + +/* + * Check bins file size + * type: + * params: + */ +shepherd.get('/update/bins/check', function(req, res, next) { + const rootLocation = path.join(__dirname, '../'); + + const successObj = { + 'msg': 'success', + 'result': 'bins' + }; + + res.end(JSON.stringify(successObj)); + + const _os = os.platform(); + console.log('checking bins: ' + _os); + + cache.io.emit('patch', { + 'patch': { + 'type': 'bins-check', + 'status': 'progress', + 'message': 'checking bins: ' + _os + } + }); + // get list of bins/dlls that can be updated to the latest + for (let i = 0; i < latestBins[_os].length; i++) { + remoteFileSize(remoteBinLocation[_os] + latestBins[_os][i], function(err, remoteBinSize) { + const localBinSize = fs.statSync(rootLocation + localBinLocation[_os] + latestBins[_os][i]).size; + + console.log('remote url: ' + (remoteBinLocation[_os] + latestBins[_os][i]) + ' (' + remoteBinSize + ')'); + console.log('local file: ' + (rootLocation + localBinLocation[_os] + latestBins[_os][i]) + ' (' + localBinSize + ')'); + + if (remoteBinSize !== localBinSize) { + console.log(latestBins[_os][i] + ' can be updated'); + binsToUpdate.push({ + '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 + } + }); + } + }); + } +}); + +/* + * Update bins + * type: + * params: + */ +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 + } + }; + + res.end(JSON.stringify(successObj)); + + for (let i = 0; i < binsToUpdate.length; i++) { + downloadFile({ + remoteFile: remoteBinLocation[_os] + 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 + } + }); + 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; + 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' + } + }); + console.log('file ' + binsToUpdate[i].name + ' succesfully downloaded'); + } else { + cache.io.emit('patch', { + 'msg': { + 'type': 'bins-update', + 'file': binsToUpdate[i].name, + 'message': 'size mismatch' + } + }); + console.log('error: ' + binsToUpdate[i].name + ' file size doesnt match remote!'); + } + }); + } +}); + /* * DL app patch * type: * params: patchList */ -shepherd.get('/patch', function(req, res, next) { - const dlLocation = path.join(__dirname, '../'); - +shepherd.get('/update/patch', function(req, res, next) { const successObj = { 'msg': 'success', 'result': 'dl started' @@ -207,32 +372,64 @@ shepherd.get('/patch', function(req, res, next) { }); shepherd.updateAgama = function() { + const rootLocation = path.join(__dirname, '../'); + downloadFile({ remoteFile: 'https://github.com/pbca26/dl-test/raw/master/patch.zip', - localFile: dlLocation + 'patch.zip', + localFile: rootLocation + 'patch.zip', onProgress: function(received, total) { const percentage = (received * 100) / total; - // console.log(percentage + '% | ' + received + ' bytes out of ' + total + ' bytes.'); - cache.io.emit('service', { - 'patch': { - 'status': 'dl', - 'progress': percentage, - 'bytesTotal': total, - 'bytesReceived': received - } - }); + if (Math.floor(percentage) % 5 === 0 || + Math.floor(percentage) % 10 === 0) { + 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 + } + }); + } } }) .then(function() { - console.log('File succesfully downloaded'); - console.log('extracting contents'); - - var zip = new AdmZip(dlLocation + 'patch.zip'); - zip.extractAllTo(/*target path*/dlLocation + '/patch/unpack', /*overwrite*/true); - // TODO: extract files in chunks - cache.io.emit('service', { - 'patch': { - 'status': 'done' + 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; + console.log('compare dl file size'); + + if (localPatchSize === remotePatchSize) { + 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`); + } + } + + 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' + } + }); + fs.unlink(rootLocation + 'patch.zip'); + } else { + cache.io.emit('patch', { + 'msg': { + 'type': 'ui', + 'status': 'error', + 'message': 'size mismatch' + } + }); + console.log('patch file size doesnt match remote!'); } }); }); @@ -243,7 +440,7 @@ shepherd.updateAgama = function() { * type: * params: */ -shepherd.get('/update-check', function(req, res, next) { +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', @@ -267,7 +464,11 @@ shepherd.get('/update-check', function(req, res, next) { } else { const successObj = { 'msg': 'success', - 'result': 'update' + 'result': 'update', + 'version': { + 'local': localVersion[0], + 'remote': remoteVersion[0], + } }; res.end(JSON.stringify(successObj));