diff --git a/manifest.json b/manifest.json index ce46732..b650fdc 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.8.5", + "version": "0.8.6", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 4218338..8b421b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.8.5", + "version": "0.8.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.8.5", + "version": "0.8.6", "license": "MIT", "dependencies": { "diff-match-patch": "^1.0.5", diff --git a/package.json b/package.json index 33f3d00..4b796ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.8.5", + "version": "0.8.6", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", diff --git a/src/LocalPouchDB.ts b/src/LocalPouchDB.ts index 17b1617..aa101f3 100644 --- a/src/LocalPouchDB.ts +++ b/src/LocalPouchDB.ts @@ -28,6 +28,8 @@ import { path2id } from "./utils"; import { Logger } from "./lib/src/logger"; import { checkRemoteVersion, connectRemoteCouchDB, getLastPostFailedBySize } from "./utils_couchdb"; +type ReplicationCallback = (e: PouchDB.Core.ExistingDocument[]) => Promise; + export class LocalPouchDB { auth: Credential; dbname: string; @@ -52,7 +54,7 @@ export class LocalPouchDB { remoteLockedAndDeviceNotAccepted = false; changeHandler: PouchDB.Core.Changes = null; - syncHandler: PouchDB.Replication.Sync = null; + syncHandler: PouchDB.Replication.Sync | PouchDB.Replication.Replication = null; leafArrivedCallbacks: { [key: string]: (() => void)[] } = {}; @@ -61,6 +63,8 @@ export class LocalPouchDB { docSent = 0; docSeq = ""; + isMobile = false; + cancelHandler | PouchDB.Replication.Sync | PouchDB.Replication.Replication>(handler: T): T { if (handler != null) { handler.removeAllListeners(); @@ -77,7 +81,7 @@ export class LocalPouchDB { this.localDatabase.removeAllListeners(); } - constructor(settings: RemoteDBSettings, dbname: string) { + constructor(settings: RemoteDBSettings, dbname: string, isMobile: boolean) { this.auth = { username: "", password: "", @@ -85,6 +89,7 @@ export class LocalPouchDB { this.dbname = dbname; this.settings = settings; this.cancelHandler = this.cancelHandler.bind(this); + this.isMobile = isMobile; // this.initializeDatabase(); } @@ -699,73 +704,18 @@ export class LocalPouchDB { replicateAllToServer(setting: RemoteDBSettings, showingNotice?: boolean) { return new Promise(async (res, rej) => { await this.waitForGCComplete(); - this.closeReplication(); - Logger("send all data to server", LOG_LEVEL.NOTICE); - let notice: WrappedNotice = null; - if (showingNotice) { - notice = NewNotice("Initializing", 0); - } - this.syncStatus = "STARTED"; - this.updateInfo(); - const uri = setting.couchDB_URI + (setting.couchDB_DBNAME == "" ? "" : "/" + setting.couchDB_DBNAME); - const auth: Credential = { - username: setting.couchDB_USER, - password: setting.couchDB_PASSWORD, - }; - const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI); - if (typeof dbret === "string") { - Logger(`could not connect to ${uri}:${dbret}`, LOG_LEVEL.NOTICE); - if (notice != null) notice.hide(); - return rej(`could not connect to ${uri}:${dbret}`); - } - - const syncOptionBase: PouchDB.Replication.SyncOptions = { - pull: { - checkpoint: "target", - }, - push: { - checkpoint: "source", - }, - batches_limit: setting.batches_limit, - batch_size: setting.batch_size, - }; - - const db = dbret.db; - const totalCount = (await this.localDatabase.info()).doc_count; - //replicate once - const replicate = this.localDatabase.replicate.to(db, { checkpoint: "source", ...syncOptionBase }); - replicate - .on("active", () => { - this.syncStatus = "CONNECTED"; - this.updateInfo(); - if (notice) { - notice.setMessage("CONNECTED"); - } - }) - .on("change", (e) => { - // no op. - this.docSent += e.docs.length; - this.updateInfo(); - notice.setMessage(`SENDING:${e.docs_written}/${totalCount}`); - Logger(`replicateAllToServer: sending..:${e.docs.length}`); - }) - .on("complete", (info) => { - this.syncStatus = "COMPLETED"; - this.updateInfo(); - Logger("replicateAllToServer: Completed", LOG_LEVEL.NOTICE); - this.cancelHandler(replicate); - if (notice != null) notice.hide(); - res(true); - }) - .on("error", (e) => { - this.syncStatus = "ERRORED"; - this.updateInfo(); - Logger("replicateAllToServer: Pulling Replication error", LOG_LEVEL.INFO); - Logger(e); - this.cancelHandler(replicate); - if (notice != null) notice.hide(); + this.openOneshotReplication( + setting, + showingNotice, + async (e) => {}, + false, + (e) => { + if (e === true) res(e); rej(e); - }); + }, + true, + false + ); }); } @@ -789,7 +739,8 @@ export class LocalPouchDB { Logger("Another replication running."); return false; } - const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI); + + const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI || this.isMobile); if (typeof dbret === "string") { Logger(`could not connect to ${uri}: ${dbret}`, LOG_LEVEL.NOTICE); return false; @@ -830,189 +781,256 @@ export class LocalPouchDB { return { db: dbret.db, info: dbret.info, syncOptionBase, syncOption }; } - async openReplication(setting: RemoteDBSettings, keepAlive: boolean, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument[]) => Promise): Promise { - return await runWithLock("replicate", false, () => { - return this._openReplication(setting, keepAlive, showResult, callback, false); - }); + openReplication(setting: RemoteDBSettings, keepAlive: boolean, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument[]) => Promise) { + if (keepAlive) { + this.openContinuousReplication(setting, showResult, callback, false); + } else { + this.openOneshotReplication(setting, showResult, callback, false, null, false, false); + } + } + replicationActivated(notice: WrappedNotice) { + this.syncStatus = "CONNECTED"; + this.updateInfo(); + Logger("Replication activated"); + if (notice != null) notice.setMessage(`Activated..`); + } + async replicationChangeDetected(e: PouchDB.Replication.SyncResult, notice: WrappedNotice, docSentOnStart: number, docArrivedOnStart: number, callback: ReplicationCallback) { + try { + if (e.direction == "pull") { + await callback(e.change.docs); + Logger(`replicated ${e.change.docs_read} doc(s)`); + this.docArrived += e.change.docs.length; + } else { + this.docSent += e.change.docs.length; + } + if (notice != null) { + notice.setMessage(`↑${this.docSent - docSentOnStart} ↓${this.docArrived - docArrivedOnStart}`); + } + this.updateInfo(); + } catch (ex) { + Logger("Replication callback error", LOG_LEVEL.NOTICE); + Logger(ex, LOG_LEVEL.NOTICE); + // + } + } + replicationCompleted(notice: WrappedNotice, showResult: boolean) { + this.syncStatus = "COMPLETED"; + this.updateInfo(); + Logger("Replication completed", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); + if (notice != null) notice.hide(); + this.syncHandler = this.cancelHandler(this.syncHandler); + } + replicationDeniend(notice: WrappedNotice, e: any) { + this.syncStatus = "ERRORED"; + this.updateInfo(); + this.syncHandler = this.cancelHandler(this.syncHandler); + if (notice != null) notice.hide(); + Logger("Replication denied", LOG_LEVEL.NOTICE); + Logger(e); + } + replicationErrored(notice: WrappedNotice, e: any) { + this.syncStatus = "ERRORED"; + this.syncHandler = this.cancelHandler(this.syncHandler); + this.updateInfo(); + } + replicationPaused(notice: WrappedNotice) { + this.syncStatus = "PAUSED"; + this.updateInfo(); + if (notice != null) notice.hide(); + Logger("replication paused", LOG_LEVEL.VERBOSE); } - originalSetting: RemoteDBSettings = null; - // last_seq: number = 200; - async _openReplication(setting: RemoteDBSettings, keepAlive: boolean, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument[]) => Promise, retrying: boolean): Promise { - const ret = await this.checkReplicationConnectivity(setting, keepAlive, retrying); - if (ret === false) return false; + async openOneshotReplication( + setting: RemoteDBSettings, + showResult: boolean, + callback: (e: PouchDB.Core.ExistingDocument[]) => Promise, + retrying: boolean, + callbackDone: (e: boolean | any) => void, + pushOnly: boolean, + pullOnly: boolean + ): Promise { + if (this.syncHandler != null) { + Logger("Replication is already in progress.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); + return; + } + Logger("Oneshot Sync begin..."); + let thisCallback = callbackDone; + const ret = await this.checkReplicationConnectivity(setting, true, retrying); let notice: WrappedNotice = null; + if (ret === false) { + Logger("Could not connect to server.", LOG_LEVEL.NOTICE); + return; + } if (showResult) { notice = NewNotice("Looking for the point last synchronized point.", 0); } - const { db, syncOptionBase, syncOption } = ret; - //replicate once + const { db, syncOptionBase } = ret; this.syncStatus = "STARTED"; this.updateInfo(); - - let resolved = false; const docArrivedOnStart = this.docArrived; const docSentOnStart = this.docSent; - - const _openReplicationSync = () => { - Logger("Sync Main Started"); - if (!retrying) { - this.originalSetting = setting; - } - this.syncHandler = this.cancelHandler(this.syncHandler); - this.syncHandler = this.localDatabase.sync(db, { - ...syncOption, - pull: { - checkpoint: "target", - }, - push: { - checkpoint: "source", - }, - }); + if (!retrying) { + // If initial replication, save setting to rollback + this.originalSetting = setting; + } + this.syncHandler = this.cancelHandler(this.syncHandler); + if (!pushOnly && !pullOnly) { + this.syncHandler = this.localDatabase.sync(db, { checkpoint: "target", ...syncOptionBase }); this.syncHandler - .on("active", () => { - this.syncStatus = "CONNECTED"; - this.updateInfo(); - Logger("Replication activated"); - if (notice != null) notice.setMessage(`Activated..`); - }) .on("change", async (e) => { - try { - if (e.direction == "pull") { - await callback(e.change.docs); - Logger(`replicated ${e.change.docs_read} doc(s)`); - this.docArrived += e.change.docs.length; - } else { - this.docSent += e.change.docs.length; - } - if (notice != null) { - notice.setMessage(`↑${this.docSent - docSentOnStart} ↓${this.docArrived - docArrivedOnStart}`); - } - this.updateInfo(); - } catch (ex) { - Logger("Replication callback error", LOG_LEVEL.NOTICE); - Logger(ex, LOG_LEVEL.NOTICE); - } - // re-connect to retry with original setting + await this.replicationChangeDetected(e, notice, docSentOnStart, docArrivedOnStart, callback); if (retrying) { if (this.docSent - docSentOnStart + (this.docArrived - docArrivedOnStart) > this.originalSetting.batch_size * 2) { - // restore sync values + // restore configration. Logger("Back into original settings once."); if (notice != null) notice.hide(); this.syncHandler = this.cancelHandler(this.syncHandler); - this._openReplication(this.originalSetting, keepAlive, showResult, callback, false); + this.openOneshotReplication(this.originalSetting, showResult, callback, false, callbackDone, pushOnly, pullOnly); } } }) .on("complete", (e) => { - this.syncStatus = "COMPLETED"; - this.updateInfo(); - Logger("Replication completed", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); - if (notice != null) notice.hide(); - if (!keepAlive) { - this.syncHandler = this.cancelHandler(this.syncHandler); - // if keep alive runnning, resolve here, + this.replicationCompleted(notice, showResult); + if (thisCallback != null) { + thisCallback(true); + } + }); + } else if (pullOnly) { + this.syncHandler = this.localDatabase.replicate.to(db, { checkpoint: "target", ...syncOptionBase }); + this.syncHandler + .on("change", async (e) => { + await this.replicationChangeDetected({ direction: "pull", change: e }, notice, docSentOnStart, docArrivedOnStart, callback); + if (retrying) { + if (this.docSent - docSentOnStart + (this.docArrived - docArrivedOnStart) > this.originalSetting.batch_size * 2) { + // restore configration. + Logger("Back into original settings once."); + if (notice != null) notice.hide(); + this.syncHandler = this.cancelHandler(this.syncHandler); + this.openOneshotReplication(this.originalSetting, showResult, callback, false, callbackDone, pushOnly, pullOnly); + } } }) - .on("denied", (e) => { - this.syncStatus = "ERRORED"; - this.updateInfo(); - this.syncHandler = this.cancelHandler(this.syncHandler); - if (notice != null) notice.hide(); - Logger("Replication denied", LOG_LEVEL.NOTICE); + .on("complete", (e) => { + this.replicationCompleted(notice, showResult); + if (thisCallback != null) { + thisCallback(true); + } + }); + } else if (pushOnly) { + this.syncHandler = this.localDatabase.replicate.to(db, { checkpoint: "target", ...syncOptionBase }); + this.syncHandler.on("complete", (e) => { + this.replicationCompleted(notice, showResult); + if (thisCallback != null) { + thisCallback(true); + } + }); + } + + this.syncHandler + .on("active", () => this.replicationActivated(notice)) + .on("denied", (e) => { + this.replicationDeniend(notice, e); + if (thisCallback != null) { + thisCallback(e); + } + }) + .on("error", (e) => { + this.replicationErrored(notice, e); + Logger("Replication stopped.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); + if (notice != null) notice.hide(); + if (getLastPostFailedBySize()) { + // Duplicate settings for smaller batch. + const xsetting: RemoteDBSettings = JSON.parse(JSON.stringify(setting)); + xsetting.batch_size = Math.ceil(xsetting.batch_size / 2) + 2; + xsetting.batches_limit = Math.ceil(xsetting.batches_limit / 2) + 2; + if (xsetting.batch_size <= 5 && xsetting.batches_limit <= 5) { + Logger("We can't replicate more lower value.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); + } else { + Logger(`Retry with lower batch size:${xsetting.batch_size}/${xsetting.batches_limit}`, showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); + thisCallback = null; + this.openOneshotReplication(xsetting, showResult, callback, true, callbackDone, pushOnly, pullOnly); + } + } else { + Logger("Replication error", LOG_LEVEL.NOTICE); Logger(e); - }) - .on("error", (e) => { - this.syncStatus = "ERRORED"; - this.syncHandler = this.cancelHandler(this.syncHandler); - this.updateInfo(); - if (notice != null) notice.hide(); - if (getLastPostFailedBySize()) { - if (keepAlive) { - Logger("Replication stopped.", LOG_LEVEL.NOTICE); - } else { - // Duplicate settings for smaller batch. - const xsetting: RemoteDBSettings = JSON.parse(JSON.stringify(setting)); - xsetting.batch_size = Math.ceil(xsetting.batch_size / 2); - xsetting.batches_limit = Math.ceil(xsetting.batches_limit / 2); - if (xsetting.batch_size <= 3 || xsetting.batches_limit <= 3) { - Logger("We can't replicate more lower value.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); - } else { - Logger(`Retry with lower batch size:${xsetting.batch_size}/${xsetting.batches_limit}`, showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); - this._openReplication(xsetting, keepAlive, showResult, callback, true); + } + if (thisCallback != null) { + thisCallback(e); + } + }) + .on("paused", (e) => this.replicationPaused(notice)); + } + + openContinuousReplication(setting: RemoteDBSettings, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument[]) => Promise, retrying: boolean) { + if (this.syncHandler != null) { + Logger("Replication is already in progress.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); + return; + } + Logger("Before LiveSync, start OneShot once..."); + this.openOneshotReplication( + setting, + showResult, + callback, + false, + async () => { + Logger("LiveSync begin..."); + const ret = await this.checkReplicationConnectivity(setting, true, true); + let notice: WrappedNotice = null; + if (ret === false) { + Logger("Could not connect to server.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); + return; + } + if (showResult) { + notice = NewNotice("Looking for the point last synchronized point.", 0); + } + const { db, syncOption } = ret; + this.syncStatus = "STARTED"; + this.updateInfo(); + const docArrivedOnStart = this.docArrived; + const docSentOnStart = this.docSent; + if (!retrying) { + //TODO if successfly saven, roll back org setting. + this.originalSetting = setting; + } + this.syncHandler = this.cancelHandler(this.syncHandler); + this.syncHandler = this.localDatabase.sync(db, { + ...syncOption, + pull: { + checkpoint: "target", + }, + push: { + checkpoint: "source", + }, + }); + this.syncHandler + .on("active", () => this.replicationActivated(notice)) + .on("change", async (e) => { + await this.replicationChangeDetected(e, notice, docSentOnStart, docArrivedOnStart, callback); + if (retrying) { + if (this.docSent - docSentOnStart + (this.docArrived - docArrivedOnStart) > this.originalSetting.batch_size * 2) { + // restore sync values + Logger("Back into original settings once."); + if (notice != null) notice.hide(); + this.syncHandler = this.cancelHandler(this.syncHandler); + this.openContinuousReplication(this.originalSetting, showResult, callback, false); } } - } else { - Logger("Replication error", LOG_LEVEL.NOTICE); - Logger(e); - } - }) - .on("paused", (e) => { - this.syncStatus = "PAUSED"; - this.updateInfo(); - if (notice != null) notice.hide(); - Logger("replication paused", LOG_LEVEL.VERBOSE); - if (keepAlive && !resolved) { - // if keep alive runnning, resolve here, - resolved = true; - } - // Logger(e); - }); - return this.syncHandler; - }; - if (!keepAlive) { - await _openReplicationSync(); - return true; - } - this.syncHandler = this.cancelHandler(this.syncHandler); - Logger("Pull before replicate."); - Logger(await this.localDatabase.info(), LOG_LEVEL.VERBOSE); - Logger(await db.info(), LOG_LEVEL.VERBOSE); - let replicate: PouchDB.Replication.Replication; - try { - replicate = this.localDatabase.replicate.from(db, { checkpoint: "target", ...syncOptionBase }); - replicate - .on("active", () => { - this.syncStatus = "CONNECTED"; - this.updateInfo(); - Logger("Replication pull activated."); - }) - .on("change", async (e) => { - // when in first run, replication will send us tombstone data - // and in normal cases, all leavs should sent before the entry that contains these item. - // so skip to completed all, we should treat all changes. - try { - await callback(e.docs); - this.docArrived += e.docs.length; - this.updateInfo(); - Logger(`pulled ${e.docs.length} doc(s)`); - if (notice != null) { - notice.setMessage(`Replication pulled:${e.docs_read}`); - } - } catch (ex) { - Logger("Replication callback error", LOG_LEVEL.NOTICE); - Logger(ex, LOG_LEVEL.NOTICE); - } - }); - this.syncStatus = "COMPLETED"; - this.updateInfo(); - this.cancelHandler(replicate); - this.syncHandler = this.cancelHandler(this.syncHandler); - Logger("Replication pull completed."); - _openReplicationSync(); - return true; - } catch (ex) { - this.syncStatus = "ERRORED"; - this.updateInfo(); - Logger("Pulling Replication error:", LOG_LEVEL.NOTICE); - Logger(ex, LOG_LEVEL.NOTICE); - this.cancelHandler(replicate); - this.syncHandler = this.cancelHandler(this.syncHandler); - if (notice != null) notice.hide(); - throw ex; - } + }) + .on("complete", (e) => this.replicationCompleted(notice, showResult)) + .on("denied", (e) => this.replicationDeniend(notice, e)) + .on("error", (e) => { + this.replicationErrored(notice, e); + Logger("Replication stopped.", LOG_LEVEL.NOTICE); + }) + .on("paused", (e) => this.replicationPaused(notice)); + }, + false, + true + ); } + originalSetting: RemoteDBSettings = null; + closeReplication() { this.syncStatus = "CLOSED"; this.updateInfo(); @@ -1039,7 +1057,7 @@ export class LocalPouchDB { username: setting.couchDB_USER, password: setting.couchDB_PASSWORD, }; - const con = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI); + const con = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI || this.isMobile); if (typeof con == "string") return; try { await con.db.destroy(); @@ -1057,7 +1075,7 @@ export class LocalPouchDB { username: setting.couchDB_USER, password: setting.couchDB_PASSWORD, }; - const con2 = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI); + const con2 = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI || this.isMobile); if (typeof con2 === "string") return; Logger("Remote Database Created or Connected", LOG_LEVEL.NOTICE); } @@ -1067,7 +1085,7 @@ export class LocalPouchDB { username: setting.couchDB_USER, password: setting.couchDB_PASSWORD, }; - const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI); + const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI || this.isMobile); if (typeof dbret === "string") { Logger(`could not connect to ${uri}:${dbret}`, LOG_LEVEL.NOTICE); return; @@ -1101,7 +1119,7 @@ export class LocalPouchDB { username: setting.couchDB_USER, password: setting.couchDB_PASSWORD, }; - const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI); + const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI || this.isMobile); if (typeof dbret === "string") { Logger(`could not connect to ${uri}:${dbret}`, LOG_LEVEL.NOTICE); return; diff --git a/src/ObsidianLiveSyncSettingTab.ts b/src/ObsidianLiveSyncSettingTab.ts index fcb7a05..5d71e3b 100644 --- a/src/ObsidianLiveSyncSettingTab.ts +++ b/src/ObsidianLiveSyncSettingTab.ts @@ -171,12 +171,18 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }) ), - new Setting(containerRemoteDatabaseEl).setName("Use the old connecting method").addToggle((toggle) => - toggle.setValue(this.plugin.settings.disableRequestURI).onChange(async (value) => { - this.plugin.settings.disableRequestURI = value; - await this.plugin.saveSettings(); + + new Setting(containerRemoteDatabaseEl) + .setDesc("This feature is locked in mobile") + .setName("Use the old connecting method") + .addToggle((toggle) => { + toggle.setValue(this.plugin.settings.disableRequestURI).onChange(async (value) => { + this.plugin.settings.disableRequestURI = value; + await this.plugin.saveSettings(); + }); + toggle.setDisabled(this.plugin.isMobile); + return toggle; }) - ) ); new Setting(containerRemoteDatabaseEl) diff --git a/src/main.ts b/src/main.ts index 08b19d2..6cce4bc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -64,6 +64,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin { statusBar2: HTMLElement; suspended: boolean; deviceAndVaultName: string; + isMobile = false; setInterval(handler: () => any, timeout?: number): number { const timer = window.setInterval(handler, timeout); @@ -93,6 +94,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin { const lsname = "obsidian-live-sync-ver" + this.app.vault.getName(); const last_version = localStorage.getItem(lsname); await this.loadSettings(); + //@ts-ignore + if (this.app.isMobile) { + this.isMobile = true; + this.settings.disableRequestURI = true; + } if (last_version && Number(last_version) < VER) { this.settings.liveSync = false; this.settings.syncOnSave = false; @@ -180,7 +186,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin { await this.realizeSettingSyncMode(); this.registerWatchEvents(); if (this.settings.syncOnStart) { - await this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult); + this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult); } } catch (ex) { Logger("Error while loading Self-hosted LiveSync", LOG_LEVEL.NOTICE); @@ -190,8 +196,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin { this.addCommand({ id: "livesync-replicate", name: "Replicate now", - callback: () => { - this.replicate(); + callback: async () => { + await this.replicate(); }, }); this.addCommand({ @@ -306,7 +312,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin { } const vaultName = this.app.vault.getName(); Logger("Open Database..."); - this.localDatabase = new LocalPouchDB(this.settings, vaultName); + //@ts-ignore + const isMobile = this.app.isMobile; + this.localDatabase = new LocalPouchDB(this.settings, vaultName, isMobile); this.localDatabase.updateInfo = () => { this.refreshStatusText(); }; @@ -389,10 +397,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin { await this.sweepPlugin(false); } if (this.settings.liveSync) { - await this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult); + this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult); } if (this.settings.syncOnStart) { - await this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult); + this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult); } if (this.settings.periodicReplication) { this.setPeriodicSync(); @@ -408,7 +416,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin { async watchWorkspaceOpenAsync(file: TFile) { await this.applyBatchChange(); - if (file == null) return; + if (file == null) { + return; + } if (this.settings.syncOnFileOpen && !this.suspended) { await this.replicate(); } @@ -449,7 +459,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin { async applyBatchChange() { if (!this.settings.batchSave || this.batchFileChange.length == 0) { - return []; + return; } return await runWithLock("batchSave", false, async () => { const batchItems = JSON.parse(JSON.stringify(this.batchFileChange)) as string[]; @@ -467,7 +477,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin { } }); this.refreshStatusText(); - return await Promise.all(promises); + await allSettledWithConcurrencyLimit(promises, 3); + return; }); } @@ -902,7 +913,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin { await this.sweepPlugin(false); } if (this.settings.liveSync) { - await this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult); + this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult); this.refreshStatusText(); } this.setPeriodicSync(); @@ -971,7 +982,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin { if (this.settings.autoSweepPlugins) { await this.sweepPlugin(false); } - await this.localDatabase.openReplication(this.settings, false, showMessage, this.parseReplicationResult); + this.localDatabase.openReplication(this.settings, false, showMessage, this.parseReplicationResult); } async initializeDatabase(showingNotice?: boolean) { diff --git a/src/pouchdb-browser.ts b/src/pouchdb-browser.ts index acd80ff..a1b3ccc 100644 --- a/src/pouchdb-browser.ts +++ b/src/pouchdb-browser.ts @@ -1,3 +1,4 @@ -import { PouchDB } from "../pouchdb-browser-webpack/dist/pouchdb-browser.js"; -console.dir(PouchDB) -export { PouchDB }; +import { PouchDB as PouchDB_ } from "../pouchdb-browser-webpack/dist/pouchdb-browser.js"; + +const Pouch: PouchDB.Static = PouchDB_; +export { Pouch as PouchDB }; diff --git a/src/utils_couchdb.ts b/src/utils_couchdb.ts index 7c37426..8b093b0 100644 --- a/src/utils_couchdb.ts +++ b/src/utils_couchdb.ts @@ -41,7 +41,6 @@ export const connectRemoteCouchDB = async (uri: string, auth: { username: string adapter: "http", auth, fetch: async function (url: string | Request, opts: RequestInit) { - let size_ok = true; let size = ""; const localURL = url.toString().substring(uri.length); const method = opts.method ?? "GET"; @@ -49,7 +48,6 @@ export const connectRemoteCouchDB = async (uri: string, auth: { username: string const opts_length = opts.body.toString().length; if (opts_length > 1024 * 1024 * 10) { // over 10MB - size_ok = false; if (uri.contains(".cloudantnosqldb.")) { last_post_successed = false; Logger("This request should fail on IBM Cloudant.", LOG_LEVEL.VERBOSE); @@ -93,7 +91,8 @@ export const connectRemoteCouchDB = async (uri: string, auth: { username: string }); } catch (ex) { Logger(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL.VERBOSE); - if (!size_ok && (method == "POST" || method == "PUT")) { + // limit only in bulk_docs. + if (url.toString().indexOf("_bulk_docs") !== -1) { last_post_successed = false; } Logger(ex); @@ -114,7 +113,8 @@ export const connectRemoteCouchDB = async (uri: string, auth: { username: string return responce; } catch (ex) { Logger(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL.VERBOSE); - if (!size_ok && (method == "POST" || method == "PUT")) { + // limit only in bulk_docs. + if (url.toString().indexOf("_bulk_docs") !== -1) { last_post_successed = false; } Logger(ex); diff --git a/tsconfig.json b/tsconfig.json index 5a6e010..b7f727e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,6 @@ "allowJs": true, "noImplicitAny": true, "moduleResolution": "node", - "types": ["svelte", "node"], // "importsNotUsedAsValues": "error", "importHelpers": true, "alwaysStrict": true,