keeweb/app/scripts/comp/updater.js

212 lines
8.3 KiB
JavaScript
Raw Normal View History

2015-10-25 20:26:33 +01:00
'use strict';
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-11-14 16:28:36 +01:00
getAutoUpdateType: function() {
if (!Launcher) {
return false;
}
var autoUpdate = AppSettingsModel.instance.get('autoUpdate');
if (autoUpdate && autoUpdate === true) {
autoUpdate = 'install';
}
return autoUpdate;
2015-10-29 22:20:01 +01:00
},
2015-11-14 16:28:36 +01:00
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-11-14 16:28:36 +01:00
2015-10-29 22:20:01 +01:00
init: function() {
var willCheckNow = this.scheduleNextCheck();
if (!willCheckNow && this.getAutoUpdateType()) {
2015-11-13 20:56:22 +01:00
this.check();
2015-10-29 22:20:01 +01:00
}
2015-11-14 16:28:36 +01:00
if (!Launcher && window.applicationCache) {
window.applicationCache.addEventListener('updateready', this.checkAppCacheUpdateReady.bind(this));
this.checkAppCacheUpdateReady();
}
2015-10-29 22:20:01 +01:00
},
2015-11-14 16:28:36 +01:00
2015-10-29 22:20:01 +01:00
scheduleNextCheck: function() {
if (this.nextCheckTimeout) {
clearTimeout(this.nextCheckTimeout);
this.nextCheckTimeout = null;
}
if (!this.getAutoUpdateType()) {
2015-10-29 22:20:01 +01:00
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);
console.log('Next update check will happen in ' + Math.round(timeDiff / 1000) + 's');
2015-10-29 22:20:01 +01:00
return timeDiff === this.MinUpdateTimeout;
},
2015-11-14 16:28:36 +01:00
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-11-14 16:28:36 +01:00
if (this.checkManualDownload()) {
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;
}
if (!startedByUser && that.getAutoUpdateType() === 'install') {
that.update(startedByUser);
} else if (UpdateModel.instance.get('lastVersion') !== RuntimeInfo.version) {
UpdateModel.instance.set('updateStatus', 'found');
}
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-14 16:28:36 +01:00
checkManualDownload: function() {
if (+Launcher.getAppVersion().split('.')[1] <= 2) {
UpdateModel.instance.set({ updateStatus: 'ready', updateManual: true });
2015-11-14 17:07:14 +01:00
return true;
2015-11-14 16:28:36 +01:00
}
},
update: function(startedByUser, successCallback) {
2015-11-13 20:56:22 +01:00
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');
}
if (typeof successCallback === 'function') {
successCallback();
}
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-11-14 16:28:36 +01:00
},
checkAppCacheUpdateReady: function() {
if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
try { window.applicationCache.swapCache(); } catch (e) { }
UpdateModel.instance.set('updateStatus', 'ready');
}
2015-10-25 20:26:33 +01:00
}
};
module.exports = Updater;