mirror of https://github.com/laurent22/joplin.git
Desktop: Add Joplin Cloud sync target
parent
770af6a53b
commit
21ea3253db
|
@ -821,6 +821,9 @@ packages/lib/Logger.js.map
|
||||||
packages/lib/PoorManIntervals.d.ts
|
packages/lib/PoorManIntervals.d.ts
|
||||||
packages/lib/PoorManIntervals.js
|
packages/lib/PoorManIntervals.js
|
||||||
packages/lib/PoorManIntervals.js.map
|
packages/lib/PoorManIntervals.js.map
|
||||||
|
packages/lib/SyncTargetJoplinCloud.d.ts
|
||||||
|
packages/lib/SyncTargetJoplinCloud.js
|
||||||
|
packages/lib/SyncTargetJoplinCloud.js.map
|
||||||
packages/lib/SyncTargetJoplinServer.d.ts
|
packages/lib/SyncTargetJoplinServer.d.ts
|
||||||
packages/lib/SyncTargetJoplinServer.js
|
packages/lib/SyncTargetJoplinServer.js
|
||||||
packages/lib/SyncTargetJoplinServer.js.map
|
packages/lib/SyncTargetJoplinServer.js.map
|
||||||
|
|
|
@ -807,6 +807,9 @@ packages/lib/Logger.js.map
|
||||||
packages/lib/PoorManIntervals.d.ts
|
packages/lib/PoorManIntervals.d.ts
|
||||||
packages/lib/PoorManIntervals.js
|
packages/lib/PoorManIntervals.js
|
||||||
packages/lib/PoorManIntervals.js.map
|
packages/lib/PoorManIntervals.js.map
|
||||||
|
packages/lib/SyncTargetJoplinCloud.d.ts
|
||||||
|
packages/lib/SyncTargetJoplinCloud.js
|
||||||
|
packages/lib/SyncTargetJoplinCloud.js.map
|
||||||
packages/lib/SyncTargetJoplinServer.d.ts
|
packages/lib/SyncTargetJoplinServer.d.ts
|
||||||
packages/lib/SyncTargetJoplinServer.js
|
packages/lib/SyncTargetJoplinServer.js
|
||||||
packages/lib/SyncTargetJoplinServer.js.map
|
packages/lib/SyncTargetJoplinServer.js.map
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { loadKeychainServiceAndSettings } from '@joplin/lib/services/SettingUtil
|
||||||
import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile';
|
import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile';
|
||||||
import { setLocale, closestSupportedLocale, defaultLocale } from '@joplin/lib/locale';
|
import { setLocale, closestSupportedLocale, defaultLocale } from '@joplin/lib/locale';
|
||||||
import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer';
|
import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer';
|
||||||
|
import SyncTargetJoplinCloud from '@joplin/lib/SyncTargetJoplinCloud';
|
||||||
import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
|
import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
|
||||||
|
|
||||||
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar, Linking, Platform } = require('react-native');
|
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar, Linking, Platform } = require('react-native');
|
||||||
|
@ -90,6 +91,7 @@ SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||||
SyncTargetRegistry.addClass(SyncTargetFilesystem);
|
SyncTargetRegistry.addClass(SyncTargetFilesystem);
|
||||||
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
||||||
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
|
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
|
||||||
|
SyncTargetRegistry.addClass(SyncTargetJoplinCloud);
|
||||||
|
|
||||||
import FsDriverRN from './utils/fs-driver-rn';
|
import FsDriverRN from './utils/fs-driver-rn';
|
||||||
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker';
|
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Setting from './models/Setting';
|
import Setting, { Env } from './models/Setting';
|
||||||
import Logger, { TargetType, LoggerWrapper } from './Logger';
|
import Logger, { TargetType, LoggerWrapper } from './Logger';
|
||||||
import shim from './shim';
|
import shim from './shim';
|
||||||
import BaseService from './services/BaseService';
|
import BaseService from './services/BaseService';
|
||||||
|
@ -46,6 +46,7 @@ const { loadKeychainServiceAndSettings } = require('./services/SettingUtils');
|
||||||
import MigrationService from './services/MigrationService';
|
import MigrationService from './services/MigrationService';
|
||||||
import ShareService from './services/share/ShareService';
|
import ShareService from './services/share/ShareService';
|
||||||
import handleSyncStartupOperation from './services/synchronizer/utils/handleSyncStartupOperation';
|
import handleSyncStartupOperation from './services/synchronizer/utils/handleSyncStartupOperation';
|
||||||
|
import SyncTargetJoplinCloud from './SyncTargetJoplinCloud';
|
||||||
const { toSystemSlashes } = require('./path-utils');
|
const { toSystemSlashes } = require('./path-utils');
|
||||||
const { setAutoFreeze } = require('immer');
|
const { setAutoFreeze } = require('immer');
|
||||||
|
|
||||||
|
@ -691,6 +692,7 @@ export default class BaseApplication {
|
||||||
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||||
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
||||||
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
|
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
|
||||||
|
SyncTargetRegistry.addClass(SyncTargetJoplinCloud);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await shim.fsDriver().remove(tempDir);
|
await shim.fsDriver().remove(tempDir);
|
||||||
|
@ -763,6 +765,10 @@ export default class BaseApplication {
|
||||||
setLocale(Setting.value('locale'));
|
setLocale(Setting.value('locale'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Setting.value('env') === Env.Dev) {
|
||||||
|
Setting.setValue('sync.10.path', 'http://api-joplincloud.local:22300');
|
||||||
|
}
|
||||||
|
|
||||||
// For now always disable fuzzy search due to performance issues:
|
// For now always disable fuzzy search due to performance issues:
|
||||||
// https://discourse.joplinapp.org/t/1-1-4-keyboard-locks-up-while-typing/11231/11
|
// https://discourse.joplinapp.org/t/1-1-4-keyboard-locks-up-while-typing/11231/11
|
||||||
// https://discourse.joplinapp.org/t/serious-lagging-when-there-are-tens-of-thousands-of-notes/11215/23
|
// https://discourse.joplinapp.org/t/serious-lagging-when-there-are-tens-of-thousands-of-notes/11215/23
|
||||||
|
|
|
@ -58,12 +58,17 @@ export default class JoplinServerApi {
|
||||||
private async session() {
|
private async session() {
|
||||||
if (this.session_) return this.session_;
|
if (this.session_) return this.session_;
|
||||||
|
|
||||||
this.session_ = await this.exec('POST', 'api/sessions', null, {
|
try {
|
||||||
email: this.options_.username(),
|
this.session_ = await this.exec('POST', 'api/sessions', null, {
|
||||||
password: this.options_.password(),
|
email: this.options_.username(),
|
||||||
});
|
password: this.options_.password(),
|
||||||
|
});
|
||||||
|
|
||||||
return this.session_;
|
return this.session_;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Could not acquire session:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sessionId() {
|
private async sessionId() {
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import Setting from './models/Setting';
|
||||||
|
import Synchronizer from './Synchronizer';
|
||||||
|
import { _ } from './locale.js';
|
||||||
|
import BaseSyncTarget from './BaseSyncTarget';
|
||||||
|
import { FileApi } from './file-api';
|
||||||
|
import SyncTargetJoplinServer, { initFileApi } from './SyncTargetJoplinServer';
|
||||||
|
|
||||||
|
interface FileApiOptions {
|
||||||
|
path(): string;
|
||||||
|
username(): string;
|
||||||
|
password(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SyncTargetJoplinCloud extends BaseSyncTarget {
|
||||||
|
|
||||||
|
public static id() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static supportsConfigCheck() {
|
||||||
|
return SyncTargetJoplinServer.supportsConfigCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static targetName() {
|
||||||
|
return 'joplinCloud';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static label() {
|
||||||
|
return _('Joplin Cloud');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async isAuthenticated() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fileApi(): Promise<FileApi> {
|
||||||
|
return super.fileApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async checkConfig(options: FileApiOptions) {
|
||||||
|
return SyncTargetJoplinServer.checkConfig({
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async initFileApi() {
|
||||||
|
return initFileApi(this.logger(), {
|
||||||
|
path: () => Setting.value('sync.10.path'),
|
||||||
|
username: () => Setting.value('sync.10.username'),
|
||||||
|
password: () => Setting.value('sync.10.password'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async initSynchronizer() {
|
||||||
|
return new Synchronizer(this.db(), await this.fileApi(), Setting.value('appType'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import { _ } from './locale.js';
|
||||||
import JoplinServerApi from './JoplinServerApi';
|
import JoplinServerApi from './JoplinServerApi';
|
||||||
import BaseSyncTarget from './BaseSyncTarget';
|
import BaseSyncTarget from './BaseSyncTarget';
|
||||||
import { FileApi } from './file-api';
|
import { FileApi } from './file-api';
|
||||||
|
import Logger from './Logger';
|
||||||
|
|
||||||
interface FileApiOptions {
|
interface FileApiOptions {
|
||||||
path(): string;
|
path(): string;
|
||||||
|
@ -12,6 +13,28 @@ interface FileApiOptions {
|
||||||
password(): string;
|
password(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function newFileApi(id: number, options: FileApiOptions) {
|
||||||
|
const apiOptions = {
|
||||||
|
baseUrl: () => options.path(),
|
||||||
|
username: () => options.username(),
|
||||||
|
password: () => options.password(),
|
||||||
|
env: Setting.value('env'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const api = new JoplinServerApi(apiOptions);
|
||||||
|
const driver = new FileApiDriverJoplinServer(api);
|
||||||
|
const fileApi = new FileApi('', driver);
|
||||||
|
fileApi.setSyncTargetId(id);
|
||||||
|
await fileApi.initialize();
|
||||||
|
return fileApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initFileApi(logger: Logger, options: FileApiOptions) {
|
||||||
|
const fileApi = await newFileApi(SyncTargetJoplinServer.id(), options);
|
||||||
|
fileApi.setLogger(logger);
|
||||||
|
return fileApi;
|
||||||
|
}
|
||||||
|
|
||||||
export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||||
|
|
||||||
public static id() {
|
public static id() {
|
||||||
|
@ -38,22 +61,6 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||||
return super.fileApi();
|
return super.fileApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async newFileApi_(options: FileApiOptions) {
|
|
||||||
const apiOptions = {
|
|
||||||
baseUrl: () => options.path(),
|
|
||||||
username: () => options.username(),
|
|
||||||
password: () => options.password(),
|
|
||||||
env: Setting.value('env'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const api = new JoplinServerApi(apiOptions);
|
|
||||||
const driver = new FileApiDriverJoplinServer(api);
|
|
||||||
const fileApi = new FileApi('', driver);
|
|
||||||
fileApi.setSyncTargetId(this.id());
|
|
||||||
await fileApi.initialize();
|
|
||||||
return fileApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async checkConfig(options: FileApiOptions) {
|
public static async checkConfig(options: FileApiOptions) {
|
||||||
const output = {
|
const output = {
|
||||||
ok: false,
|
ok: false,
|
||||||
|
@ -61,7 +68,7 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileApi = await SyncTargetJoplinServer.newFileApi_(options);
|
const fileApi = await newFileApi(SyncTargetJoplinServer.id(), options);
|
||||||
fileApi.requestRepeatCount_ = 0;
|
fileApi.requestRepeatCount_ = 0;
|
||||||
|
|
||||||
await fileApi.put('testing.txt', 'testing');
|
await fileApi.put('testing.txt', 'testing');
|
||||||
|
@ -78,15 +85,11 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async initFileApi() {
|
protected async initFileApi() {
|
||||||
const fileApi = await SyncTargetJoplinServer.newFileApi_({
|
return initFileApi(this.logger(), {
|
||||||
path: () => Setting.value('sync.9.path'),
|
path: () => Setting.value('sync.9.path'),
|
||||||
username: () => Setting.value('sync.9.username'),
|
username: () => Setting.value('sync.9.username'),
|
||||||
password: () => Setting.value('sync.9.password'),
|
password: () => Setting.value('sync.9.password'),
|
||||||
});
|
});
|
||||||
|
|
||||||
fileApi.setLogger(this.logger());
|
|
||||||
|
|
||||||
return fileApi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async initSynchronizer() {
|
protected async initSynchronizer() {
|
||||||
|
|
|
@ -24,7 +24,7 @@ class SyncTargetRegistry {
|
||||||
if (!this.reg_.hasOwnProperty(n)) continue;
|
if (!this.reg_.hasOwnProperty(n)) continue;
|
||||||
if (this.reg_[n].name === name) return this.reg_[n].id;
|
if (this.reg_[n].name === name) return this.reg_[n].id;
|
||||||
}
|
}
|
||||||
throw new Error(`Name not found: ${name}`);
|
throw new Error(`Name not found: ${name}. Was the sync target registered?`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static idToMetadata(id) {
|
static idToMetadata(id) {
|
||||||
|
|
|
@ -499,6 +499,39 @@ class Setting extends BaseModel {
|
||||||
secure: true,
|
secure: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Although sync.10.path is essentially a constant, we still define
|
||||||
|
// it here so that both Joplin Server and Joplin Cloud can be
|
||||||
|
// handled in the same consistent way. Also having it a setting
|
||||||
|
// means it can be set to something else for development.
|
||||||
|
'sync.10.path': {
|
||||||
|
value: 'https://api.joplincloud.com',
|
||||||
|
type: SettingItemType.String,
|
||||||
|
public: false,
|
||||||
|
storage: SettingStorage.Database,
|
||||||
|
},
|
||||||
|
'sync.10.username': {
|
||||||
|
value: '',
|
||||||
|
type: SettingItemType.String,
|
||||||
|
section: 'sync',
|
||||||
|
show: (settings: any) => {
|
||||||
|
return settings['sync.target'] == SyncTargetRegistry.nameToId('joplinCloud');
|
||||||
|
},
|
||||||
|
public: true,
|
||||||
|
label: () => _('Joplin Cloud email'),
|
||||||
|
storage: SettingStorage.File,
|
||||||
|
},
|
||||||
|
'sync.10.password': {
|
||||||
|
value: '',
|
||||||
|
type: SettingItemType.String,
|
||||||
|
section: 'sync',
|
||||||
|
show: (settings: any) => {
|
||||||
|
return settings['sync.target'] == SyncTargetRegistry.nameToId('joplinCloud');
|
||||||
|
},
|
||||||
|
public: true,
|
||||||
|
label: () => _('Joplin Cloud password'),
|
||||||
|
secure: true,
|
||||||
|
},
|
||||||
|
|
||||||
'sync.5.syncTargets': { value: {}, type: SettingItemType.Object, public: false },
|
'sync.5.syncTargets': { value: {}, type: SettingItemType.Object, public: false },
|
||||||
|
|
||||||
'sync.resourceDownloadMode': {
|
'sync.resourceDownloadMode': {
|
||||||
|
@ -525,6 +558,7 @@ class Setting extends BaseModel {
|
||||||
'sync.4.auth': { value: '', type: SettingItemType.String, public: false },
|
'sync.4.auth': { value: '', type: SettingItemType.String, public: false },
|
||||||
'sync.7.auth': { value: '', type: SettingItemType.String, public: false },
|
'sync.7.auth': { value: '', type: SettingItemType.String, public: false },
|
||||||
'sync.9.auth': { value: '', type: SettingItemType.String, public: false },
|
'sync.9.auth': { value: '', type: SettingItemType.String, public: false },
|
||||||
|
'sync.10.auth': { value: '', type: SettingItemType.String, public: false },
|
||||||
'sync.1.context': { value: '', type: SettingItemType.String, public: false },
|
'sync.1.context': { value: '', type: SettingItemType.String, public: false },
|
||||||
'sync.2.context': { value: '', type: SettingItemType.String, public: false },
|
'sync.2.context': { value: '', type: SettingItemType.String, public: false },
|
||||||
'sync.3.context': { value: '', type: SettingItemType.String, public: false },
|
'sync.3.context': { value: '', type: SettingItemType.String, public: false },
|
||||||
|
@ -534,6 +568,7 @@ class Setting extends BaseModel {
|
||||||
'sync.7.context': { value: '', type: SettingItemType.String, public: false },
|
'sync.7.context': { value: '', type: SettingItemType.String, public: false },
|
||||||
'sync.8.context': { value: '', type: SettingItemType.String, public: false },
|
'sync.8.context': { value: '', type: SettingItemType.String, public: false },
|
||||||
'sync.9.context': { value: '', type: SettingItemType.String, public: false },
|
'sync.9.context': { value: '', type: SettingItemType.String, public: false },
|
||||||
|
'sync.10.context': { value: '', type: SettingItemType.String, public: false },
|
||||||
|
|
||||||
'sync.maxConcurrentConnections': { value: 5, type: SettingItemType.Int, storage: SettingStorage.File, public: true, advanced: true, section: 'sync', label: () => _('Max concurrent connections'), minimum: 1, maximum: 20, step: 1 },
|
'sync.maxConcurrentConnections': { value: 5, type: SettingItemType.Int, storage: SettingStorage.File, public: true, advanced: true, section: 'sync', label: () => _('Max concurrent connections'), minimum: 1, maximum: 20, step: 1 },
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default class ShareService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public get enabled(): boolean {
|
public get enabled(): boolean {
|
||||||
return Setting.value('sync.target') === 9; // Joplin Server target
|
return [9, 10].includes(Setting.value('sync.target')); // Joplin Server, Joplin Cloud targets
|
||||||
}
|
}
|
||||||
|
|
||||||
private get store(): Store<any> {
|
private get store(): Store<any> {
|
||||||
|
@ -36,10 +36,12 @@ export default class ShareService {
|
||||||
private api(): JoplinServerApi {
|
private api(): JoplinServerApi {
|
||||||
if (this.api_) return this.api_;
|
if (this.api_) return this.api_;
|
||||||
|
|
||||||
|
const syncTargetId = Setting.value('sync.target');
|
||||||
|
|
||||||
this.api_ = new JoplinServerApi({
|
this.api_ = new JoplinServerApi({
|
||||||
baseUrl: () => Setting.value('sync.9.path'),
|
baseUrl: () => Setting.value(`sync.${syncTargetId}.path`),
|
||||||
username: () => Setting.value('sync.9.username'),
|
username: () => Setting.value(`sync.${syncTargetId}.username`),
|
||||||
password: () => Setting.value('sync.9.password'),
|
password: () => Setting.value(`sync.${syncTargetId}.password`),
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.api_;
|
return this.api_;
|
||||||
|
|
|
@ -51,6 +51,7 @@ const DropboxApi = require('../DropboxApi');
|
||||||
import JoplinServerApi from '../JoplinServerApi';
|
import JoplinServerApi from '../JoplinServerApi';
|
||||||
import { FolderEntity } from '../services/database/types';
|
import { FolderEntity } from '../services/database/types';
|
||||||
import { credentialFile } from '../utils/credentialFiles';
|
import { credentialFile } from '../utils/credentialFiles';
|
||||||
|
import SyncTargetJoplinCloud from '../SyncTargetJoplinCloud';
|
||||||
const { loadKeychainServiceAndSettings } = require('../services/SettingUtils');
|
const { loadKeychainServiceAndSettings } = require('../services/SettingUtils');
|
||||||
const md5 = require('md5');
|
const md5 = require('md5');
|
||||||
const S3 = require('aws-sdk/clients/s3');
|
const S3 = require('aws-sdk/clients/s3');
|
||||||
|
@ -112,6 +113,7 @@ SyncTargetRegistry.addClass(SyncTargetNextcloud);
|
||||||
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||||
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
||||||
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
|
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
|
||||||
|
SyncTargetRegistry.addClass(SyncTargetJoplinCloud);
|
||||||
|
|
||||||
let syncTargetName_ = '';
|
let syncTargetName_ = '';
|
||||||
let syncTargetId_: number = null;
|
let syncTargetId_: number = null;
|
||||||
|
|
Loading…
Reference in New Issue