mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2024-12-12 09:04:06 +02:00
- 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.
This commit is contained in:
parent
851c9f8a71
commit
3322d13b55
@ -5,6 +5,7 @@ import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||
import { askSelectString, askYesNo, askString } from "./utils";
|
||||
import { decrypt, encrypt } from "./lib/src/e2ee_v2";
|
||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||
import { delay } from "./lib/src/utils";
|
||||
|
||||
export class SetupLiveSync extends LiveSyncCommands {
|
||||
onunload() { }
|
||||
@ -97,7 +98,8 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
const setupAsNew = "Set it up as secondary or subsequent device";
|
||||
const setupAgain = "Reconfigure and reconstitute the data";
|
||||
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]);
|
||||
if (setupType == setupJustImport) {
|
||||
this.plugin.settings = newSettingW;
|
||||
@ -106,11 +108,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
} else if (setupType == setupAsNew) {
|
||||
this.plugin.settings = newSettingW;
|
||||
this.plugin.usedPassphrase = "";
|
||||
await this.plugin.saveSettings();
|
||||
await this.plugin.resetLocalDatabase();
|
||||
await this.plugin.localDatabase.initializeDatabase();
|
||||
await this.plugin.markRemoteResolved();
|
||||
await this.plugin.replicate(true);
|
||||
await this.fetchLocal();
|
||||
} 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.";
|
||||
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.usedPassphrase = "";
|
||||
await this.plugin.saveSettings();
|
||||
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);
|
||||
|
||||
await this.rebuildEverything();
|
||||
} else if (setupType == setupManually) {
|
||||
const keepLocalDB = await askYesNo(this.app, "Keep local DB?");
|
||||
const keepRemoteDB = await askYesNo(this.app, "Keep remote DB?");
|
||||
@ -134,6 +124,8 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
// nothing to do. so peaceful.
|
||||
this.plugin.settings = newSettingW;
|
||||
this.plugin.usedPassphrase = "";
|
||||
this.suspendAllSync();
|
||||
this.suspendExtraSync();
|
||||
await this.plugin.saveSettings();
|
||||
const replicate = await askYesNo(this.app, "Unlock and replicate?");
|
||||
if (replicate == "yes") {
|
||||
@ -189,4 +181,52 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
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) {
|
||||
passphrase = "";
|
||||
}
|
||||
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.plugin.addOnSetup.suspendAllSync();
|
||||
this.plugin.addOnSetup.suspendExtraSync();
|
||||
this.plugin.settings.encrypt = encrypt;
|
||||
this.plugin.settings.passphrase = passphrase;
|
||||
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
||||
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
||||
|
||||
await this.plugin.saveSettings();
|
||||
markDirtyControl();
|
||||
if (sendToServer) {
|
||||
await this.plugin.initializeDatabase(true);
|
||||
await this.plugin.markRemoteLocked();
|
||||
await this.plugin.tryResetRemoteDatabase();
|
||||
await this.plugin.markRemoteLocked();
|
||||
await this.plugin.replicateAllToServer(true);
|
||||
await this.plugin.addOnSetup.rebuildRemote()
|
||||
} else {
|
||||
await this.plugin.markRemoteResolved();
|
||||
await this.plugin.replicate(true);
|
||||
@ -489,47 +480,27 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
if (!encrypt) {
|
||||
passphrase = "";
|
||||
}
|
||||
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.plugin.settings.syncInternalFiles = false;
|
||||
this.plugin.settings.usePluginSync = false;
|
||||
this.plugin.addOnSetup.suspendAllSync();
|
||||
this.plugin.addOnSetup.suspendExtraSync();
|
||||
this.plugin.settings.encrypt = encrypt;
|
||||
this.plugin.settings.passphrase = passphrase;
|
||||
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
||||
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();
|
||||
markDirtyControl();
|
||||
applyDisplayEnabled();
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
this.plugin.app.setting.close();
|
||||
await delay(2000);
|
||||
if (method == "localOnly") {
|
||||
await this.plugin.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.plugin.markRemoteResolved();
|
||||
await this.plugin.openDatabase();
|
||||
this.plugin.isReady = true;
|
||||
await this.plugin.replicateAllFromServer(true);
|
||||
await this.plugin.addOnSetup.fetchLocal();
|
||||
}
|
||||
if (method == "remoteOnly") {
|
||||
await this.plugin.markRemoteLocked();
|
||||
await this.plugin.tryResetRemoteDatabase();
|
||||
await this.plugin.markRemoteLocked();
|
||||
await this.plugin.replicateAllToServer(true);
|
||||
await this.plugin.addOnSetup.rebuildRemote();
|
||||
}
|
||||
if (method == "rebuildBothByThisDevice") {
|
||||
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 this.plugin.replicateAllToServer(true);
|
||||
await this.plugin.addOnSetup.rebuildEverything();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1149,32 +1120,42 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Merge")
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.syncInternalFiles = true;
|
||||
this.plugin.addOnSetup.suspendExtraSync();
|
||||
this.display();
|
||||
Logger("Gathering files for enabling Hidden File Sync", LOG_LEVEL.NOTICE);
|
||||
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("safe", true);
|
||||
this.plugin.settings.syncInternalFiles = true;
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
Logger(`Done!`, LOG_LEVEL.NOTICE);
|
||||
})
|
||||
})
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Fetch")
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.syncInternalFiles = true;
|
||||
this.display();
|
||||
this.plugin.addOnSetup.suspendExtraSync();
|
||||
// @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);
|
||||
this.plugin.settings.syncInternalFiles = true;
|
||||
await this.plugin.saveSettings();
|
||||
Logger(`Restarting the app is strongly recommended!`, LOG_LEVEL.NOTICE);
|
||||
this.display();
|
||||
Logger(`Done! Restarting the app is strongly recommended!`, LOG_LEVEL.NOTICE);
|
||||
// this.display();
|
||||
})
|
||||
})
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Overwrite")
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.syncInternalFiles = true;
|
||||
this.display();
|
||||
this.plugin.addOnSetup.suspendExtraSync();
|
||||
// @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);
|
||||
this.plugin.settings.syncInternalFiles = true;
|
||||
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) {
|
||||
if (file instanceof TFile) {
|
||||
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: "CREATE", file },
|
||||
], 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 { 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 { delay, getDocData, isDocContentSame } from "./lib/src/utils";
|
||||
import { getDocData, isDocContentSame } from "./lib/src/utils";
|
||||
import { Logger } from "./lib/src/logger";
|
||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||
import { LogDisplayModal } from "./LogDisplayModal";
|
||||
@ -387,25 +387,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
try {
|
||||
if (this.isRedFlagRaised() || this.isRedFlag2Raised() || this.isRedFlag3Raised()) {
|
||||
this.settings.batchSave = false;
|
||||
this.settings.liveSync = false;
|
||||
this.settings.periodicReplication = false;
|
||||
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.addOnSetup.suspendAllSync();
|
||||
this.addOnSetup.suspendExtraSync();
|
||||
this.settings.suspendFileWatching = true;
|
||||
this.settings.syncInternalFiles = false;
|
||||
await this.saveSettings();
|
||||
if (this.isRedFlag2Raised()) {
|
||||
Logger(`${FLAGMD_REDFLAG2} has been detected! Self-hosted LiveSync suspends all sync and rebuild everything.`, LOG_LEVEL.NOTICE);
|
||||
await this.resetLocalDatabase();
|
||||
await this.initializeDatabase(true);
|
||||
await this.markRemoteLocked();
|
||||
await this.tryResetRemoteDatabase();
|
||||
await this.markRemoteLocked();
|
||||
await this.replicateAllToServer(true);
|
||||
await this.addOnSetup.rebuildEverything();
|
||||
await this.deleteRedFlag2();
|
||||
if (await askYesNo(this.app, "Do you want to disable Suspend file watching and restart obsidian now?") == "yes") {
|
||||
this.settings.suspendFileWatching = false;
|
||||
@ -415,12 +403,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
}
|
||||
} 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);
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.markRemoteResolved();
|
||||
await this.openDatabase();
|
||||
this.isReady = true;
|
||||
await this.replicateAllFromServer(true);
|
||||
await this.addOnSetup.fetchLocal();
|
||||
await this.deleteRedFlag3();
|
||||
if (await askYesNo(this.app, "Do you want to disable Suspend file watching and restart obsidian now?") == "yes") {
|
||||
this.settings.suspendFileWatching = false;
|
||||
@ -912,8 +895,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
const file = queue.args.file;
|
||||
const key = `file-last-proc-${queue.type}-${file.path}`;
|
||||
const last = Number(await this.kvDB.get(key) || 0);
|
||||
let mtime = file.mtime;
|
||||
if (queue.type == "DELETE") {
|
||||
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") {
|
||||
await this.addOnHiddenFileSync.watchVaultRawEventsAsync(file.path);
|
||||
} else {
|
||||
@ -930,6 +919,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
|
||||
const cache = queue.args.cache;
|
||||
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)) {
|
||||
Logger(`DB -> STORAGE: failed, cancel the relative operations: ${targetFile.path}`, LOG_LEVEL.INFO);
|
||||
// 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.kvDB.set(key, file.mtime);
|
||||
await this.kvDB.set(key, mtime);
|
||||
} while (this.vaultManager.getQueueLength() > 0);
|
||||
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.
|
||||
// We have to check no other revisions are left.
|
||||
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) {
|
||||
await this.deleteVaultItem(file);
|
||||
} else {
|
||||
// it perhaps delete some revisions.
|
||||
// may be we have to reload this
|
||||
await this.pullFile(path, null, true);
|
||||
Logger(`delete skipped:${lastDocs._id}`, LOG_LEVEL.VERBOSE);
|
||||
Logger(`delete skipped:${file.path}`, LOG_LEVEL.VERBOSE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1381,8 +1375,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
|
||||
//---> Sync
|
||||
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
||||
const docsSorted = docs.sort((a, b) => b.mtime - a.mtime);
|
||||
L1:
|
||||
for (const change of docs) {
|
||||
for (const change of docsSorted) {
|
||||
for (const proc of this.addOns) {
|
||||
if (await proc.parseReplicationResultItem(change)) {
|
||||
continue L1;
|
||||
|
18
src/utils.ts
18
src/utils.ts
@ -325,14 +325,16 @@ export function isValidPath(filename: string) {
|
||||
let touchedFiles: string[] = [];
|
||||
|
||||
export function getAbstractFileByPath(path: FilePath): TAbstractFile | null {
|
||||
// Hidden API but so useful.
|
||||
// @ts-ignore
|
||||
if ("getAbstractFileByPathInsensitive" in app.vault && (app.vault.adapter?.insensitive ?? false)) {
|
||||
// @ts-ignore
|
||||
return app.vault.getAbstractFileByPathInsensitive(path);
|
||||
} else {
|
||||
return app.vault.getAbstractFileByPath(path);
|
||||
}
|
||||
// Disabled temporary.
|
||||
return app.vault.getAbstractFileByPath(path);
|
||||
// // Hidden API but so useful.
|
||||
// // @ts-ignore
|
||||
// if ("getAbstractFileByPathInsensitive" in app.vault && (app.vault.adapter?.insensitive ?? false)) {
|
||||
// // @ts-ignore
|
||||
// return app.vault.getAbstractFileByPathInsensitive(path);
|
||||
// } else {
|
||||
// return app.vault.getAbstractFileByPath(path);
|
||||
// }
|
||||
}
|
||||
export function trimPrefix(target: string, prefix: string) {
|
||||
return target.startsWith(prefix) ? target.substring(prefix.length) : target;
|
||||
|
Loading…
Reference in New Issue
Block a user