mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-03-23 02:05:17 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
badec46d9a | ||
|
|
355e41f488 | ||
|
|
e0e7e1b5ca | ||
|
|
ce4b61557a | ||
|
|
52b02f3888 | ||
|
|
7535999388 | ||
|
|
dccf8580b8 |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.24.0",
|
||||
"version": "0.25.0",
|
||||
"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",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.24.30",
|
||||
"version": "0.25.0",
|
||||
"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",
|
||||
|
||||
1459
package-lock.json
generated
1459
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.24.30",
|
||||
"version": "0.25.0",
|
||||
"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",
|
||||
@@ -89,7 +89,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.1",
|
||||
"octagonal-wheels": "^0.1.31",
|
||||
"octagonal-wheels": "^0.1.35",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"svelte-check": "^4.1.7",
|
||||
"trystero": "^0.21.5",
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: dfbd6358b1...ab02f72aa5
@@ -292,7 +292,8 @@ export default class ObsidianLiveSyncPlugin
|
||||
skipInfo: boolean,
|
||||
compression: boolean,
|
||||
customHeaders: Record<string, string>,
|
||||
useRequestAPI: boolean
|
||||
useRequestAPI: boolean,
|
||||
getPBKDF2Salt: () => Promise<Uint8Array>
|
||||
): Promise<
|
||||
| string
|
||||
| {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { Rebuilder } from "../interfaces/DatabaseRebuilder.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import type { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator.ts";
|
||||
import { fetchAllUsedChunks } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { fetchAllUsedChunks } from "@/lib/src/pouchdb/chunks.ts";
|
||||
import { EVENT_DATABASE_REBUILT, eventHub } from "src/common/events.ts";
|
||||
|
||||
export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebuilder {
|
||||
@@ -90,8 +90,8 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
return this.rebuildEverything();
|
||||
}
|
||||
|
||||
$fetchLocal(makeLocalChunkBeforeSync?: boolean): Promise<void> {
|
||||
return this.fetchLocal(makeLocalChunkBeforeSync);
|
||||
$fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean): Promise<void> {
|
||||
return this.fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync);
|
||||
}
|
||||
|
||||
async scheduleRebuild(): Promise<void> {
|
||||
|
||||
@@ -4,7 +4,8 @@ import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||
import { purgeUnreferencedChunks, balanceChunkPurgedDBs } from "../../lib/src/pouchdb/utils_couchdb";
|
||||
import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks";
|
||||
import { purgeUnreferencedChunks } from "@/lib/src/pouchdb/chunks";
|
||||
import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator";
|
||||
import { throttle } from "octagonal-wheels/function";
|
||||
import { arrayToChunkedArray } from "octagonal-wheels/collection";
|
||||
@@ -79,8 +80,18 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
||||
$everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
return this.setReplicator();
|
||||
}
|
||||
async ensureReplicatorPBKDF2Salt(showMessage: boolean = false): Promise<boolean> {
|
||||
// Checking salt
|
||||
const replicator = this.core.$$getReplicator();
|
||||
return await replicator.ensurePBKDF2Salt(this.settings, showMessage, true);
|
||||
}
|
||||
|
||||
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
|
||||
// Checking salt
|
||||
if (!(await this.ensureReplicatorPBKDF2Salt(showMessage))) {
|
||||
Logger("Failed to ensure PBKDF2 salt for replication.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
await this.loadQueuedFiles();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -37,13 +37,17 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
|
||||
// TODO-> Move to ModuleConflictResolver?
|
||||
conflictResolveQueue = new QueueProcessor(
|
||||
async (filenames: FilePathWithPrefix[]) => {
|
||||
await this.core.$$resolveConflict(filenames[0]);
|
||||
const filename = filenames[0];
|
||||
return await this.core.$$resolveConflict(filename);
|
||||
},
|
||||
{
|
||||
suspended: false,
|
||||
batchSize: 1,
|
||||
concurrentLimit: 1,
|
||||
delay: 10,
|
||||
// No need to limit concurrency to `1` here, subsequent process will handle it,
|
||||
// And, some cases, we do not need to synchronised. (e.g., auto-merge available).
|
||||
// Therefore, limiting global concurrency is performed on resolver with the UI.
|
||||
concurrentLimit: 10,
|
||||
delay: 0,
|
||||
keepResultUntilDownstreamConnected: false,
|
||||
}
|
||||
).replaceEnqueueProcessor((queue, newEntity) => {
|
||||
@@ -57,19 +61,13 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
|
||||
new QueueProcessor(
|
||||
(files: FilePathWithPrefix[]) => {
|
||||
const filename = files[0];
|
||||
// const file = await this.core.storageAccess.isExists(filename);
|
||||
// if (!file) return [];
|
||||
// if (!(file instanceof TFile)) return;
|
||||
// if ((file instanceof TFolder)) return [];
|
||||
// Check again?
|
||||
return Promise.resolve([filename]);
|
||||
// this.conflictResolveQueue.enqueueWithKey(filename, { filename, file });
|
||||
},
|
||||
{
|
||||
suspended: false,
|
||||
batchSize: 1,
|
||||
concurrentLimit: 5,
|
||||
delay: 10,
|
||||
concurrentLimit: 10,
|
||||
delay: 0,
|
||||
keepResultUntilDownstreamConnected: true,
|
||||
pipeTo: this.conflictResolveQueue,
|
||||
totalRemainingReactiveSource: this.core.conflictProcessQueueCount,
|
||||
|
||||
@@ -121,9 +121,9 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
);
|
||||
let makeLocalChunkBeforeSync = false;
|
||||
let preventMakeLocalFilesBeforeSync = false;
|
||||
let makeLocalFilesBeforeSync = false;
|
||||
if (chunkMode === method1) {
|
||||
preventMakeLocalFilesBeforeSync = true;
|
||||
makeLocalFilesBeforeSync = true;
|
||||
} else if (chunkMode === method2) {
|
||||
makeLocalChunkBeforeSync = true;
|
||||
} else if (chunkMode === method3) {
|
||||
@@ -133,7 +133,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync);
|
||||
await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, !makeLocalFilesBeforeSync);
|
||||
|
||||
await this.deleteRedFlag3();
|
||||
if (this.settings.suspendFileWatching) {
|
||||
|
||||
@@ -84,8 +84,8 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
$msg("Doctor.Dialogue.MainFix", {
|
||||
name: getConfName(key as AllSettingItemKey),
|
||||
current: `${this.settings[key]}`,
|
||||
reason: value.reason ?? " N/A ",
|
||||
ideal: `${value.valueDisplay ?? value.value}`,
|
||||
reason: value.reasonFunc?.(this.settings) ?? value.reason ?? " N/A ",
|
||||
ideal: `${value.valueDisplayFunc ? value.valueDisplayFunc(this.settings) : value.value}`,
|
||||
//@ts-ignore
|
||||
level: `${level}`,
|
||||
note: note,
|
||||
|
||||
@@ -3,13 +3,10 @@ import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-
|
||||
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
|
||||
import { type CouchDBCredentials, type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||
import { getPathFromTFile } from "../../common/utils.ts";
|
||||
import {
|
||||
disableEncryption,
|
||||
enableEncryption,
|
||||
isCloudantURI,
|
||||
isValidRemoteCouchDBURI,
|
||||
replicationFilter,
|
||||
} from "../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { replicationFilter } from "@/lib/src/pouchdb/compress.ts";
|
||||
import { disableEncryption } from "@/lib/src/pouchdb/encryption.ts";
|
||||
import { enableEncryption } from "@/lib/src/pouchdb/encryption.ts";
|
||||
import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts";
|
||||
import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts";
|
||||
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
|
||||
@@ -103,7 +100,8 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
||||
skipInfo: boolean,
|
||||
compression: boolean,
|
||||
customHeaders: Record<string, string>,
|
||||
useRequestAPI: boolean
|
||||
useRequestAPI: boolean,
|
||||
getPBKDF2Salt: () => Promise<Uint8Array>
|
||||
): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
||||
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
||||
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
||||
@@ -229,7 +227,14 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
||||
replicationFilter(db, compression);
|
||||
disableEncryption();
|
||||
if (passphrase !== "false" && typeof passphrase === "string") {
|
||||
enableEncryption(db, passphrase, useDynamicIterationCount, false);
|
||||
enableEncryption(
|
||||
db,
|
||||
passphrase,
|
||||
useDynamicIterationCount,
|
||||
false,
|
||||
getPBKDF2Salt,
|
||||
this.settings.E2EEAlgorithm
|
||||
);
|
||||
}
|
||||
if (skipInfo) {
|
||||
return { db: db, info: { db_name: "", doc_count: 0, update_seq: "" } };
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ConflictResolveModal } from "./InteractiveConflictResolving/ConflictRes
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { displayRev, getPath, getPathWithoutPrefix } from "../../common/utils.ts";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
import { serialized } from "../../lib/src/concurrency/lock.ts";
|
||||
|
||||
export class ModuleInteractiveConflictResolver extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
@@ -34,67 +35,71 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
}
|
||||
|
||||
async $anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise<boolean> {
|
||||
this._log("Merge:open conflict dialog", LOG_LEVEL_VERBOSE);
|
||||
const dialog = new ConflictResolveModal(this.app, filename, conflictCheckResult);
|
||||
dialog.open();
|
||||
const selected = await dialog.waitForResult();
|
||||
if (selected === CANCELLED) {
|
||||
// Cancelled by UI, or another conflict.
|
||||
this._log(`Merge: Cancelled ${filename}`, LOG_LEVEL_INFO);
|
||||
return false;
|
||||
}
|
||||
const testDoc = await this.localDatabase.getDBEntry(filename, { conflicts: true }, false, true, true);
|
||||
if (testDoc === false) {
|
||||
this._log(`Merge: Could not read ${filename} from the local database`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
if (!testDoc._conflicts) {
|
||||
this._log(`Merge: Nothing to do ${filename}`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
const toDelete = selected;
|
||||
// const toKeep = conflictCheckResult.left.rev != toDelete ? conflictCheckResult.left.rev : conflictCheckResult.right.rev;
|
||||
if (toDelete === LEAVE_TO_SUBSEQUENT) {
|
||||
// Concatenate both conflicted revisions.
|
||||
// Create a new file by concatenating both conflicted revisions.
|
||||
const p = conflictCheckResult.diff.map((e) => e[1]).join("");
|
||||
const delRev = testDoc._conflicts[0];
|
||||
if (!(await this.core.databaseFileAccess.storeContent(filename, p))) {
|
||||
this._log(`Concatenated content cannot be stored:${filename}`, LOG_LEVEL_NOTICE);
|
||||
// UI for resolving conflicts should one-by-one.
|
||||
return await serialized(`conflict-resolve-ui`, async () => {
|
||||
this._log("Merge:open conflict dialog", LOG_LEVEL_VERBOSE);
|
||||
const dialog = new ConflictResolveModal(this.app, filename, conflictCheckResult);
|
||||
dialog.open();
|
||||
const selected = await dialog.waitForResult();
|
||||
if (selected === CANCELLED) {
|
||||
// Cancelled by UI, or another conflict.
|
||||
this._log(`Merge: Cancelled ${filename}`, LOG_LEVEL_INFO);
|
||||
return false;
|
||||
}
|
||||
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(
|
||||
`Concatenated saved, but cannot delete conflicted revisions: ${filename}, (${displayRev(delRev)})`,
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
const testDoc = await this.localDatabase.getDBEntry(filename, { conflicts: true }, false, true, true);
|
||||
if (testDoc === false) {
|
||||
this._log(`Merge: Could not read ${filename} from the local database`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
} else if (typeof toDelete === "string") {
|
||||
// Select one of the conflicted revision to delete.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) == MISSING_OR_ERROR
|
||||
) {
|
||||
if (!testDoc._conflicts) {
|
||||
this._log(`Merge: Nothing to do ${filename}`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
const toDelete = selected;
|
||||
// const toKeep = conflictCheckResult.left.rev != toDelete ? conflictCheckResult.left.rev : conflictCheckResult.right.rev;
|
||||
if (toDelete === LEAVE_TO_SUBSEQUENT) {
|
||||
// Concatenate both conflicted revisions.
|
||||
// Create a new file by concatenating both conflicted revisions.
|
||||
const p = conflictCheckResult.diff.map((e) => e[1]).join("");
|
||||
const delRev = testDoc._conflicts[0];
|
||||
if (!(await this.core.databaseFileAccess.storeContent(filename, p))) {
|
||||
this._log(`Concatenated content cannot be stored:${filename}`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(
|
||||
`Concatenated saved, but cannot delete conflicted revisions: ${filename}, (${displayRev(delRev)})`,
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} else if (typeof toDelete === "string") {
|
||||
// Select one of the conflicted revision to delete.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
// In here, some merge has been processed.
|
||||
// So we have to run replication if configured.
|
||||
// TODO: Make this is as a event request
|
||||
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||
await this.core.$$replicateByEvent();
|
||||
}
|
||||
// And, check it again.
|
||||
await this.core.$$queueConflictCheck(filename);
|
||||
return false;
|
||||
}
|
||||
// In here, some merge has been processed.
|
||||
// So we have to run replication if configured.
|
||||
// TODO: Make this is as a event request
|
||||
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||
await this.core.$$replicateByEvent();
|
||||
}
|
||||
// And, check it again.
|
||||
await this.core.$$queueConflictCheck(filename);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
async allConflictCheck() {
|
||||
while (await this.pickFileForResolve());
|
||||
|
||||
@@ -101,7 +101,6 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
//@ts-ignore
|
||||
newSettings[settingKey] = settingValue;
|
||||
}
|
||||
console.warn(newSettings);
|
||||
await this.applySettingWizard(this.settings, newSettings, "QR Code");
|
||||
}
|
||||
async command_copySetupURI(stripExtra = true) {
|
||||
|
||||
@@ -16,11 +16,9 @@ import {
|
||||
import { delay, isObjectDifferent, sizeToHumanReadable } from "../../../lib/src/common/utils.ts";
|
||||
import { versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||
import {
|
||||
balanceChunkPurgedDBs,
|
||||
checkSyncInfo,
|
||||
purgeUnreferencedChunks,
|
||||
} from "../../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { checkSyncInfo } from "@/lib/src/pouchdb/negotiation.ts";
|
||||
import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks.ts";
|
||||
import { purgeUnreferencedChunks } from "@/lib/src/pouchdb/chunks.ts";
|
||||
import { testCrypt } from "../../../lib/src/encryption/e2ee_v2.ts";
|
||||
import ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||
import { scheduleTask } from "../../../common/utils.ts";
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { type HashAlgorithm, LOG_LEVEL_NOTICE } from "../../../lib/src/common/types.ts";
|
||||
import {
|
||||
E2EEAlgorithmNames,
|
||||
E2EEAlgorithms,
|
||||
type HashAlgorithm,
|
||||
LOG_LEVEL_NOTICE,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
|
||||
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
|
||||
@@ -37,6 +42,19 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
void addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("watchInternalFileChanges", { invert: true });
|
||||
});
|
||||
void addPanel(paneEl, "Compatibility (Remote Database)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireDropDown("E2EEAlgorithm", {
|
||||
options: E2EEAlgorithmNames,
|
||||
});
|
||||
});
|
||||
new Setting(paneEl).autoWireToggle("useDynamicIterationCount", {
|
||||
holdValue: true,
|
||||
onUpdate: visibleOnly(
|
||||
() =>
|
||||
this.isConfiguredAs("E2EEAlgorithm", E2EEAlgorithms.ForceV1) ||
|
||||
this.isConfiguredAs("E2EEAlgorithm", E2EEAlgorithms.V1)
|
||||
),
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Edge case addressing (Database)").then((paneEl) => {
|
||||
new Setting(paneEl)
|
||||
@@ -79,4 +97,16 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
void addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch");
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Remote Database Tweak (In sunset)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("useEden").setClass("wizardHidden");
|
||||
const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true));
|
||||
new Setting(paneEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
|
||||
new Setting(paneEl)
|
||||
.autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden })
|
||||
.setClass("wizardHidden");
|
||||
new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
|
||||
|
||||
new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,24 +2,12 @@ import { type ConfigPassphraseStore } from "../../../lib/src/common/types.ts";
|
||||
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
|
||||
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
|
||||
import type { PageFunctions } from "./SettingPane.ts";
|
||||
import { visibleOnly } from "./SettingPane.ts";
|
||||
|
||||
export function panePowerUsers(
|
||||
this: ObsidianLiveSyncSettingTab,
|
||||
paneEl: HTMLElement,
|
||||
{ addPanel }: PageFunctions
|
||||
): void {
|
||||
void addPanel(paneEl, "Remote Database Tweak").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("useEden").setClass("wizardHidden");
|
||||
const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true));
|
||||
new Setting(paneEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
|
||||
new Setting(paneEl)
|
||||
.autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden })
|
||||
.setClass("wizardHidden");
|
||||
new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden");
|
||||
|
||||
new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden");
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "CouchDB Connection Tweak", undefined, this.onlyOnCouchDB).then((paneEl) => {
|
||||
paneEl.addClass("wizardHidden");
|
||||
|
||||
|
||||
@@ -579,12 +579,6 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
holdValue: true,
|
||||
onUpdate: isEncryptEnabled,
|
||||
});
|
||||
new Setting(paneEl)
|
||||
.autoWireToggle("useDynamicIterationCount", {
|
||||
holdValue: true,
|
||||
onUpdate: isEncryptEnabled,
|
||||
})
|
||||
.setClass("wizardHidden");
|
||||
});
|
||||
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleFetchSettings")).then((paneEl) => {
|
||||
|
||||
@@ -367,7 +367,7 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
||||
},
|
||||
enableDebugTools: {
|
||||
name: "Enable Developers' Debug Tools.",
|
||||
desc: "Requires restart of Obsidian",
|
||||
desc: "While enabled, it causes very performance impact but debugging replication testing and other features will be enabled. Please disable this if you have not read the source code. Requires restart of Obsidian.",
|
||||
},
|
||||
suppressNotifyHiddenFilesChange: {
|
||||
name: "Suppress notification of hidden files change",
|
||||
|
||||
194
updates.md
194
updates.md
@@ -1,5 +1,53 @@
|
||||
## 0.25.0
|
||||
19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
After reading Issue #668, I conducted another self-review of the E2EE-related code. In retrospect, it was clearly written by someone inexperienced, which is understandable, but it is still rather embarrassing. Three years is certainly enough time for growth.
|
||||
|
||||
I have now rewritten the E2EE code to be more robust and easier to understand. It is significantly more readable and should be easier to maintain in the future. The performance issue, previously considered a concern, has been addressed by introducing a master key and deriving keys using HKDF. This approach is both fast and robust, and it provides protection against rainbow table attacks. (In addition, this implementation has been [a dedicated package on the npm registry](https://github.com/vrtmrz/octagonal-wheels), and tested in 100% branch-coverage).
|
||||
|
||||
As a result, this is the first time in a while that forward compatibility has been broken. We have also taken the opportunity to change all metadata to use encryption rather than obfuscation. Furthermore, the `Dynamic Iteration Count` setting is now redundant and has been moved to the `Patches` pane in the settings. Thanks to Rabin-Karp, the eden setting is also no longer necessary and has been relocated accordingly. Therefore, v0.25.0 represents a legitimate and correct evolution.
|
||||
|
||||
### Fixed
|
||||
- The encryption algorithm now uses HKDF with a master key.
|
||||
- This is more robust and faster than the previous implementation.
|
||||
- It is now more secure against rainbow table attacks.
|
||||
- The previous implementation can still be used via `Patches` -> `End-to-end encryption algorithm` -> `Force V1`.
|
||||
- Note that `V1: Legacy` can decrypt V2, but produces V1 output.
|
||||
- `Fetch everything from the remote` now works correctly.
|
||||
- It no longer creates local database entries before synchronisation.
|
||||
- Extra log messages during QR code decoding have been removed.
|
||||
|
||||
### Changed
|
||||
- The following settings have been moved to the `Patches` pane:
|
||||
- `Remote Database Tweak`
|
||||
- `Incubate Chunks in Document`
|
||||
- `Data Compression`
|
||||
|
||||
### Behavioural and API Changes
|
||||
- `DirectFileManipulatorV2` now requires new settings (as you may already know, E2EEAlgorithm).
|
||||
- The database version has been increased to `12` from `10`.
|
||||
- If an older version is detected, we will be notified and synchronisation will be paused until the update is acknowledged. (It has been a long time since this behaviour was last encountered; we always err on the side of caution, even if it is less convenient.)
|
||||
|
||||
### Refactored
|
||||
- `couchdb_utils.ts` has been separated into several explicitly named files.
|
||||
- Some missing functions in `bgWorker.mock.ts` have been added.
|
||||
|
||||
## 0.24.31
|
||||
10th July, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- The description of `Enable Developers' Debug Tools.` has been refined.
|
||||
- Now performance impact is more clearly stated.
|
||||
- Automatic conflict checking and resolution has been improved.
|
||||
- It now works parallelly for each other file, instead of sequentially. It makes significantly faster on first synchronisation when with local files information.
|
||||
- Resolving conflicts dialogue will not be shown for the multiple files at once.
|
||||
- It will be shown for each file, one by one.
|
||||
|
||||
## 0.24.30
|
||||
|
||||
9th July, 2025
|
||||
|
||||
### New Feature
|
||||
|
||||
- New chunking algorithm `V3: Fine deduplication` has been added, and will be recommended after updates.
|
||||
@@ -30,8 +78,14 @@
|
||||
- Never-ending `ObsidianLiveSyncSettingTab.ts` has finally been separated into each pane's file.
|
||||
- Some commented-out code has been removed.
|
||||
|
||||
### Acknowledgement
|
||||
|
||||
- Jun Murakami, Shun Ishiguro, and Yoshihiro Oyama. 2012. Implementation and Evaluation of a Cache Deduplication Mechanism with Content-Defined Chunking. In _IPSJ SIG Technical Report_, Vol.2012-ARC-202, No.4. Information Processing Society of Japan, 1-7.
|
||||
|
||||
## 0.24.29
|
||||
|
||||
20th June, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Synchronisation with buckets now works correctly, regardless of whether a prefix is set or the bucket has been (re-) initialised (#664).
|
||||
@@ -41,145 +95,5 @@
|
||||
|
||||
- Importing paths have been tidied up.
|
||||
|
||||
## 0.24.28
|
||||
|
||||
### Fixed
|
||||
|
||||
- Batch Update is no longer available in LiveSync mode to avoid unexpected behaviour. (#653)
|
||||
- Now compatible with Cloudflare R2 again for bucket synchronisation.
|
||||
- @edo-bari-ikutsu, thank you for [your contribution](https://github.com/vrtmrz/livesync-commonlib/pull/12)!
|
||||
- Prevention of broken behaviour due to database connection failures added (#649).
|
||||
|
||||
## 0.24.27
|
||||
|
||||
### Improved
|
||||
|
||||
- We can use prefix for path for the Bucket synchronisation.
|
||||
- For example, if you set the `vaultName/` as a prefix for the bucket in the root directory, all data will be transferred to the bucket under the `vaultName/` directory.
|
||||
- The "Use Request API to avoid `inevitable` CORS problem" option is now promoted to the normal setting, not a niche patch.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now switching replicators applied immediately, without the need to restart Obsidian.
|
||||
|
||||
### Tidied up
|
||||
|
||||
- Some dependencies have been updated to the latest version.
|
||||
|
||||
## 0.24.26
|
||||
|
||||
This update introduces an option to circumvent Cross-Origin Resource Sharing
|
||||
(CORS) constraints for CouchDB requests, by leveraging Obsidian's native request
|
||||
API. The implementation of such a feature had previously been deferred due to
|
||||
significant security considerations.
|
||||
|
||||
CORS is a vital security mechanism, enabling servers like CouchDB -- which
|
||||
functions as a sophisticated REST API -- to control access from different
|
||||
origins, thereby ensuring secure communication across trust boundaries. I had
|
||||
long hesitated to offer a CORS circumvention method, as it deviates from
|
||||
security best practices; My preference was for users to configure CORS correctly
|
||||
on the server-side.
|
||||
|
||||
However, this policy has shifted due to specific reports of intractable
|
||||
CORS-related configuration issues, particularly within enterprise proxy
|
||||
environments where proxy servers can unpredictably alter or block
|
||||
communications. Given that a primary objective of the "Self-hosted LiveSync"
|
||||
plugin is to facilitate secure Obsidian usage within stringent corporate
|
||||
settings, addressing these 'unavoidable' user-reported problems became
|
||||
essential. Mostly raison d'être of this plugin.
|
||||
|
||||
Consequently, the option "Use Request API to avoid `inevitable` CORS problem"
|
||||
has been implemented. Users are strongly advised to enable this _only_ when
|
||||
operating within a trusted environment. We can enable this option in the `Patch` pane.
|
||||
|
||||
However, just to whisper, this is tremendously fast.
|
||||
|
||||
### New Features
|
||||
|
||||
- Automatic display-language changing according to the Obsidian language
|
||||
setting.
|
||||
- We will be asked on the migration or first startup.
|
||||
- **Note: Please revert to the default language if you report any issues.**
|
||||
- Not all messages are translated yet. We welcome your contribution!
|
||||
- Now we can limit files to be synchronised even in the hidden files.
|
||||
- "Use Request API to avoid `inevitable` CORS problem" has been implemented.
|
||||
- Less secure, please use it only if you are sure that you are in the trusted
|
||||
environment and be able to ignore the CORS. No `Web viewer` or similar tools
|
||||
are recommended. (To avoid the origin forged attack). If you are able to
|
||||
configure the server setting, always that is recommended.
|
||||
- `Show status icon instead of file warnings banner` has been implemented.
|
||||
- If enabled, the ⛔ icon will be shown inside the status instead of the file
|
||||
warnings banner. No details will be shown.
|
||||
|
||||
### Improved
|
||||
|
||||
- All regular expressions can be inverted by prefixing `!!` now.
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer unexpected files will be gathered during hidden file sync.
|
||||
- No longer broken `\n` and new-line characters during the bucket
|
||||
synchronisation.
|
||||
- We can purge the remote bucket again if we using MinIO instead of AWS S3 or
|
||||
Cloudflare R2.
|
||||
- Purging the remote bucket is now more reliable.
|
||||
- 100 files are purged at a time.
|
||||
- Some wrong messages have been fixed.
|
||||
|
||||
### Behaviour changed
|
||||
|
||||
- Entering into the deeper directories to gather the hidden files is now limited
|
||||
by `/` or `\/` prefixed ignore filters. (It means that directories are scanned
|
||||
deeper than before).
|
||||
- However, inside the these directories, the files are still limited by the
|
||||
ignore filters.
|
||||
|
||||
### Etcetera
|
||||
|
||||
- Some code has been tidied up.
|
||||
- Trying less warning-suppressing and be more safer-coding.
|
||||
- Dependent libraries have been updated to the latest version.
|
||||
- Some build processes have been separated to `pre` and `post` processes.
|
||||
|
||||
## 0.24.25
|
||||
|
||||
### Improved
|
||||
|
||||
- Peer-to-peer synchronisation has been got more robust.
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken falsy values in settings during set-up by the QR code
|
||||
generation.
|
||||
|
||||
### Refactored
|
||||
|
||||
- Some `window` references now have pointed to `globalThis`.
|
||||
- Some sloppy-import has been fixed.
|
||||
- A server side implementation `Synchromesh` has been suffixed with `deno`
|
||||
instead of `server` now.
|
||||
|
||||
## 0.24.24
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken JSON files including `\n`, during the bucket synchronisation.
|
||||
(#623)
|
||||
- Custom headers and JWT tokens are now correctly sent to the server during
|
||||
configuration checking. (#624)
|
||||
|
||||
### Improved
|
||||
|
||||
- Bucket synchronisation has been enhanced for better performance and
|
||||
reliability.
|
||||
- Now less duplicated chunks are sent to the server. Note: If you have
|
||||
encountered about too less chunks, please let me know. However, you can send
|
||||
it to the server by `Overwrite remote`.
|
||||
- Fetching conflicted files from the server is now more reliable.
|
||||
- Dependent libraries have been updated to the latest version.
|
||||
- Also, let me know if you have encountered any issues with this update.
|
||||
Especially you are using a device that has been in use for a little
|
||||
longer.
|
||||
|
||||
Older notes are in
|
||||
[updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
||||
|
||||
164
updates_old.md
164
updates_old.md
@@ -14,8 +14,161 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
---
|
||||
|
||||
## 0.24.28
|
||||
|
||||
15th June, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Batch Update is no longer available in LiveSync mode to avoid unexpected behaviour. (#653)
|
||||
- Now compatible with Cloudflare R2 again for bucket synchronisation.
|
||||
- @edo-bari-ikutsu, thank you for [your contribution](https://github.com/vrtmrz/livesync-commonlib/pull/12)!
|
||||
- Prevention of broken behaviour due to database connection failures added (#649).
|
||||
|
||||
## 0.24.27
|
||||
|
||||
10th June, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- We can use prefix for path for the Bucket synchronisation.
|
||||
- For example, if you set the `vaultName/` as a prefix for the bucket in the root directory, all data will be transferred to the bucket under the `vaultName/` directory.
|
||||
- The "Use Request API to avoid `inevitable` CORS problem" option is now promoted to the normal setting, not a niche patch.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now switching replicators applied immediately, without the need to restart Obsidian.
|
||||
|
||||
### Tidied up
|
||||
|
||||
- Some dependencies have been updated to the latest version.
|
||||
|
||||
## 0.24.26
|
||||
|
||||
14th May, 2025
|
||||
|
||||
This update introduces an option to circumvent Cross-Origin Resource Sharing
|
||||
(CORS) constraints for CouchDB requests, by leveraging Obsidian's native request
|
||||
API. The implementation of such a feature had previously been deferred due to
|
||||
significant security considerations.
|
||||
|
||||
CORS is a vital security mechanism, enabling servers like CouchDB -- which
|
||||
functions as a sophisticated REST API -- to control access from different
|
||||
origins, thereby ensuring secure communication across trust boundaries. I had
|
||||
long hesitated to offer a CORS circumvention method, as it deviates from
|
||||
security best practices; My preference was for users to configure CORS correctly
|
||||
on the server-side.
|
||||
|
||||
However, this policy has shifted due to specific reports of intractable
|
||||
CORS-related configuration issues, particularly within enterprise proxy
|
||||
environments where proxy servers can unpredictably alter or block
|
||||
communications. Given that a primary objective of the "Self-hosted LiveSync"
|
||||
plugin is to facilitate secure Obsidian usage within stringent corporate
|
||||
settings, addressing these 'unavoidable' user-reported problems became
|
||||
essential. Mostly raison d'être of this plugin.
|
||||
|
||||
Consequently, the option "Use Request API to avoid `inevitable` CORS problem"
|
||||
has been implemented. Users are strongly advised to enable this _only_ when
|
||||
operating within a trusted environment. We can enable this option in the `Patch` pane.
|
||||
|
||||
However, just to whisper, this is tremendously fast.
|
||||
|
||||
### New Features
|
||||
|
||||
- Automatic display-language changing according to the Obsidian language
|
||||
setting.
|
||||
- We will be asked on the migration or first startup.
|
||||
- **Note: Please revert to the default language if you report any issues.**
|
||||
- Not all messages are translated yet. We welcome your contribution!
|
||||
- Now we can limit files to be synchronised even in the hidden files.
|
||||
- "Use Request API to avoid `inevitable` CORS problem" has been implemented.
|
||||
- Less secure, please use it only if you are sure that you are in the trusted
|
||||
environment and be able to ignore the CORS. No `Web viewer` or similar tools
|
||||
are recommended. (To avoid the origin forged attack). If you are able to
|
||||
configure the server setting, always that is recommended.
|
||||
- `Show status icon instead of file warnings banner` has been implemented.
|
||||
- If enabled, the ⛔ icon will be shown inside the status instead of the file
|
||||
warnings banner. No details will be shown.
|
||||
|
||||
### Improved
|
||||
|
||||
- All regular expressions can be inverted by prefixing `!!` now.
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer unexpected files will be gathered during hidden file sync.
|
||||
- No longer broken `\n` and new-line characters during the bucket
|
||||
synchronisation.
|
||||
- We can purge the remote bucket again if we using MinIO instead of AWS S3 or
|
||||
Cloudflare R2.
|
||||
- Purging the remote bucket is now more reliable.
|
||||
- 100 files are purged at a time.
|
||||
- Some wrong messages have been fixed.
|
||||
|
||||
### Behaviour changed
|
||||
|
||||
- Entering into the deeper directories to gather the hidden files is now limited
|
||||
by `/` or `\/` prefixed ignore filters. (It means that directories are scanned
|
||||
deeper than before).
|
||||
- However, inside the these directories, the files are still limited by the
|
||||
ignore filters.
|
||||
|
||||
### Etcetera
|
||||
|
||||
- Some code has been tidied up.
|
||||
- Trying less warning-suppressing and be more safer-coding.
|
||||
- Dependent libraries have been updated to the latest version.
|
||||
- Some build processes have been separated to `pre` and `post` processes.
|
||||
|
||||
|
||||
## 0.24.25
|
||||
|
||||
22nd April, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Peer-to-peer synchronisation has been got more robust.
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken falsy values in settings during set-up by the QR code
|
||||
generation.
|
||||
|
||||
### Refactored
|
||||
|
||||
- Some `window` references now have pointed to `globalThis`.
|
||||
- Some sloppy-import has been fixed.
|
||||
- A server side implementation `Synchromesh` has been suffixed with `deno`
|
||||
instead of `server` now.
|
||||
|
||||
## 0.24.24
|
||||
|
||||
15th April, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken JSON files including `\n`, during the bucket synchronisation.
|
||||
(#623)
|
||||
- Custom headers and JWT tokens are now correctly sent to the server during
|
||||
configuration checking. (#624)
|
||||
|
||||
### Improved
|
||||
|
||||
- Bucket synchronisation has been enhanced for better performance and
|
||||
reliability.
|
||||
- Now less duplicated chunks are sent to the server. Note: If you have
|
||||
encountered about too less chunks, please let me know. However, you can send
|
||||
it to the server by `Overwrite remote`.
|
||||
- Fetching conflicted files from the server is now more reliable.
|
||||
- Dependent libraries have been updated to the latest version.
|
||||
- Also, let me know if you have encountered any issues with this update.
|
||||
Especially you are using a device that has been in use for a little
|
||||
longer.
|
||||
|
||||
## 0.24.23
|
||||
|
||||
10th April, 2025
|
||||
|
||||
### New Feature
|
||||
|
||||
- Now, we can send custom headers to the server.
|
||||
@@ -37,6 +190,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.22 ~~0.24.21~~
|
||||
|
||||
1st April, 2025
|
||||
|
||||
(Really sorry for the confusion. I have got a miss at releasing...).
|
||||
|
||||
### Fixed
|
||||
@@ -65,6 +220,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.20
|
||||
|
||||
24th March, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Now we can see the detail of `TypeError` using Obsidian API during remote
|
||||
@@ -79,6 +236,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.19
|
||||
|
||||
5th March, 2025
|
||||
|
||||
### New Feature
|
||||
|
||||
- Now we can generate a QR Code for transferring the configuration to another device.
|
||||
@@ -87,6 +246,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.18
|
||||
|
||||
28th February, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now no chunk creation errors will be raised after switching `Compute revisions for chunks`.
|
||||
@@ -106,10 +267,13 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.17
|
||||
|
||||
27th February, 2025
|
||||
|
||||
Confession. I got the default values wrong. So scary and sorry.
|
||||
|
||||
## 0.24.16
|
||||
|
||||
|
||||
### Improved
|
||||
|
||||
#### Peer-to-Peer
|
||||
|
||||
Reference in New Issue
Block a user