@ngxeu/util
Version:
portable angular electron fused app update installer
245 lines (214 loc) • 7.09 kB
JavaScript
let fs = require('fs');
let path = require('path');
let unzipper = require('unzipper');
var util = require('util');
var eventEmitter = require('events').EventEmitter;
let http = require("follow-redirects").http;
let https = require('follow-redirects').https;
var semver = require('semver');
var AdmZip = require('adm-zip');
const crypto = require('crypto');
const algorithm = 'aes-128-cbc';
function NgxElectronUpdaterUtil () {
if (!(this instanceof NgxElectronUpdaterUtil)) return new NgxElectronUpdaterUtil()
}
util.inherits(NgxElectronUpdaterUtil, eventEmitter);
NgxElectronUpdaterUtil.prototype.gt = function(v1,v2){
return semver.gt(v1,v2);
}
NgxElectronUpdaterUtil.prototype.gte = function(v1,v2){
return semver.gte(v1,v2);
}
NgxElectronUpdaterUtil.prototype.lt = function(v1,v2){
return semver.lt(v1,v2);
}
NgxElectronUpdaterUtil.prototype.lte = function(v1,v2){
return semver.lte(v1,v2);
}
NgxElectronUpdaterUtil.prototype.eq = function(v1,v2){
return semver.eq(v1,v2);
}
NgxElectronUpdaterUtil.prototype.neq = function(v1,v2){
return semver.neq(v1,v2);
}
NgxElectronUpdaterUtil.prototype.setOptions = function(opts){
this.opts = opts;
}
NgxElectronUpdaterUtil.prototype.extract = function() {
console.log("extraction begining");
var opts = this.opts
console.log(opts);
_extract(opts,this);
console.log("extraction done");
}
NgxElectronUpdaterUtil.prototype.archiveInfo = function() {
let ZIP_FILE_PATH = this.opts.zip_file_path;
return unzipper.Open.file(ZIP_FILE_PATH);
}
NgxElectronUpdaterUtil.prototype.createPathIfNotExist = function(directoryPath) {
_forceCreateDir(directoryPath);
}
NgxElectronUpdaterUtil.prototype.download = function(url, downloadPath,fileName) {
_download(url, downloadPath,fileName,this);
}
NgxElectronUpdaterUtil.prototype.encrypt = function(key, value) {
return _encrypt(value, key);
}
NgxElectronUpdaterUtil.prototype.decrypt = function(key, value) {
return _decrypt(value, key);
}
function _encrypt(value,salt) {
if (value == null) {
throw new Error('value must not be null or undefined');
}
const hash = crypto.createHash("sha1");
hash.update(salt);
let key = hash.digest().slice(0, 16);
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, key, iv);
const encrypted = cipher.update(String(value), 'utf8', 'hex') + cipher.final('hex');
return iv.toString('hex') + encrypted;
}
function _decrypt(value,salt) {
if (value == null) {
throw new Error('value must not be null or undefined');
}
const stringValue = String(value);
const iv = Buffer.from(stringValue.slice(0, 32), 'hex');
const encrypted = stringValue.slice(32);
const hash = crypto.createHash("sha1");
hash.update(salt);
let key = hash.digest().slice(0, 16);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
return decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8');
}
function _extract(options,installer){
let ZIP_FILE_PATH =options.zip_file_path;
let currentDir = options.app_dir;
let appName = options.appName;
let resourcePath;
let buildPath='/app/build';
let appPath="/app";
let lookupPath;
let replacePath;
let extractPath = options.extract_path||currentDir + "/ngxeu/update" ;
if(options.os==='win'){
resourcePath = 'resources';
}
else if(options.os==='mac'){
resourcePath = 'resources';
if(options.updateType==='app'){
resourcePath=appName+'.app/Contents/Resources';
}
}
if(options.updateType==='build'){
replacePath=resourcePath+appPath;
lookupPath= resourcePath+buildPath;
}
else{
replacePath=resourcePath;
lookupPath= resourcePath+appPath;
}
let zipEntryCounter = 0;
if(options.os==='win'){
var zip = new AdmZip(ZIP_FILE_PATH);
var zipEntries = zip.getEntries();
zipEntryCounter = zipEntries.length;
}
let entryCounter = 0;
fs.createReadStream(ZIP_FILE_PATH)
.pipe(unzipper.Parse())
.on("entry", (entry) =>{
entryCounter++;
var fileName = entry.path;
var type = entry.type; // 'Directory' or 'File'
if (fileName.indexOf(lookupPath) > -1) {
installer.emit('extracting',fileName);
fileName = fileName.replace(replacePath, "");
if (type == "Directory") {
_forceCreateDir(extractPath + '/' + fileName);
} else if (type == "File") {
var fstream = fs.createWriteStream(extractPath + '/' + fileName);
entry.pipe(fstream);
}
} else {
entry.autodrain();
}
if(options.os==='win'){
if(entryCounter===zipEntryCounter){
installer.emit('finish');
}
}
}
)
.on('finish',()=>{
if(options.os!='win'){
installer.emit('finish');
}
});
setTimeout(()=>{
//finish forcefully after 15 secs for mac temp fix
//permanent fix can be thought in future
if(options.os==='mac'){
installer.emit('finish');
}
},15000);
}
function _forceCreateDir(dir) {
if (fs.existsSync(dir)) {
return;
}
try {
fs.mkdirSync(dir);
} catch (err) {
if (err.code==="ENOENT") {
_forceCreateDir(path.dirname(dir)); //create parent dir
_forceCreateDir(dir); //create dir
}
}
}
function _getWebProtocol(url){
if (url.indexOf("http://") == 0 ) {
return "http"
}
else if(url.indexOf("https://") == 0){
return "https"
}
}
function _download(url, downloadPath,fileName,installer) {
let httpClient;
if(_getWebProtocol(url)=="http"){
httpClient = http;
}
else if(_getWebProtocol(url)=="https"){
httpClient = https;
}
_forceCreateDir(downloadPath);
var outStream = fs.createWriteStream(downloadPath+"/"+fileName);
var request = httpClient.get(url, (response) => {
var len = parseInt(response.headers['content-length'], 10);
var cur = 0;
var total = len / 1048576; //1048576 - bytes in 1Megabyte
response.on("data", (chunk)=> {
cur += chunk.length;
var currentPercentage = (100.0 * cur / len).toFixed(2);
var totalPercentage = total.toFixed(2);
var downloadSpeedMbps = (cur / 1048576).toFixed(2);
let downloadStatus = {};
downloadStatus.currentPercentage = currentPercentage;
downloadStatus.totalPercentage = totalPercentage;
downloadStatus.speed = downloadSpeedMbps;
installer.emit('data',downloadStatus);
if(currentPercentage==='100.00'){
installer.emit('finish');
}
});
request.on("error", function(e){
let downloadStatus = {};
downloadStatus.errorMessage = e.message;
installer.emit('error',downloadStatus);
});
response.pipe(outStream);
});
}
module.exports = NgxElectronUpdaterUtil()