This commit is contained in:
antelle 2016-03-27 15:57:22 +03:00
parent 039bb075c4
commit ec8efbf4d3
5 changed files with 120 additions and 136 deletions

View File

@ -6,7 +6,6 @@ var Timeouts = {
AutoHideHint: 3000,
FileChangeSync: 3000,
BeforeAutoLock: 300,
ScriptLoad: 15000,
CheckWindowClosed: 300
};

View File

@ -0,0 +1,28 @@
'use strict';
var Backbone = require('backbone'),
SettingsStore = require('../comp/settings-store');
var RuntimeDataModel = Backbone.Model.extend({
defaults: {},
initialize: function() {
this.listenTo(this, 'change', this.save);
},
load: function() {
var data = SettingsStore.load('runtime-data');
if (data) {
this.set(data, {silent: true});
}
},
save: function() {
SettingsStore.save('runtime-data', this.attributes);
}
});
RuntimeDataModel.instance = new RuntimeDataModel();
RuntimeDataModel.instance.load();
module.exports = RuntimeDataModel;

View File

@ -2,7 +2,8 @@
var Backbone = require('backbone'),
Logger = require('../util/logger'),
AppSettingsModel = require('../models/app-settings-model');
AppSettingsModel = require('../models/app-settings-model'),
RuntimeDataModel = require('../models/runtime-data-model');
var StorageBase = function() {
};
@ -17,6 +18,7 @@ _.extend(StorageBase.prototype, {
logger: null,
appSettings: AppSettingsModel.instance,
runtimeData: RuntimeDataModel.instance,
init: function() {
if (!this.name) {
@ -51,6 +53,10 @@ _.extend(StorageBase.prototype, {
return config.error && config.error('timeout');
});
xhr.open(config.method || 'GET', config.url);
if (this._oauthToken) {
xhr.setRequestHeader('Authorization',
this._oauthToken.tokenType + ' ' + this._oauthToken.accessToken);
}
_.forEach(config.headers, function(value, key) {
xhr.setRequestHeader(key, value);
});
@ -84,6 +90,58 @@ _.extend(StorageBase.prototype, {
win.focus();
}
return win;
},
_oauthAuthorize: function(opts) {
var that = this;
var oldToken = that.runtimeData.get(that.name + 'OAuthToken');
if (oldToken) {
that._oauthToken = oldToken;
opts.callback();
return;
}
that.logger.debug('OAuth popup opened');
that._openPopup(opts.url, 'OAuth', opts.width, opts.height);
var popupClosed = function() {
Backbone.off('popup-closed', popupClosed);
window.removeEventListener('message', windowMessage);
that.logger.error('OAuth error', 'popup closed');
opts.callback('popup closed');
};
var windowMessage = function(e) {
if (!e.data) {
return;
}
Backbone.off('popup-closed', popupClosed);
window.removeEventListener('message', windowMessage);
var token = that._oauthMsgToToken(e.data);
if (token.error) {
that.logger.error('OAuth error', token.error, token.errorDescription);
opts.callback(token.error);
} else {
that._oauthToken = token;
that.runtimeData.set(that.name + 'OAuthToken', token);
that.logger.debug('OAuth success');
opts.callback();
}
};
Backbone.on('popup-closed', popupClosed);
window.addEventListener('message', windowMessage);
},
_oauthMsgToToken: function(data) {
// jshint camelcase:false
if (data.error || !data.token_type) {
return { error: data.error || 'no token', errorDescription: data.error_description };
}
return {
tokenType: data.token_type,
accessToken: data.access_token,
authenticationToken: data.authentication_token,
expiresIn: data.expires_in,
scope: data.scope,
userId: data.user_id
};
}
});

View File

@ -1,10 +1,8 @@
'use strict';
var StorageBase = require('./storage-base'),
Backbone = require('backbone'),
Timeouts = require('../const/timeouts');
var StorageBase = require('./storage-base');
var GDriveClinetId = '847548101761-koqkji474gp3i2gn3k5omipbfju7pbt1.apps.googleusercontent.com';
var GDriveClientId = '847548101761-koqkji474gp3i2gn3k5omipbfju7pbt1.apps.googleusercontent.com';
var StorageGDrive = StorageBase.extend({
name: 'gdrive',
@ -14,7 +12,7 @@ var StorageGDrive = StorageBase.extend({
'<path d="M120.76421 71.989219 84.87226 9.6679848l-41.828196 0 35.899791 62.3212342zM58.014073 56.294956 37.107816 19.986746 1.2237094 82.284404 ' +
'22.137808 118.59261Zm-21.415974 63.012814 69.180421 0 20.9141-39.459631-67.635587 0z"/></svg>',
_gapi: null,
_baseUrl: 'https://www.googleapis.com/drive/v3',
load: function(path, opts, callback) {
var that = this;
@ -22,13 +20,12 @@ var StorageGDrive = StorageBase.extend({
if (err) { return callback && callback(err); }
that.logger.debug('Load', path);
var ts = that.logger.ts();
var url = 'https://www.googleapis.com/drive/v3/files/{id}/revisions/{rev}?alt=media'
var url = that._baseUrl + '/files/{id}/revisions/{rev}?alt=media'
.replace('{id}', path)
.replace('{rev}', stat.rev);
that._xhr({
url: url,
responseType: 'arraybuffer',
headers: { 'Authorization': that._getAuthHeader() },
success: function(response) {
that.logger.debug('Loaded', path, stat.rev, that.logger.ts(ts));
return callback && callback(null, response, { rev: stat.rev });
@ -43,18 +40,17 @@ var StorageGDrive = StorageBase.extend({
stat: function(path, opts, callback) {
var that = this;
this._getClient(function(err) {
this._authorize(function(err) {
if (err) {
return callback && callback(err);
}
that.logger.debug('Stat', path);
var ts = that.logger.ts();
var url = 'https://www.googleapis.com/drive/v3/files/{id}?fields=headRevisionId'
var url = that._baseUrl + '/files/{id}?fields=headRevisionId'
.replace('{id}', path);
that._xhr({
url: url,
responseType: 'json',
headers: { 'Authorization': that._getAuthHeader() },
success: function(response) {
var rev = response.headRevisionId;
that.logger.debug('Stated', path, rev, that.logger.ts(ts));
@ -86,7 +82,6 @@ var StorageGDrive = StorageBase.extend({
url: url,
method: 'PATCH',
responseType: 'json',
headers: { 'Authorization': that._getAuthHeader() },
data: new Blob([data], {type: 'application/octet-stream'}),
success: function(response) {
that.logger.debug('Saved', path, that.logger.ts(ts));
@ -106,17 +101,16 @@ var StorageGDrive = StorageBase.extend({
list: function(callback) {
var that = this;
this._getClient(function(err) {
this._authorize(function(err) {
if (err) { return callback && callback(err); }
that.logger.debug('List');
var url = 'https://www.googleapis.com/drive/v3/files?fields={fields}&q={q}'
var url = that._baseUrl + '/files?fields={fields}&q={q}'
.replace('{fields}', encodeURIComponent('files'))
.replace('{q}', encodeURIComponent('fileExtension="kdbx" and mimeType="application/octet-stream" and trashed=false'));
var ts = that.logger.ts();
that._xhr({
url: url,
responseType: 'json',
headers: { 'Authorization': that._getAuthHeader() },
success: function(response) {
if (!response) {
that.logger.error('List error', that.logger.ts(ts));
@ -140,77 +134,22 @@ var StorageGDrive = StorageBase.extend({
});
},
_getClient: function(callback) {
var that = this;
if (that._gapi) {
return that._authorize(callback);
}
that._gapiLoadTimeout = setTimeout(function() {
callback('Gdrive api load timeout');
delete that._gapiLoadTimeout;
}, Timeouts.ScriptLoad);
if (that._gapi) {
that._authorize.bind(callback);
} else {
that.logger.debug('Loading gapi client');
window.gApiClientLoaded = function() {
if (that._gapiLoadTimeout) {
that.logger.debug('Loaded gapi client');
delete window.gDriveClientLoaded;
that._gapi = window.gapi;
that._authorize(callback);
}
};
$.getScript('https://apis.google.com/js/client.js?onload=gApiClientLoaded', function() {
that.logger.debug('Loaded gapi script');
}).fail(function() {
that.logger.error('Failed to load gapi');
return callback('gapi load failed');
});
}
},
_authorize: function(callback) {
var that = this;
var scopes = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file'];
if (that._gapi.auth.getToken()) {
if (this._oauthToken) {
return callback();
}
var clientId = this.appSettings.get('gdriveClientId') || GDriveClinetId;
that._gapi.auth.authorize({'client_id': clientId, scope: scopes, immediate: true}, function(res) {
if (res && !res.error) {
callback();
} else {
that.logger.debug('Authorizing...');
var handlePopupClosed = function() {
that.logger.debug('Auth popup closed');
Backbone.off('popup-closed', handlePopupClosed);
callback('popup closed');
callback = null;
};
Backbone.on('popup-closed', handlePopupClosed);
var ts = that.logger.ts();
that._gapi.auth.authorize({'client_id': clientId, scope: scopes, immediate: false}, function(res) {
if (!callback) {
return;
}
Backbone.off('popup-closed', handlePopupClosed);
if (res && !res.error) {
that.logger.debug('Authorized', that.logger.ts(ts));
callback();
} else {
that.logger.error('Authorize error', that.logger.ts(ts), res);
callback(res && res.error || 'authorize error');
}
});
}
var clientId = this.appSettings.get('gdriveClientId') || GDriveClientId;
var url = 'https://accounts.google.com/o/oauth2/v2/auth' +
'?client_id={cid}&scope={scope}&response_type=token&redirect_uri={url}'
.replace('{cid}', clientId)
.replace('{scope}', encodeURIComponent('https://www.googleapis.com/auth/drive'))
.replace('{url}', encodeURIComponent(window.location));
this._oauthAuthorize({
url: url,
callback: callback,
width: 600,
height: 400
});
},
_getAuthHeader: function() {
// jshint camelcase:false
var token = this._gapi.auth.getToken();
return token.token_type + ' ' + token.access_token;
}
});

View File

@ -1,7 +1,6 @@
'use strict';
var Backbone = require('backbone'),
StorageBase = require('./storage-base');
var StorageBase = require('./storage-base');
var OneDriveClientId = {
Production: '000000004818ED3A',
@ -24,7 +23,6 @@ var StorageOneDrive = StorageBase.extend({
'6.28c-17.7-17.7-46.59-21.53-71.15-9.42-9.81 4.84-17.7 11.78-23.65 20.83-4.25 6.45-9.66 18.48-9.66 21.47 0 2.12-1.72 3.18-9.05 5.58-22.69 7.44-' +
'35.94 24.63-35.93 46.62 0 8 2.06 17.8 4.93 23.41 1.08 2.11 1.68 4.13 1.34 4.47-0.88 0.88-29.11 0.58-33.01-0.35z" /></g></g></g></svg>',
_token: null,
_baseUrl: 'https://api.onedrive.com/v1.0',
load: function(path, opts, callback) {
@ -39,7 +37,6 @@ var StorageOneDrive = StorageBase.extend({
that._xhr({
url: url,
responseType: 'json',
headers: {'Authorization': that._getAuthHeader()},
success: function (response) {
var downloadUrl = response['@content.downloadUrl'];
var rev = response.eTag;
@ -50,7 +47,6 @@ var StorageOneDrive = StorageBase.extend({
that._xhr({
url: downloadUrl,
responseType: 'arraybuffer',
headers: {'Authorization': that._getAuthHeader()},
success: function (response, xhr) {
rev = xhr.getResponseHeader('ETag') || rev;
that.logger.debug('Loaded', path, rev, that.logger.ts(ts));
@ -82,7 +78,6 @@ var StorageOneDrive = StorageBase.extend({
that._xhr({
url: url,
responseType: 'json',
headers: {'Authorization': that._getAuthHeader()},
success: function (response) {
var rev = response.eTag;
if (!rev) {
@ -113,10 +108,7 @@ var StorageOneDrive = StorageBase.extend({
url: url,
method: 'PUT',
responseType: 'json',
headers: {
'Authorization': that._getAuthHeader(),
'If-Match': rev
},
headers: { 'If-Match': rev },
data: new Blob([data], {type: 'application/octet-stream'}),
statuses: [200, 412],
success: function (response, xhr) {
@ -150,7 +142,6 @@ var StorageOneDrive = StorageBase.extend({
that._xhr({
url: url,
responseType: 'json',
headers: { 'Authorization': that._getAuthHeader() },
success: function(response) {
if (!response || !response.value) {
that.logger.error('List error', that.logger.ts(ts), response);
@ -185,52 +176,21 @@ var StorageOneDrive = StorageBase.extend({
},
_authorize: function(callback) {
if (this._token) {
if (this._oauthToken) {
return callback();
}
var clinetId = this._getClientId();
var url = 'https://login.live.com/oauth20_authorize.srf?client_id={cid}&scope={scope}&response_type=token&redirect_uri={url}'
.replace('{cid}', clinetId)
var clientId = this._getClientId();
var url = 'https://login.live.com/oauth20_authorize.srf' +
'?client_id={cid}&scope={scope}&response_type=token&redirect_uri={url}'
.replace('{cid}', clientId)
.replace('{scope}', 'onedrive.readwrite')
.replace('{url}', encodeURIComponent(window.location));
this._openPopup(url, 'onedriveAuth', 400, 600);
var that = this;
var popupClosed = function() {
Backbone.off('popup-closed', popupClosed);
window.removeEventListener('message', windowMessage);
that.logger.error('Auth error', 'popup closed');
callback('popup closed');
};
var windowMessage = function(e) {
var msgData = e.data;
if (!msgData) {
return;
}
// jshint camelcase:false
if (msgData.token_type) {
Backbone.off('popup-closed', popupClosed);
window.removeEventListener('message', windowMessage);
that._token = {
tokenType: msgData.token_type,
accessToken: msgData.access_token,
authenticationToken: msgData.authentication_token,
expiresIn: msgData.expires_in,
scope: msgData.scope,
userId: msgData.user_id
};
that.logger.debug('Auth success');
callback();
} else if (msgData.error) {
that.logger.error('Auth error', msgData.error, msgData.error_description);
callback(msgData.error);
}
};
Backbone.on('popup-closed', popupClosed);
window.addEventListener('message', windowMessage);
},
_getAuthHeader: function() {
return this._token ? this._token.tokenType + ' ' + this._token.accessToken : undefined;
this._oauthAuthorize({
url: url,
callback: callback,
width: 600,
height: 500
});
}
});