2016-03-13 17:08:25 +01:00
|
|
|
'use strict';
|
|
|
|
|
2016-03-27 09:06:23 +02:00
|
|
|
var StorageBase = require('./storage-base'),
|
|
|
|
Backbone = require('backbone'),
|
2016-03-26 21:12:56 +01:00
|
|
|
Timeouts = require('../const/timeouts');
|
2016-03-13 17:08:25 +01:00
|
|
|
|
2016-03-27 11:08:54 +02:00
|
|
|
var GDriveClinetId = '847548101761-koqkji474gp3i2gn3k5omipbfju7pbt1.apps.googleusercontent.com';
|
|
|
|
|
2016-03-27 09:06:23 +02:00
|
|
|
var StorageGDrive = StorageBase.extend({
|
2016-03-13 17:08:25 +01:00
|
|
|
name: 'gdrive',
|
2016-03-26 23:04:34 +01:00
|
|
|
enabled: true,
|
2016-03-13 17:08:25 +01:00
|
|
|
uipos: 30,
|
|
|
|
iconSvg: '<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" version="1.1">' +
|
|
|
|
'<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>',
|
|
|
|
|
2016-03-27 11:08:54 +02:00
|
|
|
_gapi: null,
|
|
|
|
|
2016-03-13 17:08:25 +01:00
|
|
|
load: function(path, opts, callback) {
|
2016-03-26 21:12:56 +01:00
|
|
|
var that = this;
|
|
|
|
that.stat(path, opts, function(err, stat) {
|
|
|
|
if (err) { return callback && callback(err); }
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Load', path);
|
|
|
|
var ts = that.logger.ts();
|
2016-03-26 21:12:56 +01:00
|
|
|
var url = 'https://www.googleapis.com/drive/v3/files/{id}/revisions/{rev}?alt=media'
|
|
|
|
.replace('{id}', path)
|
|
|
|
.replace('{rev}', stat.rev);
|
2016-03-27 09:42:48 +02:00
|
|
|
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 });
|
|
|
|
},
|
|
|
|
error: function(err) {
|
|
|
|
that.logger.debug('Load error', path, err, that.logger.ts(ts));
|
|
|
|
return callback && callback(err);
|
2016-03-26 21:12:56 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2016-03-13 17:08:25 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
stat: function(path, opts, callback) {
|
2016-03-26 21:12:56 +01:00
|
|
|
var that = this;
|
|
|
|
this._getClient(function(err) {
|
|
|
|
if (err) {
|
|
|
|
return callback && callback(err);
|
|
|
|
}
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Stat', path);
|
|
|
|
var ts = that.logger.ts();
|
2016-03-27 09:42:48 +02:00
|
|
|
var url = 'https://www.googleapis.com/drive/v3/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));
|
|
|
|
return callback && callback(null, { rev: rev });
|
|
|
|
},
|
|
|
|
error: function(err) {
|
2016-03-27 09:46:43 +02:00
|
|
|
that.logger.error('Stat error', that.logger.ts(ts), err);
|
2016-03-27 09:42:48 +02:00
|
|
|
return callback && callback(err);
|
|
|
|
}
|
2016-03-26 21:12:56 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
save: function(path, opts, data, callback, rev) {
|
|
|
|
var that = this;
|
|
|
|
that.stat(path, opts, function(err, stat) {
|
|
|
|
if (rev) {
|
|
|
|
if (err) { return callback && callback(err); }
|
|
|
|
if (stat.rev !== rev) {
|
|
|
|
return callback && callback({revConflict: true}, stat);
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Save', path);
|
|
|
|
var ts = that.logger.ts();
|
2016-03-26 21:12:56 +01:00
|
|
|
var url = 'https://www.googleapis.com/upload/drive/v3/files/{id}?uploadType=media&fields=headRevisionId'
|
|
|
|
.replace('{id}', path)
|
|
|
|
.replace('{rev}', stat.rev);
|
2016-03-27 09:42:48 +02:00
|
|
|
that._xhr({
|
|
|
|
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));
|
|
|
|
var newRev = response.headRevisionId;
|
|
|
|
if (!newRev) {
|
|
|
|
return callback && callback('save error: no rev');
|
|
|
|
}
|
|
|
|
return callback && callback(null, { rev: newRev });
|
|
|
|
},
|
|
|
|
error: function(err) {
|
|
|
|
that.logger.debug('Save error', path, err, that.logger.ts(ts));
|
|
|
|
return callback && callback(err);
|
2016-03-26 21:12:56 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
list: function(callback) {
|
|
|
|
var that = this;
|
|
|
|
this._getClient(function(err) {
|
|
|
|
if (err) { return callback && callback(err); }
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('List');
|
2016-03-27 09:42:48 +02:00
|
|
|
var url = 'https://www.googleapis.com/drive/v3/files?fields={fields}&q={q}'
|
|
|
|
.replace('{fields}', encodeURIComponent('files'))
|
|
|
|
.replace('{q}', encodeURIComponent('fileExtension="kdbx" and mimeType="application/octet-stream" and trashed=false'));
|
2016-03-27 09:06:23 +02:00
|
|
|
var ts = that.logger.ts();
|
2016-03-27 09:42:48 +02:00
|
|
|
that._xhr({
|
|
|
|
url: url,
|
|
|
|
responseType: 'json',
|
|
|
|
headers: { 'Authorization': that._getAuthHeader() },
|
|
|
|
success: function(response) {
|
|
|
|
if (!response) {
|
2016-03-27 13:54:35 +02:00
|
|
|
that.logger.error('List error', that.logger.ts(ts));
|
2016-03-27 09:42:48 +02:00
|
|
|
return callback && callback('list error');
|
|
|
|
}
|
2016-03-27 13:54:35 +02:00
|
|
|
that.logger.debug('Listed', that.logger.ts(ts));
|
2016-03-27 09:42:48 +02:00
|
|
|
var fileList = response.files.map(function(f) {
|
|
|
|
return {
|
|
|
|
name: f.name,
|
|
|
|
path: f.id,
|
|
|
|
rev: f.headRevisionId
|
|
|
|
};
|
|
|
|
});
|
|
|
|
return callback && callback(null, fileList);
|
|
|
|
},
|
|
|
|
error: function(err) {
|
|
|
|
that.logger.error('List error', that.logger.ts(ts), err);
|
|
|
|
return callback && callback(err);
|
2016-03-26 21:12:56 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_getClient: function(callback) {
|
|
|
|
var that = this;
|
2016-03-27 09:42:48 +02:00
|
|
|
if (that._gapi) {
|
2016-03-26 21:12:56 +01:00
|
|
|
return that._authorize(callback);
|
|
|
|
}
|
|
|
|
that._gapiLoadTimeout = setTimeout(function() {
|
|
|
|
callback('Gdrive api load timeout');
|
|
|
|
delete that._gapiLoadTimeout;
|
|
|
|
}, Timeouts.ScriptLoad);
|
|
|
|
if (that._gapi) {
|
2016-03-27 09:42:48 +02:00
|
|
|
that._authorize.bind(callback);
|
2016-03-26 21:12:56 +01:00
|
|
|
} else {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Loading gapi client');
|
2016-03-26 21:12:56 +01:00
|
|
|
window.gApiClientLoaded = function() {
|
|
|
|
if (that._gapiLoadTimeout) {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Loaded gapi client');
|
2016-03-26 21:12:56 +01:00
|
|
|
delete window.gDriveClientLoaded;
|
|
|
|
that._gapi = window.gapi;
|
2016-03-27 09:42:48 +02:00
|
|
|
that._authorize(callback);
|
2016-03-26 21:12:56 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
$.getScript('https://apis.google.com/js/client.js?onload=gApiClientLoaded', function() {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Loaded gapi script');
|
2016-03-26 21:12:56 +01:00
|
|
|
}).fail(function() {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.error('Failed to load gapi');
|
2016-03-26 21:12:56 +01:00
|
|
|
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()) {
|
|
|
|
return callback();
|
|
|
|
}
|
2016-03-27 11:08:54 +02:00
|
|
|
var clientId = this.appSettings.get('gdriveClientId') || GDriveClinetId;
|
|
|
|
that._gapi.auth.authorize({'client_id': clientId, scope: scopes, immediate: true}, function(res) {
|
2016-03-26 21:12:56 +01:00
|
|
|
if (res && !res.error) {
|
|
|
|
callback();
|
|
|
|
} else {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Authorizing...');
|
2016-03-26 21:12:56 +01:00
|
|
|
var handlePopupClosed = function() {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Auth popup closed');
|
2016-03-26 21:12:56 +01:00
|
|
|
Backbone.off('popup-closed', handlePopupClosed);
|
|
|
|
callback('popup closed');
|
|
|
|
callback = null;
|
|
|
|
};
|
|
|
|
Backbone.on('popup-closed', handlePopupClosed);
|
2016-03-27 09:06:23 +02:00
|
|
|
var ts = that.logger.ts();
|
2016-03-27 11:08:54 +02:00
|
|
|
that._gapi.auth.authorize({'client_id': clientId, scope: scopes, immediate: false}, function(res) {
|
2016-03-26 21:12:56 +01:00
|
|
|
if (!callback) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Backbone.off('popup-closed', handlePopupClosed);
|
|
|
|
if (res && !res.error) {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.debug('Authorized', that.logger.ts(ts));
|
2016-03-26 21:12:56 +01:00
|
|
|
callback();
|
|
|
|
} else {
|
2016-03-27 09:06:23 +02:00
|
|
|
that.logger.error('Authorize error', that.logger.ts(ts), res);
|
2016-03-26 21:12:56 +01:00
|
|
|
callback(res && res.error || 'authorize error');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2016-03-13 17:08:25 +01:00
|
|
|
},
|
|
|
|
|
2016-03-26 21:12:56 +01:00
|
|
|
_getAuthHeader: function() {
|
|
|
|
// jshint camelcase:false
|
|
|
|
var token = this._gapi.auth.getToken();
|
|
|
|
return token.token_type + ' ' + token.access_token;
|
2016-03-13 17:08:25 +01:00
|
|
|
}
|
2016-03-27 09:06:23 +02:00
|
|
|
});
|
2016-03-13 17:08:25 +01:00
|
|
|
|
2016-03-27 09:46:43 +02:00
|
|
|
module.exports = new StorageGDrive();
|