mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Merge branch 'release-1.6' into dev
This commit is contained in:
commit
3ecf942b44
@ -933,6 +933,9 @@ packages/lib/PoorManIntervals.js.map
|
||||
packages/lib/SyncTargetJoplinServer.d.ts
|
||||
packages/lib/SyncTargetJoplinServer.js
|
||||
packages/lib/SyncTargetJoplinServer.js.map
|
||||
packages/lib/SyncTargetOneDrive.d.ts
|
||||
packages/lib/SyncTargetOneDrive.js
|
||||
packages/lib/SyncTargetOneDrive.js.map
|
||||
packages/lib/Synchronizer.d.ts
|
||||
packages/lib/Synchronizer.js
|
||||
packages/lib/Synchronizer.js.map
|
||||
@ -996,6 +999,9 @@ packages/lib/models/utils/types.js.map
|
||||
packages/lib/ntpDate.d.ts
|
||||
packages/lib/ntpDate.js
|
||||
packages/lib/ntpDate.js.map
|
||||
packages/lib/onedrive-api.d.ts
|
||||
packages/lib/onedrive-api.js
|
||||
packages/lib/onedrive-api.js.map
|
||||
packages/lib/path-utils.d.ts
|
||||
packages/lib/path-utils.js
|
||||
packages/lib/path-utils.js.map
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -921,6 +921,9 @@ packages/lib/PoorManIntervals.js.map
|
||||
packages/lib/SyncTargetJoplinServer.d.ts
|
||||
packages/lib/SyncTargetJoplinServer.js
|
||||
packages/lib/SyncTargetJoplinServer.js.map
|
||||
packages/lib/SyncTargetOneDrive.d.ts
|
||||
packages/lib/SyncTargetOneDrive.js
|
||||
packages/lib/SyncTargetOneDrive.js.map
|
||||
packages/lib/Synchronizer.d.ts
|
||||
packages/lib/Synchronizer.js
|
||||
packages/lib/Synchronizer.js.map
|
||||
@ -984,6 +987,9 @@ packages/lib/models/utils/types.js.map
|
||||
packages/lib/ntpDate.d.ts
|
||||
packages/lib/ntpDate.js
|
||||
packages/lib/ntpDate.js.map
|
||||
packages/lib/onedrive-api.d.ts
|
||||
packages/lib/onedrive-api.js
|
||||
packages/lib/onedrive-api.js.map
|
||||
packages/lib/path-utils.d.ts
|
||||
packages/lib/path-utils.js
|
||||
packages/lib/path-utils.js.map
|
||||
|
@ -34,7 +34,7 @@ Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.6.6/Jo
|
||||
|
||||
Operating System | Download | Alt. Download
|
||||
---|---|---
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplinapp.org/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.4.11/joplin-v1.4.11.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.4.11/joplin-v1.4.11-32bit.apk)
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplinapp.org/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.6.7/joplin-v1.6.7.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.6.7/joplin-v1.6.7-32bit.apk)
|
||||
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplinapp.org/images/BadgeIOS.png'/></a> | -
|
||||
|
||||
## Terminal application
|
||||
|
2
packages/app-cli/package-lock.json
generated
2
packages/app-cli/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "1.6.3",
|
||||
"version": "1.6.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -39,8 +39,8 @@
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/lib": "*",
|
||||
"@joplin/renderer": "*",
|
||||
"@joplin/lib": "1.0.18",
|
||||
"@joplin/renderer": "1.0.26",
|
||||
"aws-sdk": "^2.588.0",
|
||||
"chalk": "^4.1.0",
|
||||
"clean-html": "^1.5.0",
|
||||
|
@ -16,6 +16,8 @@ import KeychainServiceDriverDummy from '@joplin/lib/services/keychain/KeychainSe
|
||||
import PluginRunner from '../app/services/plugins/PluginRunner';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import FileApiDriverJoplinServer from '@joplin/lib/file-api-driver-joplinServer';
|
||||
import OneDriveApi from '@joplin/lib/onedrive-api';
|
||||
import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const { JoplinDatabase } = require('@joplin/lib/joplin-database.js');
|
||||
@ -40,7 +42,6 @@ const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
|
||||
const SyncTargetMemory = require('@joplin/lib/SyncTargetMemory.js');
|
||||
const SyncTargetFilesystem = require('@joplin/lib/SyncTargetFilesystem.js');
|
||||
const SyncTargetOneDrive = require('@joplin/lib/SyncTargetOneDrive.js');
|
||||
const SyncTargetNextcloud = require('@joplin/lib/SyncTargetNextcloud.js');
|
||||
const SyncTargetDropbox = require('@joplin/lib/SyncTargetDropbox.js');
|
||||
const SyncTargetAmazonS3 = require('@joplin/lib/SyncTargetAmazonS3.js');
|
||||
@ -52,7 +53,6 @@ const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher.js');
|
||||
const WebDavApi = require('@joplin/lib/WebDavApi');
|
||||
const DropboxApi = require('@joplin/lib/DropboxApi');
|
||||
const JoplinServerApi = require('@joplin/lib/JoplinServerApi2').default;
|
||||
const { OneDriveApi } = require('@joplin/lib/onedrive-api');
|
||||
const { loadKeychainServiceAndSettings } = require('@joplin/lib/services/SettingUtils');
|
||||
const md5 = require('md5');
|
||||
const S3 = require('aws-sdk/clients/s3');
|
||||
|
@ -141,7 +141,7 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097620
|
||||
versionCode 2097621
|
||||
versionName "1.7.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
|
@ -338,7 +338,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 60;
|
||||
CURRENT_PROJECT_VERSION = 61;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
@ -365,7 +365,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 60;
|
||||
CURRENT_PROJECT_VERSION = 61;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
|
@ -25,6 +25,7 @@ import { loadKeychainServiceAndSettings } from '@joplin/lib/services/SettingUtil
|
||||
import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile';
|
||||
import { setLocale, closestSupportedLocale, defaultLocale } from '@joplin/lib/locale';
|
||||
import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer';
|
||||
import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
|
||||
|
||||
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar } = require('react-native');
|
||||
|
||||
@ -75,7 +76,6 @@ const WelcomeUtils = require('@joplin/lib/WelcomeUtils');
|
||||
const { themeStyle } = require('./components/global-style.js');
|
||||
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
|
||||
const SyncTargetOneDrive = require('@joplin/lib/SyncTargetOneDrive.js');
|
||||
const SyncTargetFilesystem = require('@joplin/lib/SyncTargetFilesystem.js');
|
||||
const SyncTargetNextcloud = require('@joplin/lib/SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('@joplin/lib/SyncTargetWebDAV.js');
|
||||
|
@ -7,6 +7,7 @@ import KeychainServiceDriver from './services/keychain/KeychainServiceDriver.nod
|
||||
import { _, setLocale } from './locale';
|
||||
import KvStore from './services/KvStore';
|
||||
import SyncTargetJoplinServer from './SyncTargetJoplinServer';
|
||||
import SyncTargetOneDrive from './SyncTargetOneDrive';
|
||||
|
||||
const { createStore, applyMiddleware } = require('redux');
|
||||
const { defaultState, stateUtils } = require('./reducer');
|
||||
@ -30,7 +31,6 @@ const EventEmitter = require('events');
|
||||
const syswidecas = require('./vendor/syswide-cas');
|
||||
const SyncTargetRegistry = require('./SyncTargetRegistry.js');
|
||||
const SyncTargetFilesystem = require('./SyncTargetFilesystem.js');
|
||||
const SyncTargetOneDrive = require('./SyncTargetOneDrive.js');
|
||||
const SyncTargetNextcloud = require('./SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('./SyncTargetWebDAV.js');
|
||||
const SyncTargetDropbox = require('./SyncTargetDropbox.js');
|
||||
|
@ -2,6 +2,9 @@ const moment = require('moment');
|
||||
const time = require('./time').default;
|
||||
const { FsDriverDummy } = require('./fs-driver-dummy.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const Mutex = require('async-mutex').Mutex;
|
||||
|
||||
const writeToFileMutex_ = new Mutex();
|
||||
|
||||
export enum TargetType {
|
||||
Database = 'database',
|
||||
@ -205,12 +208,23 @@ class Logger {
|
||||
const line = [timestamp];
|
||||
if (targetPrefix) line.push(targetPrefix);
|
||||
line.push(this.objectsToString(...object));
|
||||
try {
|
||||
// TODO: Should log async
|
||||
Logger.fsDriver().appendFileSync(target.path, `${line.join(': ')}\n`);
|
||||
} catch (error) {
|
||||
|
||||
// Write to file using a mutex so that log entries appear in the
|
||||
// correct order (otherwise, since the async call is not awaited
|
||||
// by caller, multiple log call in a row are not guaranteed to
|
||||
// appear in the right order). We also can't use a sync call
|
||||
// because that would slow down the main process, especially
|
||||
// when many log operations are being done (eg. during sync in
|
||||
// dev mode).
|
||||
let release: Function = null;
|
||||
writeToFileMutex_.acquire().then((r: Function) => {
|
||||
release = r;
|
||||
return Logger.fsDriver().appendFile(target.path, `${line.join(': ')}\n`, 'utf8');
|
||||
}).catch((error: any) => {
|
||||
console.error('Cannot write to log file:', error);
|
||||
}
|
||||
}).finally(() => {
|
||||
if (release) release();
|
||||
});
|
||||
} else if (target.type == 'database') {
|
||||
const msg = [];
|
||||
if (targetPrefix) msg.push(targetPrefix);
|
||||
|
@ -1,18 +1,20 @@
|
||||
import OneDriveApi from './onedrive-api';
|
||||
import { _ } from './locale';
|
||||
import Setting from './models/Setting';
|
||||
import Synchronizer from './Synchronizer';
|
||||
|
||||
const BaseSyncTarget = require('./BaseSyncTarget.js');
|
||||
const { _ } = require('./locale');
|
||||
const { OneDriveApi } = require('./onedrive-api.js');
|
||||
const Setting = require('./models/Setting').default;
|
||||
const { parameters } = require('./parameters.js');
|
||||
const { FileApi } = require('./file-api.js');
|
||||
const Synchronizer = require('./Synchronizer').default;
|
||||
const { FileApiDriverOneDrive } = require('./file-api-driver-onedrive.js');
|
||||
|
||||
class SyncTargetOneDrive extends BaseSyncTarget {
|
||||
export default class SyncTargetOneDrive extends BaseSyncTarget {
|
||||
|
||||
static id() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
constructor(db, options = null) {
|
||||
constructor(db: any, options: any = null) {
|
||||
super(db, options);
|
||||
this.api_ = null;
|
||||
}
|
||||
@ -58,9 +60,8 @@ class SyncTargetOneDrive extends BaseSyncTarget {
|
||||
const isPublic = Setting.value('appType') != 'cli' && Setting.value('appType') != 'desktop';
|
||||
|
||||
this.api_ = new OneDriveApi(this.oneDriveParameters().id, this.oneDriveParameters().secret, isPublic);
|
||||
this.api_.setLogger(this.logger());
|
||||
|
||||
this.api_.on('authRefreshed', a => {
|
||||
this.api_.on('authRefreshed', (a: any) => {
|
||||
this.logger().info('Saving updated OneDrive auth.');
|
||||
Setting.setValue(`sync.${this.syncTargetId()}.auth`, a ? JSON.stringify(a) : null);
|
||||
});
|
||||
@ -110,5 +111,3 @@ class SyncTargetOneDrive extends BaseSyncTarget {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SyncTargetOneDrive;
|
@ -1,27 +0,0 @@
|
||||
const SyncTargetOneDrive = require('./SyncTargetOneDrive.js');
|
||||
const { _ } = require('./locale');
|
||||
const { parameters } = require('./parameters.js');
|
||||
|
||||
class SyncTargetOneDriveDev extends SyncTargetOneDrive {
|
||||
static id() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
static targetName() {
|
||||
return 'onedrive_dev';
|
||||
}
|
||||
|
||||
static label() {
|
||||
return _('OneDrive Dev (For testing only)');
|
||||
}
|
||||
|
||||
syncTargetId() {
|
||||
return SyncTargetOneDriveDev.id();
|
||||
}
|
||||
|
||||
oneDriveParameters() {
|
||||
return parameters('dev').oneDrive;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SyncTargetOneDriveDev;
|
@ -1,4 +1,5 @@
|
||||
const moment = require('moment');
|
||||
const { basicDelta } = require('./file-api');
|
||||
const { dirname, basename } = require('./path-utils');
|
||||
const shim = require('./shim').default;
|
||||
const Buffer = require('buffer').Buffer;
|
||||
@ -83,10 +84,12 @@ class FileApiDriverOneDrive {
|
||||
context: null,
|
||||
}, options);
|
||||
|
||||
let query = this.itemFilter_();
|
||||
let query = Object.assign({}, this.itemFilter_(), { '$top': 1000 });
|
||||
let url = `${this.makePath_(path)}:/children`;
|
||||
|
||||
if (options.context) {
|
||||
// If there's a context, it already includes all required query
|
||||
// parameters, including $top
|
||||
query = null;
|
||||
url = options.context;
|
||||
}
|
||||
@ -213,6 +216,24 @@ class FileApiDriverOneDrive {
|
||||
}
|
||||
|
||||
async delta(path, options = null) {
|
||||
const getDirStats = async path => {
|
||||
let items = [];
|
||||
let context = null;
|
||||
|
||||
while (true) {
|
||||
const result = await this.list(path, { includeDirs: false, context: context });
|
||||
items = items.concat(result.items);
|
||||
context = result.context;
|
||||
if (!result.hasMore) break;
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
return await basicDelta(path, getDirStats, options);
|
||||
}
|
||||
|
||||
async delta_BROKEN(path, options = null) {
|
||||
const output = {
|
||||
hasMore: false,
|
||||
context: {},
|
||||
|
@ -1,17 +1,28 @@
|
||||
const shim = require('./shim').default;
|
||||
import shim from './shim';
|
||||
import time from './time';
|
||||
import Logger from './Logger';
|
||||
import { _ } from './locale';
|
||||
|
||||
const { stringify } = require('query-string');
|
||||
const time = require('./time').default;
|
||||
const Logger = require('./Logger').default;
|
||||
const { _ } = require('./locale');
|
||||
const urlUtils = require('./urlUtils.js');
|
||||
const Buffer = require('buffer').Buffer;
|
||||
|
||||
class OneDriveApi {
|
||||
const logger = Logger.create('OneDriveApi');
|
||||
|
||||
export default class OneDriveApi {
|
||||
|
||||
private clientId_: string;
|
||||
private clientSecret_: string;
|
||||
private auth_: any = null;
|
||||
private accountProperties_: any = null;
|
||||
private isPublic_: boolean;
|
||||
private listeners_: Record<string, any>;
|
||||
|
||||
// `isPublic` is to tell OneDrive whether the application is a "public" one (Mobile and desktop
|
||||
// apps are considered "public"), in which case the secret should not be sent to the API.
|
||||
// In practice the React Native app is public, and the Node one is not because we
|
||||
// use a local server for the OAuth dance.
|
||||
constructor(clientId, clientSecret, isPublic) {
|
||||
constructor(clientId: string, clientSecret: string, isPublic: boolean) {
|
||||
this.clientId_ = clientId;
|
||||
this.clientSecret_ = clientSecret;
|
||||
this.auth_ = null;
|
||||
@ -20,29 +31,20 @@ class OneDriveApi {
|
||||
this.listeners_ = {
|
||||
authRefreshed: [],
|
||||
};
|
||||
this.logger_ = new Logger();
|
||||
}
|
||||
|
||||
setLogger(l) {
|
||||
this.logger_ = l;
|
||||
}
|
||||
|
||||
logger() {
|
||||
return this.logger_;
|
||||
}
|
||||
|
||||
isPublic() {
|
||||
return this.isPublic_;
|
||||
}
|
||||
|
||||
dispatch(eventName, param) {
|
||||
dispatch(eventName: string, param: any) {
|
||||
const ls = this.listeners_[eventName];
|
||||
for (let i = 0; i < ls.length; i++) {
|
||||
ls[i](param);
|
||||
}
|
||||
}
|
||||
|
||||
on(eventName, callback) {
|
||||
on(eventName: string, callback: Function) {
|
||||
this.listeners_[eventName].push(callback);
|
||||
}
|
||||
|
||||
@ -54,11 +56,11 @@ class OneDriveApi {
|
||||
return 'https://login.microsoftonline.com/common/oauth2/nativeclient';
|
||||
}
|
||||
|
||||
auth() {
|
||||
auth(): any {
|
||||
return this.auth_;
|
||||
}
|
||||
|
||||
setAuth(auth) {
|
||||
setAuth(auth: any) {
|
||||
this.auth_ = auth;
|
||||
this.dispatch('authRefreshed', this.auth());
|
||||
}
|
||||
@ -81,7 +83,7 @@ class OneDriveApi {
|
||||
return `${r.parentReference.path}/${r.name}`;
|
||||
}
|
||||
|
||||
authCodeUrl(redirectUri) {
|
||||
authCodeUrl(redirectUri: string) {
|
||||
const query = {
|
||||
client_id: this.clientId_,
|
||||
scope: 'files.readwrite offline_access sites.readwrite.all',
|
||||
@ -91,8 +93,8 @@ class OneDriveApi {
|
||||
return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${stringify(query)}`;
|
||||
}
|
||||
|
||||
async execTokenRequest(code, redirectUri) {
|
||||
const body = {};
|
||||
async execTokenRequest(code: string, redirectUri: string) {
|
||||
const body: any = {};
|
||||
body['client_id'] = this.clientId();
|
||||
if (!this.isPublic()) body['client_secret'] = this.clientSecret();
|
||||
body['code'] = code;
|
||||
@ -123,12 +125,12 @@ class OneDriveApi {
|
||||
}
|
||||
}
|
||||
|
||||
oneDriveErrorResponseToError(errorResponse) {
|
||||
oneDriveErrorResponseToError(errorResponse: any) {
|
||||
if (!errorResponse) return new Error('Undefined error');
|
||||
|
||||
if (errorResponse.error) {
|
||||
const e = errorResponse.error;
|
||||
const output = new Error(e.message);
|
||||
const output: any = new Error(e.message);
|
||||
if (e.code) output.code = e.code;
|
||||
if (e.innerError) output.innerError = e.innerError;
|
||||
return output;
|
||||
@ -137,7 +139,7 @@ class OneDriveApi {
|
||||
}
|
||||
}
|
||||
|
||||
async uploadChunk(url, handle, buffer, options) {
|
||||
async uploadChunk(url: string, handle: any, buffer: any, options: any) {
|
||||
options = Object.assign({}, options);
|
||||
if (!options.method) { options.method = 'POST'; }
|
||||
|
||||
@ -159,7 +161,7 @@ class OneDriveApi {
|
||||
return response;
|
||||
}
|
||||
|
||||
async uploadBigFile(url, options) {
|
||||
async uploadBigFile(url: string, options: any) {
|
||||
const response = await shim.fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -199,7 +201,7 @@ class OneDriveApi {
|
||||
endByte = (i + 1) * chunkSize - 1;
|
||||
contentLength = chunkSize;
|
||||
}
|
||||
this.logger().debug(`Uploading File Fragment ${(startByte / 1048576).toFixed(2)} - ${(endByte / 1048576).toFixed(2)} from ${(byteSize / 1048576).toFixed(2)} Mbit ...`);
|
||||
logger.debug(`Uploading File Fragment ${(startByte / 1048576).toFixed(2)} - ${(endByte / 1048576).toFixed(2)} from ${(byteSize / 1048576).toFixed(2)} Mbit ...`);
|
||||
const headers = {
|
||||
'Content-Length': contentLength,
|
||||
'Content-Range': `bytes ${startByte}-${endByte}/${byteSize}`,
|
||||
@ -215,7 +217,7 @@ class OneDriveApi {
|
||||
return { ok: true };
|
||||
} catch (error) {
|
||||
const type = (handle) ? 'Resource' : 'Note Content';
|
||||
this.logger().error(`Couldn't upload ${type} > 4 Mb. Got unhandled error:`, error ? error.code : '', error ? error.message : '', error);
|
||||
logger.error(`Couldn't upload ${type} > 4 Mb. Got unhandled error:`, error ? error.code : '', error ? error.message : '', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (handle) await shim.fsDriver().close(handle);
|
||||
@ -224,7 +226,7 @@ class OneDriveApi {
|
||||
}
|
||||
}
|
||||
|
||||
async exec(method, path, query = null, data = null, options = null) {
|
||||
async exec(method: string, path: string, query: any = null, data: any = null, options: any = null) {
|
||||
if (!path) throw new Error('Path is required');
|
||||
|
||||
method = method.toUpperCase();
|
||||
@ -264,6 +266,12 @@ class OneDriveApi {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
options.headers['Authorization'] = `bearer ${this.token()}`;
|
||||
|
||||
const handleRequestRepeat = async (error: any) => {
|
||||
logger.info(`Got error below - retrying (${i})...`);
|
||||
logger.info(error);
|
||||
await time.sleep((i + 1) * 5);
|
||||
};
|
||||
|
||||
let response = null;
|
||||
try {
|
||||
if (path.includes('/createUploadSession')) {
|
||||
@ -277,24 +285,31 @@ class OneDriveApi {
|
||||
response = await shim.fetchBlob(url, options);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger().error('Got unhandled error:', error ? error.code : '', error ? error.message : '', error);
|
||||
if (shim.fetchRequestCanBeRetried(error)) {
|
||||
await handleRequestRepeat(error);
|
||||
continue;
|
||||
} else {
|
||||
logger.error('Got unhandled error:', error ? error.code : '', error ? error.message : '', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorResponseText = await response.text();
|
||||
let errorResponse = null;
|
||||
|
||||
try {
|
||||
errorResponse = JSON.parse(errorResponseText); // await response.json();
|
||||
} catch (error) {
|
||||
error.message = `OneDriveApi::exec: Cannot parse JSON error: ${errorResponseText} ${error.message}`;
|
||||
throw error;
|
||||
await handleRequestRepeat(error);
|
||||
continue;
|
||||
}
|
||||
|
||||
const error = this.oneDriveErrorResponseToError(errorResponse);
|
||||
|
||||
if (error.code == 'InvalidAuthenticationToken' || error.code == 'unauthenticated') {
|
||||
this.logger().info('Token expired: refreshing...');
|
||||
logger.info('Token expired: refreshing...');
|
||||
await this.refreshAccessToken();
|
||||
continue;
|
||||
} else if (error && ((error.error && error.error.code == 'generalException') || error.code == 'generalException' || error.code == 'EAGAIN')) {
|
||||
@ -312,9 +327,7 @@ class OneDriveApi {
|
||||
// type: 'system',
|
||||
// errno: 'EAGAIN',
|
||||
// code: 'EAGAIN' }
|
||||
this.logger().info(`Got error below - retrying (${i})...`);
|
||||
this.logger().info(error);
|
||||
await time.sleep((i + 1) * 3);
|
||||
await handleRequestRepeat(error);
|
||||
continue;
|
||||
} else if (error && (error.code === 'resourceModified' || (error.error && error.error.code === 'resourceModified'))) {
|
||||
// NOTE: not tested, very hard to reproduce and non-informative error message, but can be repeated
|
||||
@ -324,9 +337,7 @@ class OneDriveApi {
|
||||
// Header: {"_headers":{"cache-control":["private"],"transfer-encoding":["chunked"],"content-type":["application/json"],"request-id":["d...ea47"],"client-request-id":["d99...ea47"],"x-ms-ags-diagnostic":["{\"ServerInfo\":{\"DataCenter\":\"North Europe\",\"Slice\":\"SliceA\",\"Ring\":\"2\",\"ScaleUnit\":\"000\",\"Host\":\"AGSFE_IN_13\",\"ADSiteName\":\"DUB\"}}"],"duration":["96.9464"],"date":[],"connection":["close"]}}
|
||||
// Request: PATCH https://graph.microsoft.com/v1.0/drive/root:/Apps/JoplinDev/f56c5601fee94b8085524513bf3e352f.md null "{\"fileSystemInfo\":{\"lastModifiedDateTime\":\"....\"}}" {"headers":{"Content-Type":"application/json","Authorization":"bearer ...
|
||||
|
||||
this.logger().info(`Got error below - retrying (${i})...`);
|
||||
this.logger().info(error);
|
||||
await time.sleep((i + 1) * 3);
|
||||
await handleRequestRepeat(error);
|
||||
continue;
|
||||
} else if (error.code == 'itemNotFound' && method == 'DELETE') {
|
||||
// Deleting a non-existing item is ok - noop
|
||||
@ -344,7 +355,7 @@ class OneDriveApi {
|
||||
throw new Error(`Could not execute request after multiple attempts: ${method} ${url}`);
|
||||
}
|
||||
|
||||
setAccountProperties(accountProperties) {
|
||||
setAccountProperties(accountProperties: any) {
|
||||
this.accountProperties_ = accountProperties;
|
||||
}
|
||||
|
||||
@ -360,7 +371,7 @@ class OneDriveApi {
|
||||
}
|
||||
}
|
||||
|
||||
async execJson(method, path, query, data) {
|
||||
async execJson(method: string, path: string, query: any = null, data: any = null) {
|
||||
const response = await this.exec(method, path, query, data);
|
||||
const errorResponseText = await response.text();
|
||||
try {
|
||||
@ -373,7 +384,7 @@ class OneDriveApi {
|
||||
}
|
||||
}
|
||||
|
||||
async execText(method, path, query, data) {
|
||||
async execText(method: string, path: string, query: any = null, data: any = null) {
|
||||
const response = await this.exec(method, path, query, data);
|
||||
const output = await response.text();
|
||||
return output;
|
||||
@ -385,7 +396,7 @@ class OneDriveApi {
|
||||
throw new Error(_('Cannot refresh token: authentication data is missing. Starting the synchronisation again may fix the problem.'));
|
||||
}
|
||||
|
||||
const body = {};
|
||||
const body: any = {};
|
||||
body['client_id'] = this.clientId();
|
||||
if (!this.isPublic()) body['client_secret'] = this.clientSecret();
|
||||
body['refresh_token'] = this.auth_.refresh_token;
|
||||
@ -410,5 +421,3 @@ class OneDriveApi {
|
||||
this.setAuth(auth);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { OneDriveApi };
|
@ -1,5 +1,9 @@
|
||||
# Joplin terminal app changelog
|
||||
|
||||
## [cli-v1.6.4](https://github.com/laurent22/joplin/releases/tag/cli-v1.6.4) - 2021-01-21T10:01:15Z
|
||||
|
||||
- Fixed: Fixed infinite sync issue with OneDrive (#4305)
|
||||
|
||||
## [cli-v1.6.3](https://github.com/laurent22/joplin/releases/tag/cli-v1.6.3) - 2021-01-11T11:52:11Z
|
||||
|
||||
- New: Add more log info when a revision cannot be deleted due to still-encrypted itel
|
||||
|
Loading…
Reference in New Issue
Block a user