mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-07 16:21:51 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e75917b8d | ||
|
|
3322d13b55 | ||
|
|
851c9f8a71 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.18.2",
|
"version": "0.18.4",
|
||||||
"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
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.18.2",
|
"version": "0.18.4",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.18.2",
|
"version": "0.18.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.18.2",
|
"version": "0.18.4",
|
||||||
"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",
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
await this.kvDB.set("diff-caches-internal", caches);
|
await this.kvDB.set("diff-caches-internal", caches);
|
||||||
|
|
||||||
// When files has been retrieved from the database. they must be reloaded.
|
// When files has been retrieved from the database. they must be reloaded.
|
||||||
if (direction == "pull" || direction == "pullForce" && filesChanged != 0) {
|
if ((direction == "pull" || direction == "pullForce") && filesChanged != 0) {
|
||||||
const configDir = normalizePath(this.app.vault.configDir);
|
const configDir = normalizePath(this.app.vault.configDir);
|
||||||
// Show notification to restart obsidian when something has been changed in configDir.
|
// Show notification to restart obsidian when something has been changed in configDir.
|
||||||
if (configDir in updatedFolders) {
|
if (configDir in updatedFolders) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
|||||||
import { askSelectString, askYesNo, askString } from "./utils";
|
import { askSelectString, askYesNo, askString } from "./utils";
|
||||||
import { decrypt, encrypt } from "./lib/src/e2ee_v2";
|
import { decrypt, encrypt } from "./lib/src/e2ee_v2";
|
||||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||||
|
import { delay } from "./lib/src/utils";
|
||||||
|
|
||||||
export class SetupLiveSync extends LiveSyncCommands {
|
export class SetupLiveSync extends LiveSyncCommands {
|
||||||
onunload() { }
|
onunload() { }
|
||||||
@@ -97,7 +98,8 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
const setupAsNew = "Set it up as secondary or subsequent device";
|
const setupAsNew = "Set it up as secondary or subsequent device";
|
||||||
const setupAgain = "Reconfigure and reconstitute the data";
|
const setupAgain = "Reconfigure and reconstitute the data";
|
||||||
const setupManually = "Leave everything to me";
|
const setupManually = "Leave everything to me";
|
||||||
|
newSettingW.syncInternalFiles = false;
|
||||||
|
newSettingW.usePluginSync = false;
|
||||||
const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupJustImport, setupManually]);
|
const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupJustImport, setupManually]);
|
||||||
if (setupType == setupJustImport) {
|
if (setupType == setupJustImport) {
|
||||||
this.plugin.settings = newSettingW;
|
this.plugin.settings = newSettingW;
|
||||||
@@ -106,11 +108,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
} else if (setupType == setupAsNew) {
|
} else if (setupType == setupAsNew) {
|
||||||
this.plugin.settings = newSettingW;
|
this.plugin.settings = newSettingW;
|
||||||
this.plugin.usedPassphrase = "";
|
this.plugin.usedPassphrase = "";
|
||||||
await this.plugin.saveSettings();
|
await this.fetchLocal();
|
||||||
await this.plugin.resetLocalDatabase();
|
|
||||||
await this.plugin.localDatabase.initializeDatabase();
|
|
||||||
await this.plugin.markRemoteResolved();
|
|
||||||
await this.plugin.replicate(true);
|
|
||||||
} else if (setupType == setupAgain) {
|
} else if (setupType == setupAgain) {
|
||||||
const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed.";
|
const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed.";
|
||||||
if (await askSelectString(this.app, "Do you really want to do this?", ["Cancel", confirm]) != confirm) {
|
if (await askSelectString(this.app, "Do you really want to do this?", ["Cancel", confirm]) != confirm) {
|
||||||
@@ -118,15 +116,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
}
|
}
|
||||||
this.plugin.settings = newSettingW;
|
this.plugin.settings = newSettingW;
|
||||||
this.plugin.usedPassphrase = "";
|
this.plugin.usedPassphrase = "";
|
||||||
await this.plugin.saveSettings();
|
await this.rebuildEverything();
|
||||||
await this.plugin.resetLocalDatabase();
|
|
||||||
await this.plugin.localDatabase.initializeDatabase();
|
|
||||||
await this.plugin.initializeDatabase(true);
|
|
||||||
await this.plugin.tryResetRemoteDatabase();
|
|
||||||
await this.plugin.markRemoteLocked();
|
|
||||||
await this.plugin.markRemoteResolved();
|
|
||||||
await this.plugin.replicate(true);
|
|
||||||
|
|
||||||
} else if (setupType == setupManually) {
|
} else if (setupType == setupManually) {
|
||||||
const keepLocalDB = await askYesNo(this.app, "Keep local DB?");
|
const keepLocalDB = await askYesNo(this.app, "Keep local DB?");
|
||||||
const keepRemoteDB = await askYesNo(this.app, "Keep remote DB?");
|
const keepRemoteDB = await askYesNo(this.app, "Keep remote DB?");
|
||||||
@@ -134,6 +124,8 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
// nothing to do. so peaceful.
|
// nothing to do. so peaceful.
|
||||||
this.plugin.settings = newSettingW;
|
this.plugin.settings = newSettingW;
|
||||||
this.plugin.usedPassphrase = "";
|
this.plugin.usedPassphrase = "";
|
||||||
|
this.suspendAllSync();
|
||||||
|
this.suspendExtraSync();
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
const replicate = await askYesNo(this.app, "Unlock and replicate?");
|
const replicate = await askYesNo(this.app, "Unlock and replicate?");
|
||||||
if (replicate == "yes") {
|
if (replicate == "yes") {
|
||||||
@@ -189,4 +181,52 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
Logger("Couldn't parse or decrypt configuration uri.", LOG_LEVEL.NOTICE);
|
Logger("Couldn't parse or decrypt configuration uri.", LOG_LEVEL.NOTICE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspendExtraSync() {
|
||||||
|
Logger("Hidden files and plugin synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.", LOG_LEVEL.NOTICE)
|
||||||
|
this.plugin.settings.syncInternalFiles = false;
|
||||||
|
this.plugin.settings.usePluginSync = false;
|
||||||
|
this.plugin.settings.autoSweepPlugins = false;
|
||||||
|
}
|
||||||
|
suspendAllSync() {
|
||||||
|
this.plugin.settings.liveSync = false;
|
||||||
|
this.plugin.settings.periodicReplication = false;
|
||||||
|
this.plugin.settings.syncOnSave = false;
|
||||||
|
this.plugin.settings.syncOnStart = false;
|
||||||
|
this.plugin.settings.syncOnFileOpen = false;
|
||||||
|
this.plugin.settings.syncAfterMerge = false;
|
||||||
|
//this.suspendExtraSync();
|
||||||
|
}
|
||||||
|
async fetchLocal() {
|
||||||
|
this.suspendExtraSync();
|
||||||
|
await this.plugin.realizeSettingSyncMode();
|
||||||
|
await this.plugin.resetLocalDatabase();
|
||||||
|
await delay(1000);
|
||||||
|
await this.plugin.markRemoteResolved();
|
||||||
|
await this.plugin.openDatabase();
|
||||||
|
this.plugin.isReady = true;
|
||||||
|
await delay(500);
|
||||||
|
await this.plugin.replicateAllFromServer(true);
|
||||||
|
}
|
||||||
|
async rebuildRemote() {
|
||||||
|
this.suspendExtraSync();
|
||||||
|
await this.plugin.realizeSettingSyncMode();
|
||||||
|
await this.plugin.markRemoteLocked();
|
||||||
|
await this.plugin.tryResetRemoteDatabase();
|
||||||
|
await this.plugin.markRemoteLocked();
|
||||||
|
await delay(500);
|
||||||
|
await this.plugin.replicateAllToServer(true);
|
||||||
|
}
|
||||||
|
async rebuildEverything() {
|
||||||
|
this.suspendExtraSync();
|
||||||
|
await this.plugin.realizeSettingSyncMode();
|
||||||
|
await this.plugin.resetLocalDatabase();
|
||||||
|
await delay(1000);
|
||||||
|
await this.plugin.initializeDatabase(true);
|
||||||
|
await this.plugin.markRemoteLocked();
|
||||||
|
await this.plugin.tryResetRemoteDatabase();
|
||||||
|
await this.plugin.markRemoteLocked();
|
||||||
|
await delay(500);
|
||||||
|
await this.plugin.replicateAllToServer(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -420,25 +420,16 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
if (!encrypt) {
|
if (!encrypt) {
|
||||||
passphrase = "";
|
passphrase = "";
|
||||||
}
|
}
|
||||||
this.plugin.settings.liveSync = false;
|
this.plugin.addOnSetup.suspendAllSync();
|
||||||
this.plugin.settings.periodicReplication = false;
|
this.plugin.addOnSetup.suspendExtraSync();
|
||||||
this.plugin.settings.syncOnSave = false;
|
|
||||||
this.plugin.settings.syncOnStart = false;
|
|
||||||
this.plugin.settings.syncOnFileOpen = false;
|
|
||||||
this.plugin.settings.syncAfterMerge = false;
|
|
||||||
this.plugin.settings.encrypt = encrypt;
|
this.plugin.settings.encrypt = encrypt;
|
||||||
this.plugin.settings.passphrase = passphrase;
|
this.plugin.settings.passphrase = passphrase;
|
||||||
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
||||||
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
||||||
|
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
markDirtyControl();
|
markDirtyControl();
|
||||||
if (sendToServer) {
|
if (sendToServer) {
|
||||||
await this.plugin.initializeDatabase(true);
|
await this.plugin.addOnSetup.rebuildRemote()
|
||||||
await this.plugin.markRemoteLocked();
|
|
||||||
await this.plugin.tryResetRemoteDatabase();
|
|
||||||
await this.plugin.markRemoteLocked();
|
|
||||||
await this.plugin.replicateAllToServer(true);
|
|
||||||
} else {
|
} else {
|
||||||
await this.plugin.markRemoteResolved();
|
await this.plugin.markRemoteResolved();
|
||||||
await this.plugin.replicate(true);
|
await this.plugin.replicate(true);
|
||||||
@@ -489,47 +480,27 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
if (!encrypt) {
|
if (!encrypt) {
|
||||||
passphrase = "";
|
passphrase = "";
|
||||||
}
|
}
|
||||||
this.plugin.settings.liveSync = false;
|
this.plugin.addOnSetup.suspendAllSync();
|
||||||
this.plugin.settings.periodicReplication = false;
|
this.plugin.addOnSetup.suspendExtraSync();
|
||||||
this.plugin.settings.syncOnSave = false;
|
|
||||||
this.plugin.settings.syncOnStart = false;
|
|
||||||
this.plugin.settings.syncOnFileOpen = false;
|
|
||||||
this.plugin.settings.syncAfterMerge = false;
|
|
||||||
this.plugin.settings.syncInternalFiles = false;
|
|
||||||
this.plugin.settings.usePluginSync = false;
|
|
||||||
this.plugin.settings.encrypt = encrypt;
|
this.plugin.settings.encrypt = encrypt;
|
||||||
this.plugin.settings.passphrase = passphrase;
|
this.plugin.settings.passphrase = passphrase;
|
||||||
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
||||||
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
||||||
Logger("Hidden files and plugin synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.", LOG_LEVEL.NOTICE)
|
Logger("All synchronizations have been temporarily disabled. Please enable them after the fetching, if you need them.", LOG_LEVEL.NOTICE)
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
markDirtyControl();
|
markDirtyControl();
|
||||||
applyDisplayEnabled();
|
applyDisplayEnabled();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.plugin.app.setting.close()
|
this.plugin.app.setting.close();
|
||||||
await delay(2000);
|
await delay(2000);
|
||||||
if (method == "localOnly") {
|
if (method == "localOnly") {
|
||||||
await this.plugin.resetLocalDatabase();
|
await this.plugin.addOnSetup.fetchLocal();
|
||||||
await delay(1000);
|
|
||||||
await this.plugin.markRemoteResolved();
|
|
||||||
await this.plugin.openDatabase();
|
|
||||||
this.plugin.isReady = true;
|
|
||||||
await this.plugin.replicateAllFromServer(true);
|
|
||||||
}
|
}
|
||||||
if (method == "remoteOnly") {
|
if (method == "remoteOnly") {
|
||||||
await this.plugin.markRemoteLocked();
|
await this.plugin.addOnSetup.rebuildRemote();
|
||||||
await this.plugin.tryResetRemoteDatabase();
|
|
||||||
await this.plugin.markRemoteLocked();
|
|
||||||
await this.plugin.replicateAllToServer(true);
|
|
||||||
}
|
}
|
||||||
if (method == "rebuildBothByThisDevice") {
|
if (method == "rebuildBothByThisDevice") {
|
||||||
await this.plugin.resetLocalDatabase();
|
await this.plugin.addOnSetup.rebuildEverything();
|
||||||
await delay(1000);
|
|
||||||
await this.plugin.initializeDatabase(true);
|
|
||||||
await this.plugin.markRemoteLocked();
|
|
||||||
await this.plugin.tryResetRemoteDatabase();
|
|
||||||
await this.plugin.markRemoteLocked();
|
|
||||||
await this.plugin.replicateAllToServer(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1149,32 +1120,42 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
.addButton((button) => {
|
.addButton((button) => {
|
||||||
button.setButtonText("Merge")
|
button.setButtonText("Merge")
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
this.plugin.settings.syncInternalFiles = true;
|
this.plugin.addOnSetup.suspendExtraSync();
|
||||||
this.display();
|
this.display();
|
||||||
|
Logger("Gathering files for enabling Hidden File Sync", LOG_LEVEL.NOTICE);
|
||||||
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("safe", true);
|
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("safe", true);
|
||||||
|
this.plugin.settings.syncInternalFiles = true;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.display();
|
Logger(`Done!`, LOG_LEVEL.NOTICE);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.addButton((button) => {
|
.addButton((button) => {
|
||||||
button.setButtonText("Fetch")
|
button.setButtonText("Fetch")
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
this.plugin.settings.syncInternalFiles = true;
|
this.plugin.addOnSetup.suspendExtraSync();
|
||||||
this.display();
|
// @ts-ignore
|
||||||
|
this.plugin.app.setting.close()
|
||||||
|
Logger("Gathering files for enabling Hidden File Sync", LOG_LEVEL.NOTICE);
|
||||||
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pullForce", true);
|
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pullForce", true);
|
||||||
|
this.plugin.settings.syncInternalFiles = true;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
Logger(`Restarting the app is strongly recommended!`, LOG_LEVEL.NOTICE);
|
Logger(`Done! Restarting the app is strongly recommended!`, LOG_LEVEL.NOTICE);
|
||||||
this.display();
|
// this.display();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.addButton((button) => {
|
.addButton((button) => {
|
||||||
button.setButtonText("Overwrite")
|
button.setButtonText("Overwrite")
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
this.plugin.settings.syncInternalFiles = true;
|
this.plugin.addOnSetup.suspendExtraSync();
|
||||||
this.display();
|
// @ts-ignore
|
||||||
|
this.plugin.app.setting.close()
|
||||||
|
Logger("Gathering files for enabling Hidden File Sync", LOG_LEVEL.NOTICE);
|
||||||
|
// this.display();
|
||||||
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pushForce", true);
|
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pushForce", true);
|
||||||
|
this.plugin.settings.syncInternalFiles = true;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.display();
|
Logger(`Done!`, LOG_LEVEL.NOTICE);
|
||||||
|
// this.display();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
watchVaultRename(file: TAbstractFile, oldFile: string, ctx?: any) {
|
watchVaultRename(file: TAbstractFile, oldFile: string, ctx?: any) {
|
||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
this.appendWatchEvent([
|
this.appendWatchEvent([
|
||||||
{ type: "CREATE", file },
|
|
||||||
{ type: "DELETE", file: { path: oldFile as FilePath, mtime: file.stat.mtime, ctime: file.stat.ctime, size: file.stat.size, deleted: true } },
|
{ type: "DELETE", file: { path: oldFile as FilePath, mtime: file.stat.mtime, ctime: file.stat.ctime, size: file.stat.size, deleted: true } },
|
||||||
|
{ type: "CREATE", file },
|
||||||
], ctx);
|
], ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/main.ts
45
src/main.ts
@@ -4,7 +4,7 @@ import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "di
|
|||||||
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, RequestUrlParam, RequestUrlResponse, requestUrl } from "./deps";
|
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, RequestUrlParam, RequestUrlResponse, requestUrl } from "./deps";
|
||||||
import { EntryDoc, LoadedEntry, ObsidianLiveSyncSettings, diff_check_result, diff_result_leaf, EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, ConfigPassphraseStore, CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, DatabaseConnectingStatus, EntryHasPath, DocumentID, FilePathWithPrefix, FilePath, AnyEntry } from "./lib/src/types";
|
import { EntryDoc, LoadedEntry, ObsidianLiveSyncSettings, diff_check_result, diff_result_leaf, EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, ConfigPassphraseStore, CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, DatabaseConnectingStatus, EntryHasPath, DocumentID, FilePathWithPrefix, FilePath, AnyEntry } from "./lib/src/types";
|
||||||
import { InternalFileInfo, queueItem, CacheData, FileEventItem, FileWatchEventQueueMax } from "./types";
|
import { InternalFileInfo, queueItem, CacheData, FileEventItem, FileWatchEventQueueMax } from "./types";
|
||||||
import { delay, getDocData, isDocContentSame } from "./lib/src/utils";
|
import { getDocData, isDocContentSame } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||||
import { LogDisplayModal } from "./LogDisplayModal";
|
import { LogDisplayModal } from "./LogDisplayModal";
|
||||||
@@ -387,25 +387,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
try {
|
try {
|
||||||
if (this.isRedFlagRaised() || this.isRedFlag2Raised() || this.isRedFlag3Raised()) {
|
if (this.isRedFlagRaised() || this.isRedFlag2Raised() || this.isRedFlag3Raised()) {
|
||||||
this.settings.batchSave = false;
|
this.settings.batchSave = false;
|
||||||
this.settings.liveSync = false;
|
this.addOnSetup.suspendAllSync();
|
||||||
this.settings.periodicReplication = false;
|
this.addOnSetup.suspendExtraSync();
|
||||||
this.settings.syncOnSave = false;
|
|
||||||
this.settings.syncOnStart = false;
|
|
||||||
this.settings.syncOnFileOpen = false;
|
|
||||||
this.settings.syncAfterMerge = false;
|
|
||||||
this.settings.autoSweepPlugins = false;
|
|
||||||
this.settings.usePluginSync = false;
|
|
||||||
this.settings.suspendFileWatching = true;
|
this.settings.suspendFileWatching = true;
|
||||||
this.settings.syncInternalFiles = false;
|
|
||||||
await this.saveSettings();
|
await this.saveSettings();
|
||||||
if (this.isRedFlag2Raised()) {
|
if (this.isRedFlag2Raised()) {
|
||||||
Logger(`${FLAGMD_REDFLAG2} has been detected! Self-hosted LiveSync suspends all sync and rebuild everything.`, LOG_LEVEL.NOTICE);
|
Logger(`${FLAGMD_REDFLAG2} has been detected! Self-hosted LiveSync suspends all sync and rebuild everything.`, LOG_LEVEL.NOTICE);
|
||||||
await this.resetLocalDatabase();
|
await this.addOnSetup.rebuildEverything();
|
||||||
await this.initializeDatabase(true);
|
|
||||||
await this.markRemoteLocked();
|
|
||||||
await this.tryResetRemoteDatabase();
|
|
||||||
await this.markRemoteLocked();
|
|
||||||
await this.replicateAllToServer(true);
|
|
||||||
await this.deleteRedFlag2();
|
await this.deleteRedFlag2();
|
||||||
if (await askYesNo(this.app, "Do you want to disable Suspend file watching and restart obsidian now?") == "yes") {
|
if (await askYesNo(this.app, "Do you want to disable Suspend file watching and restart obsidian now?") == "yes") {
|
||||||
this.settings.suspendFileWatching = false;
|
this.settings.suspendFileWatching = false;
|
||||||
@@ -415,12 +403,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
} else if (this.isRedFlag3Raised()) {
|
} else if (this.isRedFlag3Raised()) {
|
||||||
Logger(`${FLAGMD_REDFLAG3} has been detected! Self-hosted LiveSync will discard the local database and fetch everything from the remote once again.`, LOG_LEVEL.NOTICE);
|
Logger(`${FLAGMD_REDFLAG3} has been detected! Self-hosted LiveSync will discard the local database and fetch everything from the remote once again.`, LOG_LEVEL.NOTICE);
|
||||||
await this.resetLocalDatabase();
|
await this.addOnSetup.fetchLocal();
|
||||||
await delay(1000);
|
|
||||||
await this.markRemoteResolved();
|
|
||||||
await this.openDatabase();
|
|
||||||
this.isReady = true;
|
|
||||||
await this.replicateAllFromServer(true);
|
|
||||||
await this.deleteRedFlag3();
|
await this.deleteRedFlag3();
|
||||||
if (await askYesNo(this.app, "Do you want to disable Suspend file watching and restart obsidian now?") == "yes") {
|
if (await askYesNo(this.app, "Do you want to disable Suspend file watching and restart obsidian now?") == "yes") {
|
||||||
this.settings.suspendFileWatching = false;
|
this.settings.suspendFileWatching = false;
|
||||||
@@ -912,8 +895,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
const file = queue.args.file;
|
const file = queue.args.file;
|
||||||
const key = `file-last-proc-${queue.type}-${file.path}`;
|
const key = `file-last-proc-${queue.type}-${file.path}`;
|
||||||
const last = Number(await this.kvDB.get(key) || 0);
|
const last = Number(await this.kvDB.get(key) || 0);
|
||||||
|
let mtime = file.mtime;
|
||||||
if (queue.type == "DELETE") {
|
if (queue.type == "DELETE") {
|
||||||
await this.deleteFromDBbyPath(file.path);
|
await this.deleteFromDBbyPath(file.path);
|
||||||
|
mtime = file.mtime - 1;
|
||||||
|
const keyD1 = `file-last-proc-CREATE-${file.path}`;
|
||||||
|
const keyD2 = `file-last-proc-CHANGED-${file.path}`;
|
||||||
|
await this.kvDB.set(keyD1, mtime);
|
||||||
|
await this.kvDB.set(keyD2, mtime);
|
||||||
} else if (queue.type == "INTERNAL") {
|
} else if (queue.type == "INTERNAL") {
|
||||||
await this.addOnHiddenFileSync.watchVaultRawEventsAsync(file.path);
|
await this.addOnHiddenFileSync.watchVaultRawEventsAsync(file.path);
|
||||||
} else {
|
} else {
|
||||||
@@ -930,6 +919,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
|
|
||||||
const cache = queue.args.cache;
|
const cache = queue.args.cache;
|
||||||
if (queue.type == "CREATE" || queue.type == "CHANGED") {
|
if (queue.type == "CREATE" || queue.type == "CHANGED") {
|
||||||
|
const keyD1 = `file-last-proc-DELETED-${file.path}`;
|
||||||
|
await this.kvDB.set(keyD1, mtime);
|
||||||
if (!await this.updateIntoDB(targetFile, false, cache)) {
|
if (!await this.updateIntoDB(targetFile, false, cache)) {
|
||||||
Logger(`DB -> STORAGE: failed, cancel the relative operations: ${targetFile.path}`, LOG_LEVEL.INFO);
|
Logger(`DB -> STORAGE: failed, cancel the relative operations: ${targetFile.path}`, LOG_LEVEL.INFO);
|
||||||
// cancel running queues and remove one of atomic operation
|
// cancel running queues and remove one of atomic operation
|
||||||
@@ -942,7 +933,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
await this.watchVaultRenameAsync(targetFile, queue.args.oldPath);
|
await this.watchVaultRenameAsync(targetFile, queue.args.oldPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.kvDB.set(key, file.mtime);
|
await this.kvDB.set(key, mtime);
|
||||||
} while (this.vaultManager.getQueueLength() > 0);
|
} while (this.vaultManager.getQueueLength() > 0);
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
@@ -1114,13 +1105,16 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
// This occurs not only when files are deleted, but also when conflicts are resolved.
|
// This occurs not only when files are deleted, but also when conflicts are resolved.
|
||||||
// We have to check no other revisions are left.
|
// We have to check no other revisions are left.
|
||||||
const lastDocs = await this.localDatabase.getDBEntry(path);
|
const lastDocs = await this.localDatabase.getDBEntry(path);
|
||||||
|
if (path != file.path) {
|
||||||
|
Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL.VERBOSE);
|
||||||
|
}
|
||||||
if (lastDocs === false) {
|
if (lastDocs === false) {
|
||||||
await this.deleteVaultItem(file);
|
await this.deleteVaultItem(file);
|
||||||
} else {
|
} else {
|
||||||
// it perhaps delete some revisions.
|
// it perhaps delete some revisions.
|
||||||
// may be we have to reload this
|
// may be we have to reload this
|
||||||
await this.pullFile(path, null, true);
|
await this.pullFile(path, null, true);
|
||||||
Logger(`delete skipped:${lastDocs._id}`, LOG_LEVEL.VERBOSE);
|
Logger(`delete skipped:${file.path}`, LOG_LEVEL.VERBOSE);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1381,8 +1375,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
|
|
||||||
//---> Sync
|
//---> Sync
|
||||||
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
||||||
|
const docsSorted = docs.sort((a, b) => b.mtime - a.mtime);
|
||||||
L1:
|
L1:
|
||||||
for (const change of docs) {
|
for (const change of docsSorted) {
|
||||||
for (const proc of this.addOns) {
|
for (const proc of this.addOns) {
|
||||||
if (await proc.parseReplicationResultItem(change)) {
|
if (await proc.parseReplicationResultItem(change)) {
|
||||||
continue L1;
|
continue L1;
|
||||||
|
|||||||
18
src/utils.ts
18
src/utils.ts
@@ -325,14 +325,16 @@ export function isValidPath(filename: string) {
|
|||||||
let touchedFiles: string[] = [];
|
let touchedFiles: string[] = [];
|
||||||
|
|
||||||
export function getAbstractFileByPath(path: FilePath): TAbstractFile | null {
|
export function getAbstractFileByPath(path: FilePath): TAbstractFile | null {
|
||||||
// Hidden API but so useful.
|
// Disabled temporary.
|
||||||
// @ts-ignore
|
return app.vault.getAbstractFileByPath(path);
|
||||||
if ("getAbstractFileByPathInsensitive" in app.vault && (app.vault.adapter?.insensitive ?? false)) {
|
// // Hidden API but so useful.
|
||||||
// @ts-ignore
|
// // @ts-ignore
|
||||||
return app.vault.getAbstractFileByPathInsensitive(path);
|
// if ("getAbstractFileByPathInsensitive" in app.vault && (app.vault.adapter?.insensitive ?? false)) {
|
||||||
} else {
|
// // @ts-ignore
|
||||||
return app.vault.getAbstractFileByPath(path);
|
// return app.vault.getAbstractFileByPathInsensitive(path);
|
||||||
}
|
// } else {
|
||||||
|
// return app.vault.getAbstractFileByPath(path);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
export function trimPrefix(target: string, prefix: string) {
|
export function trimPrefix(target: string, prefix: string) {
|
||||||
return target.startsWith(prefix) ? target.substring(prefix.length) : target;
|
return target.startsWith(prefix) ? target.substring(prefix.length) : target;
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ Note: **When changing this configuration, we need to rebuild both of the local a
|
|||||||
- The setting pane has been refined.
|
- The setting pane has been refined.
|
||||||
- We can enable `hidden files sync` with several initial behaviours; `Merge`, `Fetch` remote, and `Overwrite` remote.
|
- We can enable `hidden files sync` with several initial behaviours; `Merge`, `Fetch` remote, and `Overwrite` remote.
|
||||||
- No longer `Touch hidden files`.
|
- No longer `Touch hidden files`.
|
||||||
|
- 0.18.3
|
||||||
|
- Fixed Pop-up is now correctly shown after hidden file synchronisation.
|
||||||
|
- 0.18.4
|
||||||
|
- Fixed:
|
||||||
|
- `Fetch` and `Rebuild database` will work more safely.
|
||||||
|
- Case-sensitive renaming now works fine.
|
||||||
|
Revoked the logic which was made at #130, however, looks fine now.
|
||||||
|
|
||||||
### 0.17.0
|
### 0.17.0
|
||||||
- 0.17.0 has no surfaced changes but the design of saving chunks has been changed. They have compatibility but changing files after upgrading makes different chunks than before 0.16.x.
|
- 0.17.0 has no surfaced changes but the design of saving chunks has been changed. They have compatibility but changing files after upgrading makes different chunks than before 0.16.x.
|
||||||
|
|||||||
Reference in New Issue
Block a user