2023-07-27 17:05:56 +02:00
|
|
|
import Logger from '@joplin/utils/Logger';
|
2021-01-29 20:45:11 +02:00
|
|
|
import Synchronizer from './Synchronizer';
|
2021-08-23 19:47:07 +02:00
|
|
|
import EncryptionService from './services/e2ee/EncryptionService';
|
2021-01-29 20:45:11 +02:00
|
|
|
import shim from './shim';
|
|
|
|
import ResourceService from './services/ResourceService';
|
2021-05-13 18:57:37 +02:00
|
|
|
import ShareService from './services/share/ShareService';
|
2021-01-29 20:45:11 +02:00
|
|
|
|
|
|
|
export default class BaseSyncTarget {
|
|
|
|
|
2023-06-30 11:30:29 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
2021-01-29 20:45:11 +02:00
|
|
|
public static dispatch: Function = () => {};
|
|
|
|
|
|
|
|
private synchronizer_: Synchronizer = null;
|
|
|
|
private initState_: any = null;
|
|
|
|
private logger_: Logger = null;
|
|
|
|
private options_: any;
|
|
|
|
private db_: any;
|
|
|
|
protected fileApi_: any;
|
|
|
|
|
|
|
|
public constructor(db: any, options: any = null) {
|
|
|
|
this.db_ = db;
|
|
|
|
this.options_ = options;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static supportsConfigCheck() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-14 19:12:49 +02:00
|
|
|
// Returns true if the sync target expects a non-empty sync.{id}.password
|
|
|
|
// setting.
|
|
|
|
public static requiresPassword() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-16 16:20:14 +02:00
|
|
|
public static description(): string {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
public static supportsSelfHosted(): boolean {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-03 20:19:24 +02:00
|
|
|
public static supportsRecursiveLinkedNotes(): boolean {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-29 20:45:11 +02:00
|
|
|
public option(name: string, defaultValue: any = null) {
|
|
|
|
return this.options_ && name in this.options_ ? this.options_[name] : defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected logger() {
|
|
|
|
return this.logger_;
|
|
|
|
}
|
|
|
|
|
|
|
|
public setLogger(v: Logger) {
|
|
|
|
this.logger_ = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected db() {
|
|
|
|
return this.db_;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If [] is returned it means all platforms are supported
|
|
|
|
public static unsupportedPlatforms(): any[] {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
public async isAuthenticated() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public authRouteName(): string {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static id() {
|
|
|
|
throw new Error('id() not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: it cannot be called just "name()" because that's a reserved keyword and
|
|
|
|
// it would throw an obscure error in React Native.
|
|
|
|
public static targetName() {
|
|
|
|
throw new Error('targetName() not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
public static label() {
|
|
|
|
throw new Error('label() not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected async initSynchronizer(): Promise<Synchronizer> {
|
|
|
|
throw new Error('initSynchronizer() not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected async initFileApi(): Promise<any> {
|
|
|
|
throw new Error('initFileApi() not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
public async fileApi() {
|
|
|
|
if (this.fileApi_) return this.fileApi_;
|
|
|
|
this.fileApi_ = await this.initFileApi();
|
|
|
|
return this.fileApi_;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Usually each sync target should create and setup its own file API via initFileApi()
|
|
|
|
// but for testing purposes it might be convenient to provide it here so that multiple
|
|
|
|
// clients can share and sync to the same file api (see test-utils.js)
|
|
|
|
public setFileApi(v: any) {
|
|
|
|
this.fileApi_ = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async synchronizer(): Promise<Synchronizer> {
|
|
|
|
if (this.synchronizer_) return this.synchronizer_;
|
|
|
|
|
2022-07-23 09:31:32 +02:00
|
|
|
if (this.initState_ === 'started') {
|
2021-01-29 20:45:11 +02:00
|
|
|
// Synchronizer is already being initialized, so wait here till it's done.
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const iid = shim.setInterval(() => {
|
2022-07-23 09:31:32 +02:00
|
|
|
if (this.initState_ === 'ready') {
|
2021-01-29 20:45:11 +02:00
|
|
|
shim.clearInterval(iid);
|
|
|
|
resolve(this.synchronizer_);
|
|
|
|
}
|
2022-07-23 09:31:32 +02:00
|
|
|
if (this.initState_ === 'error') {
|
2021-01-29 20:45:11 +02:00
|
|
|
shim.clearInterval(iid);
|
|
|
|
reject(new Error('Could not initialise synchroniser'));
|
|
|
|
}
|
|
|
|
}, 1000);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.initState_ = 'started';
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.synchronizer_ = await this.initSynchronizer();
|
|
|
|
this.synchronizer_.setLogger(this.logger());
|
|
|
|
this.synchronizer_.setEncryptionService(EncryptionService.instance());
|
|
|
|
this.synchronizer_.setResourceService(ResourceService.instance());
|
2021-05-13 18:57:37 +02:00
|
|
|
this.synchronizer_.setShareService(ShareService.instance());
|
2021-01-29 20:45:11 +02:00
|
|
|
this.synchronizer_.dispatch = BaseSyncTarget.dispatch;
|
|
|
|
this.initState_ = 'ready';
|
|
|
|
return this.synchronizer_;
|
|
|
|
} catch (error) {
|
|
|
|
this.initState_ = 'error';
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public async syncStarted() {
|
|
|
|
if (!this.synchronizer_) return false;
|
|
|
|
if (!(await this.isAuthenticated())) return false;
|
|
|
|
const sync = await this.synchronizer();
|
2022-07-23 09:31:32 +02:00
|
|
|
return sync.state() !== 'idle';
|
2021-01-29 20:45:11 +02:00
|
|
|
}
|
|
|
|
}
|