- Freezing LiveSync on mobile devices.
This commit is contained in:
vorotamoroz
2022-05-06 18:14:45 +09:00
parent 6f76f90075
commit 5db3a374a9
9 changed files with 291 additions and 256 deletions

View File

@@ -1,7 +1,7 @@
{ {
"id": "obsidian-livesync", "id": "obsidian-livesync",
"name": "Self-hosted LiveSync", "name": "Self-hosted LiveSync",
"version": "0.8.5", "version": "0.8.6",
"minAppVersion": "0.9.12", "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.", "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", "author": "vorotamoroz",

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "0.8.5", "version": "0.8.6",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "0.8.5", "version": "0.8.6",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"diff-match-patch": "^1.0.5", "diff-match-patch": "^1.0.5",

View File

@@ -1,6 +1,6 @@
{ {
"name": "obsidian-livesync", "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.", "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", "main": "main.js",
"type": "module", "type": "module",

View File

@@ -28,6 +28,8 @@ import { path2id } from "./utils";
import { Logger } from "./lib/src/logger"; import { Logger } from "./lib/src/logger";
import { checkRemoteVersion, connectRemoteCouchDB, getLastPostFailedBySize } from "./utils_couchdb"; import { checkRemoteVersion, connectRemoteCouchDB, getLastPostFailedBySize } from "./utils_couchdb";
type ReplicationCallback = (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<void>;
export class LocalPouchDB { export class LocalPouchDB {
auth: Credential; auth: Credential;
dbname: string; dbname: string;
@@ -52,7 +54,7 @@ export class LocalPouchDB {
remoteLockedAndDeviceNotAccepted = false; remoteLockedAndDeviceNotAccepted = false;
changeHandler: PouchDB.Core.Changes<EntryDoc> = null; changeHandler: PouchDB.Core.Changes<EntryDoc> = null;
syncHandler: PouchDB.Replication.Sync<EntryDoc> = null; syncHandler: PouchDB.Replication.Sync<EntryDoc> | PouchDB.Replication.Replication<EntryDoc> = null;
leafArrivedCallbacks: { [key: string]: (() => void)[] } = {}; leafArrivedCallbacks: { [key: string]: (() => void)[] } = {};
@@ -61,6 +63,8 @@ export class LocalPouchDB {
docSent = 0; docSent = 0;
docSeq = ""; docSeq = "";
isMobile = false;
cancelHandler<T extends PouchDB.Core.Changes<EntryDoc> | PouchDB.Replication.Sync<EntryDoc> | PouchDB.Replication.Replication<EntryDoc>>(handler: T): T { cancelHandler<T extends PouchDB.Core.Changes<EntryDoc> | PouchDB.Replication.Sync<EntryDoc> | PouchDB.Replication.Replication<EntryDoc>>(handler: T): T {
if (handler != null) { if (handler != null) {
handler.removeAllListeners(); handler.removeAllListeners();
@@ -77,7 +81,7 @@ export class LocalPouchDB {
this.localDatabase.removeAllListeners(); this.localDatabase.removeAllListeners();
} }
constructor(settings: RemoteDBSettings, dbname: string) { constructor(settings: RemoteDBSettings, dbname: string, isMobile: boolean) {
this.auth = { this.auth = {
username: "", username: "",
password: "", password: "",
@@ -85,6 +89,7 @@ export class LocalPouchDB {
this.dbname = dbname; this.dbname = dbname;
this.settings = settings; this.settings = settings;
this.cancelHandler = this.cancelHandler.bind(this); this.cancelHandler = this.cancelHandler.bind(this);
this.isMobile = isMobile;
// this.initializeDatabase(); // this.initializeDatabase();
} }
@@ -699,73 +704,18 @@ export class LocalPouchDB {
replicateAllToServer(setting: RemoteDBSettings, showingNotice?: boolean) { replicateAllToServer(setting: RemoteDBSettings, showingNotice?: boolean) {
return new Promise(async (res, rej) => { return new Promise(async (res, rej) => {
await this.waitForGCComplete(); await this.waitForGCComplete();
this.closeReplication(); this.openOneshotReplication(
Logger("send all data to server", LOG_LEVEL.NOTICE); setting,
let notice: WrappedNotice = null; showingNotice,
if (showingNotice) { async (e) => {},
notice = NewNotice("Initializing", 0); false,
} (e) => {
this.syncStatus = "STARTED"; if (e === true) res(e);
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();
rej(e); rej(e);
}); },
true,
false
);
}); });
} }
@@ -789,7 +739,8 @@ export class LocalPouchDB {
Logger("Another replication running."); Logger("Another replication running.");
return false; return false;
} }
const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI);
const dbret = await connectRemoteCouchDB(uri, auth, setting.disableRequestURI || this.isMobile);
if (typeof dbret === "string") { if (typeof dbret === "string") {
Logger(`could not connect to ${uri}: ${dbret}`, LOG_LEVEL.NOTICE); Logger(`could not connect to ${uri}: ${dbret}`, LOG_LEVEL.NOTICE);
return false; return false;
@@ -830,53 +781,20 @@ export class LocalPouchDB {
return { db: dbret.db, info: dbret.info, syncOptionBase, syncOption }; return { db: dbret.db, info: dbret.info, syncOptionBase, syncOption };
} }
async openReplication(setting: RemoteDBSettings, keepAlive: boolean, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<void>): Promise<boolean> { openReplication(setting: RemoteDBSettings, keepAlive: boolean, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<void>) {
return await runWithLock("replicate", false, () => { if (keepAlive) {
return this._openReplication(setting, keepAlive, showResult, callback, false); this.openContinuousReplication(setting, showResult, callback, false);
}); } else {
this.openOneshotReplication(setting, showResult, callback, false, null, false, false);
} }
originalSetting: RemoteDBSettings = null;
// last_seq: number = 200;
async _openReplication(setting: RemoteDBSettings, keepAlive: boolean, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<void>, retrying: boolean): Promise<boolean> {
const ret = await this.checkReplicationConnectivity(setting, keepAlive, retrying);
if (ret === false) return false;
let notice: WrappedNotice = null;
if (showResult) {
notice = NewNotice("Looking for the point last synchronized point.", 0);
} }
const { db, syncOptionBase, syncOption } = ret; replicationActivated(notice: WrappedNotice) {
//replicate once
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<EntryDoc>(db, {
...syncOption,
pull: {
checkpoint: "target",
},
push: {
checkpoint: "source",
},
});
this.syncHandler
.on("active", () => {
this.syncStatus = "CONNECTED"; this.syncStatus = "CONNECTED";
this.updateInfo(); this.updateInfo();
Logger("Replication activated"); Logger("Replication activated");
if (notice != null) notice.setMessage(`Activated..`); if (notice != null) notice.setMessage(`Activated..`);
}) }
.on("change", async (e) => { async replicationChangeDetected(e: PouchDB.Replication.SyncResult<EntryDoc>, notice: WrappedNotice, docSentOnStart: number, docArrivedOnStart: number, callback: ReplicationCallback) {
try { try {
if (e.direction == "pull") { if (e.direction == "pull") {
await callback(e.change.docs); await callback(e.change.docs);
@@ -892,126 +810,226 @@ export class LocalPouchDB {
} catch (ex) { } catch (ex) {
Logger("Replication callback error", LOG_LEVEL.NOTICE); Logger("Replication callback error", LOG_LEVEL.NOTICE);
Logger(ex, LOG_LEVEL.NOTICE); Logger(ex, LOG_LEVEL.NOTICE);
} //
// re-connect to retry with original setting
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._openReplication(this.originalSetting, keepAlive, showResult, callback, false);
} }
} }
}) replicationCompleted(notice: WrappedNotice, showResult: boolean) {
.on("complete", (e) => {
this.syncStatus = "COMPLETED"; this.syncStatus = "COMPLETED";
this.updateInfo(); this.updateInfo();
Logger("Replication completed", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); Logger("Replication completed", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO);
if (notice != null) notice.hide(); if (notice != null) notice.hide();
if (!keepAlive) {
this.syncHandler = this.cancelHandler(this.syncHandler); this.syncHandler = this.cancelHandler(this.syncHandler);
// if keep alive runnning, resolve here,
} }
}) replicationDeniend(notice: WrappedNotice, e: any) {
.on("denied", (e) => {
this.syncStatus = "ERRORED"; this.syncStatus = "ERRORED";
this.updateInfo(); this.updateInfo();
this.syncHandler = this.cancelHandler(this.syncHandler); this.syncHandler = this.cancelHandler(this.syncHandler);
if (notice != null) notice.hide(); if (notice != null) notice.hide();
Logger("Replication denied", LOG_LEVEL.NOTICE); Logger("Replication denied", LOG_LEVEL.NOTICE);
Logger(e); Logger(e);
}) }
.on("error", (e) => { replicationErrored(notice: WrappedNotice, e: any) {
this.syncStatus = "ERRORED"; this.syncStatus = "ERRORED";
this.syncHandler = this.cancelHandler(this.syncHandler); this.syncHandler = this.cancelHandler(this.syncHandler);
this.updateInfo(); this.updateInfo();
}
replicationPaused(notice: WrappedNotice) {
this.syncStatus = "PAUSED";
this.updateInfo();
if (notice != null) notice.hide();
Logger("replication paused", LOG_LEVEL.VERBOSE);
}
async openOneshotReplication(
setting: RemoteDBSettings,
showResult: boolean,
callback: (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<void>,
retrying: boolean,
callbackDone: (e: boolean | any) => void,
pushOnly: boolean,
pullOnly: boolean
): Promise<boolean> {
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 } = ret;
this.syncStatus = "STARTED";
this.updateInfo();
const docArrivedOnStart = this.docArrived;
const docSentOnStart = this.docSent;
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("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 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("complete", (e) => {
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("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 (notice != null) notice.hide();
if (getLastPostFailedBySize()) { if (getLastPostFailedBySize()) {
if (keepAlive) {
Logger("Replication stopped.", LOG_LEVEL.NOTICE);
} else {
// Duplicate settings for smaller batch. // Duplicate settings for smaller batch.
const xsetting: RemoteDBSettings = JSON.parse(JSON.stringify(setting)); const xsetting: RemoteDBSettings = JSON.parse(JSON.stringify(setting));
xsetting.batch_size = Math.ceil(xsetting.batch_size / 2); xsetting.batch_size = Math.ceil(xsetting.batch_size / 2) + 2;
xsetting.batches_limit = Math.ceil(xsetting.batches_limit / 2); xsetting.batches_limit = Math.ceil(xsetting.batches_limit / 2) + 2;
if (xsetting.batch_size <= 3 || xsetting.batches_limit <= 3) { if (xsetting.batch_size <= 5 && xsetting.batches_limit <= 5) {
Logger("We can't replicate more lower value.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); Logger("We can't replicate more lower value.", showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO);
} else { } else {
Logger(`Retry with lower batch size:${xsetting.batch_size}/${xsetting.batches_limit}`, showResult ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO); 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); thisCallback = null;
} this.openOneshotReplication(xsetting, showResult, callback, true, callbackDone, pushOnly, pullOnly);
} }
} else { } else {
Logger("Replication error", LOG_LEVEL.NOTICE); Logger("Replication error", LOG_LEVEL.NOTICE);
Logger(e); Logger(e);
} }
}) if (thisCallback != null) {
.on("paused", (e) => { thisCallback(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); })
}); .on("paused", (e) => this.replicationPaused(notice));
return this.syncHandler; }
};
if (!keepAlive) { openContinuousReplication(setting: RemoteDBSettings, showResult: boolean, callback: (e: PouchDB.Core.ExistingDocument<EntryDoc>[]) => Promise<void>, retrying: boolean) {
await _openReplicationSync(); if (this.syncHandler != null) {
return true; 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.cancelHandler(this.syncHandler);
Logger("Pull before replicate."); this.syncHandler = this.localDatabase.sync<EntryDoc>(db, {
Logger(await this.localDatabase.info(), LOG_LEVEL.VERBOSE); ...syncOption,
Logger(await db.info(), LOG_LEVEL.VERBOSE); pull: {
let replicate: PouchDB.Replication.Replication<EntryDoc>; checkpoint: "target",
try { },
replicate = this.localDatabase.replicate.from(db, { checkpoint: "target", ...syncOptionBase }); push: {
replicate checkpoint: "source",
.on("active", () => { },
this.syncStatus = "CONNECTED"; });
this.updateInfo(); this.syncHandler
Logger("Replication pull activated."); .on("active", () => this.replicationActivated(notice))
})
.on("change", async (e) => { .on("change", async (e) => {
// when in first run, replication will send us tombstone data await this.replicationChangeDetected(e, notice, docSentOnStart, docArrivedOnStart, callback);
// and in normal cases, all leavs should sent before the entry that contains these item. if (retrying) {
// so skip to completed all, we should treat all changes. if (this.docSent - docSentOnStart + (this.docArrived - docArrivedOnStart) > this.originalSetting.batch_size * 2) {
try { // restore sync values
await callback(e.docs); Logger("Back into original settings once.");
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(); if (notice != null) notice.hide();
throw ex; this.syncHandler = this.cancelHandler(this.syncHandler);
this.openContinuousReplication(this.originalSetting, showResult, callback, false);
} }
} }
})
.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() { closeReplication() {
this.syncStatus = "CLOSED"; this.syncStatus = "CLOSED";
@@ -1039,7 +1057,7 @@ export class LocalPouchDB {
username: setting.couchDB_USER, username: setting.couchDB_USER,
password: setting.couchDB_PASSWORD, 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; if (typeof con == "string") return;
try { try {
await con.db.destroy(); await con.db.destroy();
@@ -1057,7 +1075,7 @@ export class LocalPouchDB {
username: setting.couchDB_USER, username: setting.couchDB_USER,
password: setting.couchDB_PASSWORD, 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; if (typeof con2 === "string") return;
Logger("Remote Database Created or Connected", LOG_LEVEL.NOTICE); Logger("Remote Database Created or Connected", LOG_LEVEL.NOTICE);
} }
@@ -1067,7 +1085,7 @@ export class LocalPouchDB {
username: setting.couchDB_USER, username: setting.couchDB_USER,
password: setting.couchDB_PASSWORD, 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") { if (typeof dbret === "string") {
Logger(`could not connect to ${uri}:${dbret}`, LOG_LEVEL.NOTICE); Logger(`could not connect to ${uri}:${dbret}`, LOG_LEVEL.NOTICE);
return; return;
@@ -1101,7 +1119,7 @@ export class LocalPouchDB {
username: setting.couchDB_USER, username: setting.couchDB_USER,
password: setting.couchDB_PASSWORD, 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") { if (typeof dbret === "string") {
Logger(`could not connect to ${uri}:${dbret}`, LOG_LEVEL.NOTICE); Logger(`could not connect to ${uri}:${dbret}`, LOG_LEVEL.NOTICE);
return; return;

View File

@@ -171,12 +171,18 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}) })
), ),
new Setting(containerRemoteDatabaseEl).setName("Use the old connecting method").addToggle((toggle) =>
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) => { toggle.setValue(this.plugin.settings.disableRequestURI).onChange(async (value) => {
this.plugin.settings.disableRequestURI = value; this.plugin.settings.disableRequestURI = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
});
toggle.setDisabled(this.plugin.isMobile);
return toggle;
}) })
)
); );
new Setting(containerRemoteDatabaseEl) new Setting(containerRemoteDatabaseEl)

View File

@@ -64,6 +64,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
statusBar2: HTMLElement; statusBar2: HTMLElement;
suspended: boolean; suspended: boolean;
deviceAndVaultName: string; deviceAndVaultName: string;
isMobile = false;
setInterval(handler: () => any, timeout?: number): number { setInterval(handler: () => any, timeout?: number): number {
const timer = window.setInterval(handler, timeout); 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 lsname = "obsidian-live-sync-ver" + this.app.vault.getName();
const last_version = localStorage.getItem(lsname); const last_version = localStorage.getItem(lsname);
await this.loadSettings(); await this.loadSettings();
//@ts-ignore
if (this.app.isMobile) {
this.isMobile = true;
this.settings.disableRequestURI = true;
}
if (last_version && Number(last_version) < VER) { if (last_version && Number(last_version) < VER) {
this.settings.liveSync = false; this.settings.liveSync = false;
this.settings.syncOnSave = false; this.settings.syncOnSave = false;
@@ -180,7 +186,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
await this.realizeSettingSyncMode(); await this.realizeSettingSyncMode();
this.registerWatchEvents(); this.registerWatchEvents();
if (this.settings.syncOnStart) { 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) { } catch (ex) {
Logger("Error while loading Self-hosted LiveSync", LOG_LEVEL.NOTICE); Logger("Error while loading Self-hosted LiveSync", LOG_LEVEL.NOTICE);
@@ -190,8 +196,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.addCommand({ this.addCommand({
id: "livesync-replicate", id: "livesync-replicate",
name: "Replicate now", name: "Replicate now",
callback: () => { callback: async () => {
this.replicate(); await this.replicate();
}, },
}); });
this.addCommand({ this.addCommand({
@@ -306,7 +312,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
} }
const vaultName = this.app.vault.getName(); const vaultName = this.app.vault.getName();
Logger("Open Database..."); 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.localDatabase.updateInfo = () => {
this.refreshStatusText(); this.refreshStatusText();
}; };
@@ -389,10 +397,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
await this.sweepPlugin(false); await this.sweepPlugin(false);
} }
if (this.settings.liveSync) { 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) { 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) { if (this.settings.periodicReplication) {
this.setPeriodicSync(); this.setPeriodicSync();
@@ -408,7 +416,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
async watchWorkspaceOpenAsync(file: TFile) { async watchWorkspaceOpenAsync(file: TFile) {
await this.applyBatchChange(); await this.applyBatchChange();
if (file == null) return; if (file == null) {
return;
}
if (this.settings.syncOnFileOpen && !this.suspended) { if (this.settings.syncOnFileOpen && !this.suspended) {
await this.replicate(); await this.replicate();
} }
@@ -449,7 +459,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
async applyBatchChange() { async applyBatchChange() {
if (!this.settings.batchSave || this.batchFileChange.length == 0) { if (!this.settings.batchSave || this.batchFileChange.length == 0) {
return []; return;
} }
return await runWithLock("batchSave", false, async () => { return await runWithLock("batchSave", false, async () => {
const batchItems = JSON.parse(JSON.stringify(this.batchFileChange)) as string[]; const batchItems = JSON.parse(JSON.stringify(this.batchFileChange)) as string[];
@@ -467,7 +477,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
} }
}); });
this.refreshStatusText(); 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); await this.sweepPlugin(false);
} }
if (this.settings.liveSync) { 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.refreshStatusText();
} }
this.setPeriodicSync(); this.setPeriodicSync();
@@ -971,7 +982,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
if (this.settings.autoSweepPlugins) { if (this.settings.autoSweepPlugins) {
await this.sweepPlugin(false); 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) { async initializeDatabase(showingNotice?: boolean) {

View File

@@ -1,3 +1,4 @@
import { PouchDB } from "../pouchdb-browser-webpack/dist/pouchdb-browser.js"; import { PouchDB as PouchDB_ } from "../pouchdb-browser-webpack/dist/pouchdb-browser.js";
console.dir(PouchDB)
export { PouchDB }; const Pouch: PouchDB.Static = PouchDB_;
export { Pouch as PouchDB };

View File

@@ -41,7 +41,6 @@ export const connectRemoteCouchDB = async (uri: string, auth: { username: string
adapter: "http", adapter: "http",
auth, auth,
fetch: async function (url: string | Request, opts: RequestInit) { fetch: async function (url: string | Request, opts: RequestInit) {
let size_ok = true;
let size = ""; let size = "";
const localURL = url.toString().substring(uri.length); const localURL = url.toString().substring(uri.length);
const method = opts.method ?? "GET"; 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; const opts_length = opts.body.toString().length;
if (opts_length > 1024 * 1024 * 10) { if (opts_length > 1024 * 1024 * 10) {
// over 10MB // over 10MB
size_ok = false;
if (uri.contains(".cloudantnosqldb.")) { if (uri.contains(".cloudantnosqldb.")) {
last_post_successed = false; last_post_successed = false;
Logger("This request should fail on IBM Cloudant.", LOG_LEVEL.VERBOSE); 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) { } catch (ex) {
Logger(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL.VERBOSE); 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; last_post_successed = false;
} }
Logger(ex); Logger(ex);
@@ -114,7 +113,8 @@ export const connectRemoteCouchDB = async (uri: string, auth: { username: string
return responce; return responce;
} catch (ex) { } catch (ex) {
Logger(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL.VERBOSE); 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; last_post_successed = false;
} }
Logger(ex); Logger(ex);

View File

@@ -6,7 +6,6 @@
"allowJs": true, "allowJs": true,
"noImplicitAny": true, "noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "node",
"types": ["svelte", "node"],
// "importsNotUsedAsValues": "error", // "importsNotUsedAsValues": "error",
"importHelpers": true, "importHelpers": true,
"alwaysStrict": true, "alwaysStrict": true,