2015-10-25 20:26:33 +01:00
|
|
|
'use strict';
|
|
|
|
|
2015-11-11 20:26:04 +01:00
|
|
|
var Backbone = require('backbone'),
|
|
|
|
RuntimeInfo = require('./runtime-info'),
|
2015-10-25 20:26:33 +01:00
|
|
|
Links = require('../const/links'),
|
2015-10-29 22:20:01 +01:00
|
|
|
Launcher = require('../comp/launcher'),
|
|
|
|
AppSettingsModel = require('../models/app-settings-model'),
|
2015-11-13 20:56:22 +01:00
|
|
|
UpdateModel = require('../models/update-model'),
|
|
|
|
Transport = require('../comp/transport');
|
2015-10-25 20:26:33 +01:00
|
|
|
|
|
|
|
var Updater = {
|
2015-10-29 22:20:01 +01:00
|
|
|
UpdateInterval: 1000*60*60*24,
|
2015-11-14 12:09:36 +01:00
|
|
|
MinUpdateTimeout: 500,
|
2015-11-13 20:56:22 +01:00
|
|
|
MinUpdateSize: 10000,
|
|
|
|
UpdateCheckFiles: ['index.html', 'app.js'],
|
2015-10-29 22:20:01 +01:00
|
|
|
nextCheckTimeout: null,
|
2015-11-13 20:56:22 +01:00
|
|
|
updateCheckDate: new Date(0),
|
2015-10-29 22:20:01 +01:00
|
|
|
enabledAutoUpdate: function() {
|
|
|
|
return Launcher && AppSettingsModel.instance.get('autoUpdate');
|
|
|
|
},
|
2015-11-14 12:09:36 +01:00
|
|
|
updateInProgress: function() {
|
|
|
|
return UpdateModel.instance.get('status') === 'checking' ||
|
|
|
|
['downloading', 'extracting'].indexOf(UpdateModel.instance.get('updateStatus')) >= 0;
|
|
|
|
},
|
2015-10-29 22:20:01 +01:00
|
|
|
init: function() {
|
|
|
|
var willCheckNow = this.scheduleNextCheck();
|
|
|
|
if (!willCheckNow && this.enabledAutoUpdate()) {
|
2015-11-13 20:56:22 +01:00
|
|
|
this.check();
|
2015-10-29 22:20:01 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
scheduleNextCheck: function() {
|
|
|
|
if (this.nextCheckTimeout) {
|
|
|
|
clearTimeout(this.nextCheckTimeout);
|
|
|
|
this.nextCheckTimeout = null;
|
|
|
|
}
|
|
|
|
if (!this.enabledAutoUpdate()) {
|
|
|
|
return;
|
|
|
|
}
|
2015-11-13 20:56:22 +01:00
|
|
|
var timeDiff = this.MinUpdateTimeout;
|
2015-10-29 22:20:01 +01:00
|
|
|
var lastCheckDate = UpdateModel.instance.get('lastCheckDate');
|
|
|
|
if (lastCheckDate) {
|
|
|
|
timeDiff = Math.min(Math.max(this.UpdateInterval + (lastCheckDate - new Date()), this.MinUpdateTimeout), this.UpdateInterval);
|
|
|
|
}
|
|
|
|
this.nextCheckTimeout = setTimeout(this.check.bind(this), timeDiff);
|
2015-11-14 12:09:36 +01:00
|
|
|
console.log('Update check will happen in ' + Math.round(timeDiff / 1000) + 's');
|
2015-10-29 22:20:01 +01:00
|
|
|
return timeDiff === this.MinUpdateTimeout;
|
|
|
|
},
|
2015-11-13 20:56:22 +01:00
|
|
|
check: function(startedByUser) {
|
2015-11-14 12:09:36 +01:00
|
|
|
if (!Launcher || this.updateInProgress()) {
|
2015-10-25 20:26:33 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-10-29 22:20:01 +01:00
|
|
|
UpdateModel.instance.set('status', 'checking');
|
|
|
|
var that = this;
|
2015-11-13 20:56:22 +01:00
|
|
|
if (!startedByUser) {
|
|
|
|
// additional protection from broken program logic, to ensure that auto-checks are not performed more than once an hour
|
|
|
|
var diffMs = new Date() - this.updateCheckDate;
|
|
|
|
if (isNaN(diffMs) || diffMs < 1000 * 60 * 60) {
|
|
|
|
console.error('Prevented update check; last check was performed at ' + this.updateCheckDate);
|
|
|
|
that.scheduleNextCheck();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.updateCheckDate = new Date();
|
|
|
|
}
|
|
|
|
console.log('Checking for update...');
|
|
|
|
Transport.httpGet({
|
2015-10-25 20:26:33 +01:00
|
|
|
url: Links.WebApp + 'manifest.appcache',
|
2015-11-13 20:56:22 +01:00
|
|
|
utf8: true,
|
|
|
|
success: function(data) {
|
2015-10-29 22:20:01 +01:00
|
|
|
var dt = new Date();
|
|
|
|
var match = data.match(/#\s*(\d+\-\d+\-\d+):v([\d+\.\w]+)/);
|
2015-11-13 20:56:22 +01:00
|
|
|
console.log('Update check: ' + (match ? match[0] : 'unknown'));
|
2015-10-25 20:26:33 +01:00
|
|
|
if (!match) {
|
2015-10-29 22:20:01 +01:00
|
|
|
var errMsg = 'No version info found';
|
2015-11-13 20:56:22 +01:00
|
|
|
UpdateModel.instance.set({ status: 'error', lastCheckDate: dt, lastCheckError: errMsg });
|
2015-10-29 22:20:01 +01:00
|
|
|
UpdateModel.instance.save();
|
|
|
|
that.scheduleNextCheck();
|
2015-10-25 20:26:33 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-11-14 12:09:36 +01:00
|
|
|
var prevLastVersion = UpdateModel.instance.get('lastVersion');
|
2015-11-13 20:56:22 +01:00
|
|
|
UpdateModel.instance.set({
|
|
|
|
status: 'ok',
|
|
|
|
lastCheckDate: dt,
|
|
|
|
lastSuccessCheckDate: dt,
|
|
|
|
lastVersionReleaseDate: new Date(match[1]),
|
|
|
|
lastVersion: match[2],
|
|
|
|
lastcheckError: null
|
|
|
|
});
|
2015-10-29 22:20:01 +01:00
|
|
|
UpdateModel.instance.save();
|
|
|
|
that.scheduleNextCheck();
|
2015-11-14 12:09:36 +01:00
|
|
|
if (prevLastVersion === UpdateModel.instance.get('lastVersion') &&
|
|
|
|
UpdateModel.instance.get('updateStatus') === 'ready') {
|
|
|
|
console.log('Waiting for the user to apply downloaded update');
|
|
|
|
return;
|
|
|
|
}
|
2015-11-13 20:56:22 +01:00
|
|
|
that.update(startedByUser);
|
2015-10-29 22:20:01 +01:00
|
|
|
},
|
2015-11-13 20:56:22 +01:00
|
|
|
error: function(e) {
|
|
|
|
console.error('Update check error', e);
|
|
|
|
UpdateModel.instance.set({
|
|
|
|
status: 'error',
|
|
|
|
lastCheckDate: new Date(),
|
|
|
|
lastCheckError: 'Error checking last version'
|
|
|
|
});
|
2015-10-29 22:20:01 +01:00
|
|
|
UpdateModel.instance.save();
|
|
|
|
that.scheduleNextCheck();
|
|
|
|
}
|
2015-10-25 20:26:33 +01:00
|
|
|
});
|
2015-10-29 22:20:01 +01:00
|
|
|
},
|
2015-11-13 20:56:22 +01:00
|
|
|
update: function(startedByUser) {
|
|
|
|
var ver = UpdateModel.instance.get('lastVersion');
|
2015-11-14 12:09:36 +01:00
|
|
|
if (!Launcher || ver === RuntimeInfo.version) {
|
2015-11-13 20:56:22 +01:00
|
|
|
console.log('You are using the latest version');
|
2015-10-29 22:20:01 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-11-13 20:56:22 +01:00
|
|
|
UpdateModel.instance.set({ updateStatus: 'downloading', updateError: null });
|
|
|
|
var that = this;
|
|
|
|
console.log('Downloading update', ver);
|
|
|
|
Transport.httpGet({
|
|
|
|
url: Links.UpdateDesktop.replace('{ver}', ver),
|
|
|
|
file: 'KeeWeb-' + ver + '.zip',
|
|
|
|
cache: !startedByUser,
|
|
|
|
success: function(filePath) {
|
2015-11-14 12:09:36 +01:00
|
|
|
UpdateModel.instance.set('updateStatus', 'extracting');
|
|
|
|
console.log('Extracting update file', that.UpdateCheckFiles, filePath);
|
2015-11-13 20:56:22 +01:00
|
|
|
that.extractAppUpdate(filePath, function(err) {
|
|
|
|
if (err) {
|
|
|
|
console.error('Error extracting update', err);
|
|
|
|
UpdateModel.instance.set({ updateStatus: 'error', updateError: 'Error extracting update' });
|
|
|
|
} else {
|
|
|
|
UpdateModel.instance.set({ updateStatus: 'ready', updateError: null });
|
2015-11-14 12:09:36 +01:00
|
|
|
if (!startedByUser) {
|
|
|
|
Backbone.trigger('update-app');
|
|
|
|
}
|
2015-11-13 20:56:22 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
error: function(e) {
|
|
|
|
console.error('Error downloading update', e);
|
|
|
|
UpdateModel.instance.set({ updateStatus: 'error', updateError: 'Error downloading update' });
|
2015-10-29 22:20:01 +01:00
|
|
|
}
|
2015-11-13 20:56:22 +01:00
|
|
|
});
|
|
|
|
},
|
2015-10-29 22:20:01 +01:00
|
|
|
|
2015-11-14 12:09:36 +01:00
|
|
|
extractAppUpdate: function(updateFile, cb) {
|
|
|
|
var expectedFiles = this.UpdateCheckFiles;
|
2015-11-13 20:56:22 +01:00
|
|
|
var appPath = Launcher.getUserDataPath();
|
|
|
|
var StreamZip = Launcher.req('node-stream-zip');
|
|
|
|
var zip = new StreamZip({ file: updateFile, storeEntries: true });
|
|
|
|
zip.on('error', cb);
|
|
|
|
zip.on('ready', function() {
|
|
|
|
var containsAll = expectedFiles.every(function(expFile) {
|
|
|
|
var entry = zip.entry(expFile);
|
|
|
|
return entry && entry.isFile;
|
|
|
|
});
|
|
|
|
if (!containsAll) {
|
|
|
|
return cb('Bad archive');
|
|
|
|
}
|
|
|
|
zip.extract(null, appPath, function(err) {
|
|
|
|
zip.close();
|
|
|
|
if (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
Launcher.req('fs').unlink(updateFile);
|
|
|
|
cb();
|
|
|
|
});
|
|
|
|
});
|
2015-10-25 20:26:33 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = Updater;
|