keeweb/app/scripts/storage/impl/storage-onedrive.js

245 lines
9.0 KiB
JavaScript
Raw Normal View History

2019-09-15 14:16:32 +02:00
import { StorageBase } from 'storage/storage-base';
import { OneDriveApps } from 'const/cloud-storage-apps';
import { Features } from 'util/features';
2016-03-27 13:54:35 +02:00
// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
2016-03-13 17:08:25 +01:00
2019-09-18 21:26:43 +02:00
class StorageOneDrive extends StorageBase {
name = 'onedrive';
enabled = true;
uipos = 40;
2020-03-29 15:01:11 +02:00
iconSvg = 'onedrive';
2016-03-13 17:08:25 +01:00
2019-09-18 21:26:43 +02:00
_baseUrl = 'https://graph.microsoft.com/v1.0/me';
2016-03-27 13:54:35 +02:00
2019-08-18 10:17:09 +02:00
getPathForName(fileName) {
2016-03-27 19:17:09 +02:00
return '/drive/root:/' + fileName + '.kdbx';
2019-09-18 21:26:43 +02:00
}
2016-03-27 19:17:09 +02:00
2019-08-18 10:17:09 +02:00
load(path, opts, callback) {
2020-06-01 16:53:51 +02:00
this._oauthAuthorize((err) => {
2016-03-27 13:54:35 +02:00
if (err) {
return callback && callback(err);
}
this.logger.debug('Load', path);
2017-01-31 07:50:28 +01:00
const ts = this.logger.ts();
const url = this._baseUrl + path;
this._xhr({
2019-08-18 10:17:09 +02:00
url,
2016-03-27 13:54:35 +02:00
responseType: 'json',
2020-06-01 16:53:51 +02:00
success: (response) => {
2017-11-26 21:43:36 +01:00
const downloadUrl = response['@microsoft.graph.downloadUrl'];
2017-01-31 07:50:28 +01:00
let rev = response.eTag;
2016-03-27 13:54:35 +02:00
if (!downloadUrl || !response.eTag) {
2019-08-18 08:05:38 +02:00
this.logger.debug(
'Load error',
path,
'no download url',
response,
this.logger.ts(ts)
);
2016-03-27 13:54:35 +02:00
return callback && callback('no download url');
}
this._xhr({
2016-03-27 13:54:35 +02:00
url: downloadUrl,
responseType: 'arraybuffer',
skipAuth: true,
success: (response, xhr) => {
2016-03-27 13:54:35 +02:00
rev = xhr.getResponseHeader('ETag') || rev;
this.logger.debug('Loaded', path, rev, this.logger.ts(ts));
2019-08-18 10:17:09 +02:00
return callback && callback(null, response, { rev });
2016-03-27 13:54:35 +02:00
},
2020-06-01 16:53:51 +02:00
error: (err) => {
this.logger.error('Load error', path, err, this.logger.ts(ts));
2016-03-27 13:54:35 +02:00
return callback && callback(err);
}
});
},
2020-06-01 16:53:51 +02:00
error: (err) => {
this.logger.error('Load error', path, err, this.logger.ts(ts));
2016-03-27 13:54:35 +02:00
return callback && callback(err);
}
});
});
2019-09-18 21:26:43 +02:00
}
2016-03-13 17:08:25 +01:00
2019-08-18 10:17:09 +02:00
stat(path, opts, callback) {
2020-06-01 16:53:51 +02:00
this._oauthAuthorize((err) => {
2016-03-27 13:54:35 +02:00
if (err) {
return callback && callback(err);
}
this.logger.debug('Stat', path);
2017-01-31 07:50:28 +01:00
const ts = this.logger.ts();
const url = this._baseUrl + path;
this._xhr({
2019-08-18 10:17:09 +02:00
url,
2016-03-27 13:54:35 +02:00
responseType: 'json',
2020-06-01 16:53:51 +02:00
success: (response) => {
2017-01-31 07:50:28 +01:00
const rev = response.eTag;
2016-03-27 13:54:35 +02:00
if (!rev) {
this.logger.error('Stat error', path, 'no eTag', this.logger.ts(ts));
2016-03-27 13:54:35 +02:00
return callback && callback('no eTag');
}
this.logger.debug('Stated', path, rev, this.logger.ts(ts));
2019-08-18 10:17:09 +02:00
return callback && callback(null, { rev });
2016-03-27 13:54:35 +02:00
},
error: (err, xhr) => {
2016-03-27 19:17:09 +02:00
if (xhr.status === 404) {
this.logger.debug('Stated not found', path, this.logger.ts(ts));
2016-03-27 19:17:09 +02:00
return callback && callback({ notFound: true });
}
this.logger.error('Stat error', path, err, this.logger.ts(ts));
2016-03-27 13:54:35 +02:00
return callback && callback(err);
}
});
});
2019-09-18 21:26:43 +02:00
}
2016-03-27 13:54:35 +02:00
2019-08-18 10:17:09 +02:00
save(path, opts, data, callback, rev) {
2020-06-01 16:53:51 +02:00
this._oauthAuthorize((err) => {
2016-03-27 13:54:35 +02:00
if (err) {
return callback && callback(err);
}
this.logger.debug('Save', path, rev);
2017-01-31 07:50:28 +01:00
const ts = this.logger.ts();
const url = this._baseUrl + path + ':/content';
this._xhr({
2019-08-18 10:17:09 +02:00
url,
2016-03-27 13:54:35 +02:00
method: 'PUT',
responseType: 'json',
2016-03-27 19:17:09 +02:00
headers: rev ? { 'If-Match': rev } : null,
data,
2016-03-27 19:17:09 +02:00
statuses: [200, 201, 412],
success: (response, xhr) => {
2016-03-27 13:54:35 +02:00
rev = response.eTag;
if (!rev) {
this.logger.error('Save error', path, 'no eTag', this.logger.ts(ts));
2016-03-27 13:54:35 +02:00
return callback && callback('no eTag');
}
if (xhr.status === 412) {
this.logger.debug('Save conflict', path, rev, this.logger.ts(ts));
2019-08-18 10:17:09 +02:00
return callback && callback({ revConflict: true }, { rev });
2016-03-27 13:54:35 +02:00
}
this.logger.debug('Saved', path, rev, this.logger.ts(ts));
2019-08-18 10:17:09 +02:00
return callback && callback(null, { rev });
2016-03-27 13:54:35 +02:00
},
2020-06-01 16:53:51 +02:00
error: (err) => {
this.logger.error('Save error', path, err, this.logger.ts(ts));
2016-03-27 13:54:35 +02:00
return callback && callback(err);
}
});
});
2019-09-18 21:26:43 +02:00
}
2016-03-27 13:54:35 +02:00
2019-08-18 10:17:09 +02:00
list(dir, callback) {
2020-06-01 16:53:51 +02:00
this._oauthAuthorize((err) => {
2019-08-16 23:05:39 +02:00
if (err) {
return callback && callback(err);
}
this.logger.debug('List');
2017-01-31 07:50:28 +01:00
const ts = this.logger.ts();
2018-03-13 19:11:18 +01:00
const url = this._baseUrl + (dir ? `${dir}:/children` : '/drive/root/children');
this._xhr({
2019-08-18 10:17:09 +02:00
url,
2016-03-27 13:54:35 +02:00
responseType: 'json',
2020-06-01 16:53:51 +02:00
success: (response) => {
2016-03-27 13:54:35 +02:00
if (!response || !response.value) {
this.logger.error('List error', this.logger.ts(ts), response);
2016-03-27 13:54:35 +02:00
return callback && callback('list error');
}
this.logger.debug('Listed', this.logger.ts(ts));
2017-01-31 07:50:28 +01:00
const fileList = response.value
2020-06-01 16:53:51 +02:00
.filter((f) => f.name)
.map((f) => ({
2016-07-17 13:30:38 +02:00
name: f.name,
path: f.parentReference.path + '/' + f.name,
2017-11-26 21:43:36 +01:00
rev: f.eTag,
dir: !!f.folder
2016-07-17 13:30:38 +02:00
}));
2016-03-27 13:54:35 +02:00
return callback && callback(null, fileList);
},
2020-06-01 16:53:51 +02:00
error: (err) => {
this.logger.error('List error', this.logger.ts(ts), err);
2016-03-27 13:54:35 +02:00
return callback && callback(err);
}
});
});
2019-09-18 21:26:43 +02:00
}
2016-03-27 13:54:35 +02:00
2019-08-18 10:17:09 +02:00
remove(path, callback) {
this.logger.debug('Remove', path);
2017-01-31 07:50:28 +01:00
const ts = this.logger.ts();
const url = this._baseUrl + path;
this._xhr({
2019-08-18 10:17:09 +02:00
url,
2016-03-27 19:17:09 +02:00
method: 'DELETE',
responseType: 'json',
statuses: [200, 204],
success: () => {
this.logger.debug('Removed', path, this.logger.ts(ts));
2016-03-27 19:17:09 +02:00
return callback && callback();
},
2020-06-01 16:53:51 +02:00
error: (err) => {
this.logger.error('Remove error', path, err, this.logger.ts(ts));
2016-03-27 19:17:09 +02:00
return callback && callback(err);
}
});
2019-09-18 21:26:43 +02:00
}
2016-03-27 19:17:09 +02:00
2019-08-18 10:17:09 +02:00
mkdir(path, callback) {
2020-06-01 16:53:51 +02:00
this._oauthAuthorize((err) => {
2019-08-16 23:05:39 +02:00
if (err) {
return callback && callback(err);
}
2016-08-21 17:43:59 +02:00
this.logger.debug('Make dir', path);
2017-01-31 07:50:28 +01:00
const ts = this.logger.ts();
const url = this._baseUrl + '/drive/root/children';
const data = JSON.stringify({ name: path.replace('/drive/root:/', ''), folder: {} });
2016-08-21 17:43:59 +02:00
this._xhr({
2019-08-18 10:17:09 +02:00
url,
2016-08-21 17:43:59 +02:00
method: 'POST',
responseType: 'json',
statuses: [200, 204],
data,
dataType: 'application/json',
2016-08-21 17:43:59 +02:00
success: () => {
this.logger.debug('Made dir', path, this.logger.ts(ts));
return callback && callback();
},
2020-06-01 16:53:51 +02:00
error: (err) => {
2016-08-21 17:43:59 +02:00
this.logger.error('Make dir error', path, err, this.logger.ts(ts));
return callback && callback(err);
}
});
});
2019-09-18 21:26:43 +02:00
}
2016-08-21 17:43:59 +02:00
2020-04-17 21:36:56 +02:00
logout(enabled) {
this._oauthRevokeToken();
2019-09-18 21:26:43 +02:00
}
_getOAuthConfig() {
2019-09-17 19:50:42 +02:00
let clientId = this.appSettings.onedriveClientId;
let clientSecret = this.appSettings.onedriveClientSecret;
if (!clientId || !clientSecret) {
if (Features.isLocal) {
({ id: clientId, secret: clientSecret } = OneDriveApps.Local);
} else {
({ id: clientId, secret: clientSecret } = OneDriveApps.Production);
}
2016-03-27 14:06:27 +02:00
}
2016-03-27 15:18:05 +02:00
return {
2017-11-26 21:43:36 +01:00
url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
scope: 'files.readwrite offline_access',
2019-08-18 10:17:09 +02:00
clientId,
clientSecret,
pkce: true,
2016-03-27 14:57:22 +02:00
width: 600,
height: 500
2016-03-27 15:18:05 +02:00
};
2019-09-18 21:26:43 +02:00
}
}
2016-03-13 17:08:25 +01:00
2019-09-15 14:16:32 +02:00
export { StorageOneDrive };