mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-11 02:01:52 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dca8e4b2a4 | ||
|
|
89de2dcc37 | ||
|
|
172b08dbb3 | ||
|
|
d518a3fc1b | ||
|
|
c6ed867498 | ||
|
|
4f4923e977 | ||
|
|
a5ebf29b3d | ||
|
|
cbf5023593 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.23.4",
|
"version": "0.23.7",
|
||||||
"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.23.3",
|
"version": "0.23.7",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.23.3",
|
"version": "0.23.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.556.0",
|
"@aws-sdk/client-s3": "^3.556.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.23.4",
|
"version": "0.23.7",
|
||||||
"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",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { readString, decodeBinary, arrayBufferToBase64, digestHash } from "../li
|
|||||||
import { serialized } from "../lib/src/concurrency/lock.ts";
|
import { serialized } from "../lib/src/concurrency/lock.ts";
|
||||||
import { LiveSyncCommands } from "./LiveSyncCommands.ts";
|
import { LiveSyncCommands } from "./LiveSyncCommands.ts";
|
||||||
import { stripAllPrefixes } from "../lib/src/string_and_binary/path.ts";
|
import { stripAllPrefixes } from "../lib/src/string_and_binary/path.ts";
|
||||||
import { PeriodicProcessor, askYesNo, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "../common/utils.ts";
|
import { PeriodicProcessor, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "../common/utils.ts";
|
||||||
import { PluginDialogModal } from "../common/dialogs.ts";
|
import { PluginDialogModal } from "../common/dialogs.ts";
|
||||||
import { JsonResolveModal } from "../ui/JsonResolveModal.ts";
|
import { JsonResolveModal } from "../ui/JsonResolveModal.ts";
|
||||||
import { QueueProcessor } from '../lib/src/concurrency/processor.ts';
|
import { QueueProcessor } from '../lib/src/concurrency/processor.ts';
|
||||||
@@ -466,12 +466,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
Logger(`Plugin reloaded: ${pluginManifest.name}`, LOG_LEVEL_NOTICE, "plugin-reload-" + pluginManifest.id);
|
Logger(`Plugin reloaded: ${pluginManifest.name}`, LOG_LEVEL_NOTICE, "plugin-reload-" + pluginManifest.id);
|
||||||
}
|
}
|
||||||
} else if (data.category == "CONFIG") {
|
} else if (data.category == "CONFIG") {
|
||||||
scheduleTask("configReload", 250, async () => {
|
this.plugin.askReload();
|
||||||
if (await askYesNo(this.app, "Do you want to restart and reload Obsidian now?") == "yes") {
|
|
||||||
// @ts-ignore
|
|
||||||
this.app.commands.executeCommandById("app:reload")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@@ -684,6 +679,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
children: [],
|
children: [],
|
||||||
deleted: false,
|
deleted: false,
|
||||||
type: "newnote",
|
type: "newnote",
|
||||||
|
eden: {}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (old.mtime == mtime) {
|
if (old.mtime == mtime) {
|
||||||
|
|||||||
@@ -432,13 +432,14 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
|
|
||||||
// If something changes left, notify for reloading Obsidian.
|
// If something changes left, notify for reloading Obsidian.
|
||||||
if (updatedCount != 0) {
|
if (updatedCount != 0) {
|
||||||
this.plugin.askInPopup(`updated-any-hidden`, `Hidden files have been synchronized, Press {HERE} to reload Obsidian, or press elsewhere to dismiss this message.`, (anchor) => {
|
if (!this.plugin.isReloadingScheduled) {
|
||||||
anchor.text = "HERE";
|
this.plugin.askInPopup(`updated-any-hidden`, `Hidden files have been synchronised, Press {HERE} to schedule a reload of Obsidian, or press elsewhere to dismiss this message.`, (anchor) => {
|
||||||
anchor.addEventListener("click", () => {
|
anchor.text = "HERE";
|
||||||
// @ts-ignore
|
anchor.addEventListener("click", () => {
|
||||||
this.app.commands.executeCommandById("app:reload");
|
this.plugin.scheduleAppReload();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,6 +472,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
children: [],
|
children: [],
|
||||||
deleted: false,
|
deleted: false,
|
||||||
type: "newnote",
|
type: "newnote",
|
||||||
|
eden: {},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (await isDocContentSame(readAsBlob(old), content) && !forceWrite) {
|
if (await isDocContentSame(readAsBlob(old), content) && !forceWrite) {
|
||||||
@@ -521,6 +523,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
children: [],
|
children: [],
|
||||||
deleted: true,
|
deleted: true,
|
||||||
type: "newnote",
|
type: "newnote",
|
||||||
|
eden: {}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Remove all conflicted before deleting.
|
// Remove all conflicted before deleting.
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: 1417452fec...13f8370ef5
288
src/main.ts
288
src/main.ts
@@ -2,9 +2,9 @@ const isDebug = false;
|
|||||||
|
|
||||||
import { type Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch, stringifyYaml, parseYaml } from "./deps";
|
import { type Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch, stringifyYaml, parseYaml } from "./deps";
|
||||||
import { Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, type RequestUrlParam, type RequestUrlResponse, requestUrl, type MarkdownFileInfo } from "./deps";
|
import { Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, type RequestUrlParam, type RequestUrlResponse, requestUrl, type MarkdownFileInfo } from "./deps";
|
||||||
import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type diff_check_result, type diff_result_leaf, type EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, type ConfigPassphraseStore, type CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, type AnyEntry, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT, LOG_LEVEL_VERBOSE, type SavingEntry, MISSING_OR_ERROR, NOT_CONFLICTED, AUTO_MERGED, CANCELLED, LEAVE_TO_SUBSEQUENT, FLAGMD_REDFLAG2_HR, FLAGMD_REDFLAG3_HR, REMOTE_MINIO, REMOTE_COUCHDB, type BucketSyncSetting, } from "./lib/src/common/types.ts";
|
import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type diff_check_result, type diff_result_leaf, type EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, type ConfigPassphraseStore, type CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, type AnyEntry, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT, LOG_LEVEL_VERBOSE, type SavingEntry, MISSING_OR_ERROR, NOT_CONFLICTED, AUTO_MERGED, CANCELLED, LEAVE_TO_SUBSEQUENT, FLAGMD_REDFLAG2_HR, FLAGMD_REDFLAG3_HR, REMOTE_MINIO, REMOTE_COUCHDB, type BucketSyncSetting, TweakValuesShouldMatchedTemplate, confName, type TweakValues, } from "./lib/src/common/types.ts";
|
||||||
import { type InternalFileInfo, type CacheData, type FileEventItem, FileWatchEventQueueMax } from "./common/types.ts";
|
import { type InternalFileInfo, type CacheData, type FileEventItem, FileWatchEventQueueMax } from "./common/types.ts";
|
||||||
import { arrayToChunkedArray, createBlob, delay, determineTypeFromBlob, fireAndForget, getDocData, isAnyNote, isDocContentSame, isObjectDifferent, readContent, sendValue, throttle, type SimpleStore } from "./lib/src/common/utils.ts";
|
import { arrayToChunkedArray, createBlob, delay, determineTypeFromBlob, escapeMarkdownValue, extractObject, fireAndForget, getDocData, isAnyNote, isDocContentSame, isObjectDifferent, readContent, sendValue, throttle, type SimpleStore } from "./lib/src/common/utils.ts";
|
||||||
import { Logger, setGlobalLogFunction } from "./lib/src/common/logger.ts";
|
import { Logger, setGlobalLogFunction } from "./lib/src/common/logger.ts";
|
||||||
import { PouchDB } from "./lib/src/pouchdb/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb/pouchdb-browser.js";
|
||||||
import { ConflictResolveModal } from "./ui/ConflictResolveModal.ts";
|
import { ConflictResolveModal } from "./ui/ConflictResolveModal.ts";
|
||||||
@@ -32,7 +32,7 @@ import { LogPaneView, VIEW_TYPE_LOG } from "./ui/LogPaneView.ts";
|
|||||||
import { LRUCache } from "./lib/src/memory/LRUCache.ts";
|
import { LRUCache } from "./lib/src/memory/LRUCache.ts";
|
||||||
import { SerializedFileAccess } from "./storages/SerializedFileAccess.js";
|
import { SerializedFileAccess } from "./storages/SerializedFileAccess.js";
|
||||||
import { QueueProcessor } from "./lib/src/concurrency/processor.js";
|
import { QueueProcessor } from "./lib/src/concurrency/processor.js";
|
||||||
import { reactive, reactiveSource } from "./lib/src/dataobject/reactive.js";
|
import { reactive, reactiveSource, type ReactiveValue } from "./lib/src/dataobject/reactive.js";
|
||||||
import { initializeStores } from "./common/stores.js";
|
import { initializeStores } from "./common/stores.js";
|
||||||
import { JournalSyncMinio } from "./lib/src/replication/journal/objectstore/JournalSyncMinio.js";
|
import { JournalSyncMinio } from "./lib/src/replication/journal/objectstore/JournalSyncMinio.js";
|
||||||
import { LiveSyncJournalReplicator, type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
import { LiveSyncJournalReplicator, type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
||||||
@@ -1374,6 +1374,7 @@ We can perform a command in this file.
|
|||||||
} else {
|
} else {
|
||||||
// suspend all temporary.
|
// suspend all temporary.
|
||||||
if (this.suspended) return;
|
if (this.suspended) return;
|
||||||
|
if (!this.hasFocus) return;
|
||||||
await Promise.all(this.addOns.map(e => e.onResume()));
|
await Promise.all(this.addOns.map(e => e.onResume()));
|
||||||
if (this.settings.remoteType == REMOTE_COUCHDB) {
|
if (this.settings.remoteType == REMOTE_COUCHDB) {
|
||||||
if (this.settings.liveSync) {
|
if (this.settings.liveSync) {
|
||||||
@@ -1421,55 +1422,62 @@ We can perform a command in this file.
|
|||||||
}
|
}
|
||||||
async handleFileEvent(queue: FileEventItem): Promise<any> {
|
async handleFileEvent(queue: FileEventItem): Promise<any> {
|
||||||
const file = queue.args.file;
|
const file = queue.args.file;
|
||||||
const key = `file-last-proc-${queue.type}-${file.path}`;
|
const lockKey = `handleFile:${file.path}`;
|
||||||
const last = Number(await this.kvDB.get(key) || 0);
|
return await serialized(lockKey, async () => {
|
||||||
let mtime = file.mtime;
|
const key = `file-last-proc-${queue.type}-${file.path}`;
|
||||||
if (queue.type == "DELETE") {
|
const last = Number(await this.kvDB.get(key) || 0);
|
||||||
await this.deleteFromDBbyPath(file.path);
|
let mtime = file.mtime;
|
||||||
mtime = file.mtime - 1;
|
if (queue.type == "DELETE") {
|
||||||
const keyD1 = `file-last-proc-CREATE-${file.path}`;
|
await this.deleteFromDBbyPath(file.path);
|
||||||
const keyD2 = `file-last-proc-CHANGED-${file.path}`;
|
mtime = file.mtime - 1;
|
||||||
await this.kvDB.set(keyD1, mtime);
|
const keyD1 = `file-last-proc-CREATE-${file.path}`;
|
||||||
await this.kvDB.set(keyD2, mtime);
|
const keyD2 = `file-last-proc-CHANGED-${file.path}`;
|
||||||
} else if (queue.type == "INTERNAL") {
|
|
||||||
await this.addOnHiddenFileSync.watchVaultRawEventsAsync(file.path);
|
|
||||||
await this.addOnConfigSync.watchVaultRawEventsAsync(file.path);
|
|
||||||
} else {
|
|
||||||
const targetFile = this.vaultAccess.getAbstractFileByPath(file.path);
|
|
||||||
if (!(targetFile instanceof TFile)) {
|
|
||||||
Logger(`Target file was not found: ${file.path}`, LOG_LEVEL_INFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (file.mtime == last) {
|
|
||||||
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const cache = queue.args.cache;
|
|
||||||
if (queue.type == "CREATE" || queue.type == "CHANGED") {
|
|
||||||
fireAndForget(() => this.checkAndApplySettingFromMarkdown(queue.args.file.path, true));
|
|
||||||
const keyD1 = `file-last-proc-DELETED-${file.path}`;
|
|
||||||
await this.kvDB.set(keyD1, mtime);
|
await this.kvDB.set(keyD1, mtime);
|
||||||
if (!await this.updateIntoDB(targetFile, undefined)) {
|
await this.kvDB.set(keyD2, mtime);
|
||||||
Logger(`STORAGE -> DB: failed, cancel the relative operations: ${targetFile.path}`, LOG_LEVEL_INFO);
|
} else if (queue.type == "INTERNAL") {
|
||||||
// cancel running queues and remove one of atomic operation
|
await this.addOnHiddenFileSync.watchVaultRawEventsAsync(file.path);
|
||||||
this.cancelRelativeEvent(queue);
|
await this.addOnConfigSync.watchVaultRawEventsAsync(file.path);
|
||||||
|
} else {
|
||||||
|
const targetFile = this.vaultAccess.getAbstractFileByPath(file.path);
|
||||||
|
if (!(targetFile instanceof TFile)) {
|
||||||
|
Logger(`Target file was not found: ${file.path}`, LOG_LEVEL_INFO);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (file.mtime == last) {
|
||||||
|
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const cache = queue.args.cache;
|
||||||
|
if (queue.type == "CREATE" || queue.type == "CHANGED") {
|
||||||
|
fireAndForget(() => this.checkAndApplySettingFromMarkdown(queue.args.file.path, true));
|
||||||
|
const keyD1 = `file-last-proc-DELETED-${file.path}`;
|
||||||
|
await this.kvDB.set(keyD1, mtime);
|
||||||
|
if (!await this.updateIntoDB(targetFile, undefined)) {
|
||||||
|
Logger(`STORAGE -> DB: failed, cancel the relative operations: ${targetFile.path}`, LOG_LEVEL_INFO);
|
||||||
|
// cancel running queues and remove one of atomic operation
|
||||||
|
this.cancelRelativeEvent(queue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queue.type == "RENAME") {
|
||||||
|
// Obsolete
|
||||||
|
await this.watchVaultRenameAsync(targetFile, queue.args.oldPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (queue.type == "RENAME") {
|
await this.kvDB.set(key, mtime);
|
||||||
// Obsolete
|
});
|
||||||
await this.watchVaultRenameAsync(targetFile, queue.args.oldPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this.kvDB.set(key, mtime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingFileEventCount = reactiveSource(0);
|
pendingFileEventCount = reactiveSource(0);
|
||||||
processingFileEventCount = reactiveSource(0);
|
processingFileEventCount = reactiveSource(0);
|
||||||
fileEventQueue =
|
fileEventQueue =
|
||||||
new QueueProcessor(
|
new QueueProcessor(
|
||||||
(items: FileEventItem[]) => this.handleFileEvent(items[0]),
|
async (items: FileEventItem[]) => {
|
||||||
|
await this.handleFileEvent(items[0]);
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
,
|
||||||
{ suspended: true, batchSize: 1, concurrentLimit: 5, delay: 100, yieldThreshold: FileWatchEventQueueMax, totalRemainingReactiveSource: this.pendingFileEventCount, processingEntitiesReactiveSource: this.processingFileEventCount }
|
{ suspended: true, batchSize: 1, concurrentLimit: 5, delay: 100, yieldThreshold: FileWatchEventQueueMax, totalRemainingReactiveSource: this.pendingFileEventCount, processingEntitiesReactiveSource: this.processingFileEventCount }
|
||||||
).replaceEnqueueProcessor((items, newItem) => this.queueNextFileEvent(items, newItem));
|
).replaceEnqueueProcessor((items, newItem) => this.queueNextFileEvent(items, newItem));
|
||||||
|
|
||||||
@@ -1893,7 +1901,7 @@ We can perform a command in this file.
|
|||||||
observeForLogs() {
|
observeForLogs() {
|
||||||
const padSpaces = `\u{2007}`.repeat(10);
|
const padSpaces = `\u{2007}`.repeat(10);
|
||||||
// const emptyMark = `\u{2003}`;
|
// const emptyMark = `\u{2003}`;
|
||||||
const rerenderTimer = new Map<string, [ReturnType<typeof setTimeout>, number]>;
|
const rerenderTimer = new Map<string, [ReturnType<typeof setTimeout>, number]>();
|
||||||
const tick = reactiveSource(0);
|
const tick = reactiveSource(0);
|
||||||
function padLeftSp(num: number, mark: string) {
|
function padLeftSp(num: number, mark: string) {
|
||||||
const numLen = `${num}`.length + 1;
|
const numLen = `${num}`.length + 1;
|
||||||
@@ -2004,9 +2012,9 @@ We can perform a command in this file.
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
const statusBarLabels = reactive(() => {
|
const statusBarLabels = reactive(() => {
|
||||||
|
const scheduleMessage = this.isReloadingScheduled ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : "";
|
||||||
const { message } = statusLineLabel.value;
|
const { message } = statusLineLabel.value;
|
||||||
const status = this.statusLog.value;
|
const status = scheduleMessage + this.statusLog.value;
|
||||||
return {
|
return {
|
||||||
message, status
|
message, status
|
||||||
}
|
}
|
||||||
@@ -2052,58 +2060,114 @@ We can perform a command in this file.
|
|||||||
await this.loadQueuedFiles();
|
await this.loadQueuedFiles();
|
||||||
const ret = await this.replicator.openReplication(this.settings, false, showMessage, false);
|
const ret = await this.replicator.openReplication(this.settings, false, showMessage, false);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
if (this.replicator.remoteLockedAndDeviceNotAccepted) {
|
if (this.replicator.tweakSettingsMismatched) {
|
||||||
if (this.replicator.remoteCleaned && this.settings.useIndexedDBAdapter) {
|
const remoteSettings = this.replicator.mismatchedTweakValues;
|
||||||
Logger(`The remote database has been cleaned.`, showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
|
const mustSettings = remoteSettings.map(e => extractObject(TweakValuesShouldMatchedTemplate, e));
|
||||||
await skipIfDuplicated("cleanup", async () => {
|
const items = Object.entries(TweakValuesShouldMatchedTemplate);
|
||||||
const count = await purgeUnreferencedChunks(this.localDatabase.localDatabase, true);
|
// Making tables:
|
||||||
const message = `The remote database has been cleaned up.
|
let table = `| Value name | Ours | ${mustSettings.map((_, i) => `Remote ${i + 1} |`).join("")}\n` +
|
||||||
|
`|: --- |: --- :${`|: --- :`.repeat(mustSettings.length)}|\n`
|
||||||
|
for (const v of items) {
|
||||||
|
const key = v[0] as keyof typeof TweakValuesShouldMatchedTemplate;
|
||||||
|
const value = mustSettings.map(e => e[key]);
|
||||||
|
table += `| ${confName(key)} | ${escapeMarkdownValue(this.settings[key])} | ${value.map((v) => `${escapeMarkdownValue(v)} |`).join("")}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = `
|
||||||
|
Configuration mismatching between the clients has been detected.
|
||||||
|
This can be harmful or extra capacity consumption. We have to make these value unified.
|
||||||
|
|
||||||
|
Configured values:
|
||||||
|
|
||||||
|
${table}
|
||||||
|
|
||||||
|
Please select a unification method.
|
||||||
|
|
||||||
|
However, even if we answer that you will \`Use mine\`, we will be prompted to accept it again on the other device and have to decide accept or not.`;
|
||||||
|
|
||||||
|
//TODO: apply this settings.
|
||||||
|
const CHOICE_USE_REMOTE = "Use Remote ";
|
||||||
|
const CHOICE_USR_MINE = "Use ours";
|
||||||
|
const CHOICE_DISMISS = "Dismiss";
|
||||||
|
// const ourConfig = extractObject(TweakValuesShouldMatchedTemplate, this.settings);
|
||||||
|
const CHOICE_AND_VALUES = [
|
||||||
|
...mustSettings.map((e, i) => [`${CHOICE_USE_REMOTE} ${i + 1}`, e]),
|
||||||
|
[CHOICE_USR_MINE, true],
|
||||||
|
[CHOICE_DISMISS, false]
|
||||||
|
]
|
||||||
|
const CHOICES = Object.fromEntries(CHOICE_AND_VALUES) as Record<string, TweakValues | boolean>;
|
||||||
|
const retKey = await confirmWithMessage(this, "Locked", message, Object.keys(CHOICES), CHOICE_DISMISS, 60);
|
||||||
|
if (!retKey) return;
|
||||||
|
const conf = CHOICES[retKey];
|
||||||
|
if (!conf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (conf === true) {
|
||||||
|
await this.replicator.resetRemoteTweakSettings(this.settings);
|
||||||
|
Logger(`Tweak values on the remote server have been cleared, and will be overwritten in next synchronisation.`, LOG_LEVEL_NOTICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (conf) {
|
||||||
|
this.settings = { ...this.settings, ...conf };
|
||||||
|
await this.saveSettingData();
|
||||||
|
Logger(`Tweak Values have been overwritten by the chosen one.`, LOG_LEVEL_NOTICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (this.replicator.remoteLockedAndDeviceNotAccepted) {
|
||||||
|
if (this.replicator.remoteCleaned && this.settings.useIndexedDBAdapter) {
|
||||||
|
Logger(`The remote database has been cleaned.`, showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
|
||||||
|
await skipIfDuplicated("cleanup", async () => {
|
||||||
|
const count = await purgeUnreferencedChunks(this.localDatabase.localDatabase, true);
|
||||||
|
const message = `The remote database has been cleaned up.
|
||||||
To synchronize, this device must be also cleaned up. ${count} chunk(s) will be erased from this device.
|
To synchronize, this device must be also cleaned up. ${count} chunk(s) will be erased from this device.
|
||||||
However, If there are many chunks to be deleted, maybe fetching again is faster.
|
However, If there are many chunks to be deleted, maybe fetching again is faster.
|
||||||
We will lose the history of this device if we fetch the remote database again.
|
We will lose the history of this device if we fetch the remote database again.
|
||||||
Even if you choose to clean up, you will see this option again if you exit Obsidian and then synchronise again.`
|
Even if you choose to clean up, you will see this option again if you exit Obsidian and then synchronise again.`
|
||||||
const CHOICE_FETCH = "Fetch again";
|
const CHOICE_FETCH = "Fetch again";
|
||||||
const CHOICE_CLEAN = "Cleanup";
|
const CHOICE_CLEAN = "Cleanup";
|
||||||
const CHOICE_DISMISS = "Dismiss";
|
const CHOICE_DISMISS = "Dismiss";
|
||||||
const ret = await confirmWithMessage(this, "Cleaned", message, [CHOICE_FETCH, CHOICE_CLEAN, CHOICE_DISMISS], CHOICE_DISMISS, 30);
|
const ret = await confirmWithMessage(this, "Cleaned", message, [CHOICE_FETCH, CHOICE_CLEAN, CHOICE_DISMISS], CHOICE_DISMISS, 30);
|
||||||
if (ret == CHOICE_FETCH) {
|
if (ret == CHOICE_FETCH) {
|
||||||
await performRebuildDB(this, "localOnly");
|
await performRebuildDB(this, "localOnly");
|
||||||
}
|
|
||||||
if (ret == CHOICE_CLEAN) {
|
|
||||||
const replicator = this.getReplicator();
|
|
||||||
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
|
||||||
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(this.settings, this.getIsMobile(), true);
|
|
||||||
if (typeof remoteDB == "string") {
|
|
||||||
Logger(remoteDB, LOG_LEVEL_NOTICE);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
if (ret == CHOICE_CLEAN) {
|
||||||
|
const replicator = this.getReplicator();
|
||||||
|
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
||||||
|
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(this.settings, this.getIsMobile(), true);
|
||||||
|
if (typeof remoteDB == "string") {
|
||||||
|
Logger(remoteDB, LOG_LEVEL_NOTICE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
|
|
||||||
this.localDatabase.hashCaches.clear();
|
|
||||||
// Perform the synchronisation once.
|
|
||||||
if (await this.replicator.openReplication(this.settings, false, showMessage, true)) {
|
|
||||||
await balanceChunkPurgedDBs(this.localDatabase.localDatabase, remoteDB.db);
|
|
||||||
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
|
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
|
||||||
this.localDatabase.hashCaches.clear();
|
this.localDatabase.hashCaches.clear();
|
||||||
await this.getReplicator().markRemoteResolved(this.settings);
|
// Perform the synchronisation once.
|
||||||
Logger("The local database has been cleaned up.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
if (await this.replicator.openReplication(this.settings, false, showMessage, true)) {
|
||||||
} else {
|
await balanceChunkPurgedDBs(this.localDatabase.localDatabase, remoteDB.db);
|
||||||
Logger("Replication has been cancelled. Please try it again.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
|
||||||
}
|
this.localDatabase.hashCaches.clear();
|
||||||
|
await this.getReplicator().markRemoteResolved(this.settings);
|
||||||
|
Logger("The local database has been cleaned up.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
||||||
|
} else {
|
||||||
|
Logger("Replication has been cancelled. Please try it again.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const message = `
|
const message = `
|
||||||
The remote database has been rebuilt.
|
The remote database has been rebuilt.
|
||||||
To synchronize, this device must fetch everything again once.
|
To synchronize, this device must fetch everything again once.
|
||||||
Or if you are sure know what had been happened, we can unlock the database from the setting dialog.
|
Or if you are sure know what had been happened, we can unlock the database from the setting dialog.
|
||||||
`
|
`
|
||||||
const CHOICE_FETCH = "Fetch again";
|
const CHOICE_FETCH = "Fetch again";
|
||||||
const CHOICE_DISMISS = "Dismiss";
|
const CHOICE_DISMISS = "Dismiss";
|
||||||
const ret = await confirmWithMessage(this, "Locked", message, [CHOICE_FETCH, CHOICE_DISMISS], CHOICE_DISMISS, 10);
|
const ret = await confirmWithMessage(this, "Locked", message, [CHOICE_FETCH, CHOICE_DISMISS], CHOICE_DISMISS, 10);
|
||||||
if (ret == CHOICE_FETCH) {
|
if (ret == CHOICE_FETCH) {
|
||||||
await performRebuildDB(this, "localOnly");
|
await performRebuildDB(this, "localOnly");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3117,5 +3181,61 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.app.commands.executeCommandById(id)
|
this.app.commands.executeCommandById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_totalProcessingCount?: ReactiveValue<number>;
|
||||||
|
get isReloadingScheduled() {
|
||||||
|
return this._totalProcessingCount !== undefined;
|
||||||
|
}
|
||||||
|
askReload(message?: string) {
|
||||||
|
if (this.isReloadingScheduled) {
|
||||||
|
Logger(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduleTask("configReload", 250, async () => {
|
||||||
|
const RESTART_NOW = "Yes, restart immediately";
|
||||||
|
const RESTART_AFTER_STABLE = "Yes, schedule a restart after stabilisation";
|
||||||
|
const RETRY_LATER = "No, Leave it to me";
|
||||||
|
const ret = await askSelectString(this.app, message || "Do you want to restart and reload Obsidian now?", [RESTART_AFTER_STABLE, RESTART_NOW, RETRY_LATER]);
|
||||||
|
if (ret == RESTART_NOW) {
|
||||||
|
this.performAppReload();
|
||||||
|
} else if (ret == RESTART_AFTER_STABLE) {
|
||||||
|
this.scheduleAppReload();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
scheduleAppReload() {
|
||||||
|
if (!this._totalProcessingCount) {
|
||||||
|
const __tick = reactiveSource(0);
|
||||||
|
this._totalProcessingCount = reactive(() => {
|
||||||
|
const dbCount = this.databaseQueueCount.value;
|
||||||
|
const replicationCount = this.replicationResultCount.value;
|
||||||
|
const storageApplyingCount = this.storageApplyingCount.value;
|
||||||
|
const chunkCount = collectingChunks.value;
|
||||||
|
const pluginScanCount = pluginScanningCount.value;
|
||||||
|
const hiddenFilesCount = hiddenFilesEventCount.value + hiddenFilesProcessingCount.value;
|
||||||
|
const conflictProcessCount = this.conflictProcessQueueCount.value;
|
||||||
|
const e = this.pendingFileEventCount.value;
|
||||||
|
const proc = this.processingFileEventCount.value;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const __ = __tick.value;
|
||||||
|
return dbCount + replicationCount + storageApplyingCount + chunkCount + pluginScanCount + hiddenFilesCount + conflictProcessCount + e + proc;
|
||||||
|
})
|
||||||
|
this.registerInterval(setInterval(() => {
|
||||||
|
__tick.value++;
|
||||||
|
}, 1000) as unknown as number);
|
||||||
|
|
||||||
|
let stableCheck = 3;
|
||||||
|
this._totalProcessingCount.onChanged(e => {
|
||||||
|
if (e.value == 0) {
|
||||||
|
if (stableCheck-- <= 0) {
|
||||||
|
this.performAppReload();
|
||||||
|
}
|
||||||
|
Logger(`Obsidian will be restarted soon! (Within ${stableCheck} seconds)`, LOG_LEVEL_NOTICE, "restart-notice");
|
||||||
|
} else {
|
||||||
|
stableCheck = 3;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ import {
|
|||||||
REMOTE_MINIO,
|
REMOTE_MINIO,
|
||||||
type BucketSyncSetting,
|
type BucketSyncSetting,
|
||||||
type RemoteType,
|
type RemoteType,
|
||||||
PREFERRED_JOURNAL_SYNC
|
PREFERRED_JOURNAL_SYNC,
|
||||||
|
confName
|
||||||
} from "../lib/src/common/types.ts";
|
} from "../lib/src/common/types.ts";
|
||||||
import { createBlob, delay, extractObject, isDocContentSame, readAsBlob } from "../lib/src/common/utils.ts";
|
import { createBlob, delay, extractObject, isDocContentSame, readAsBlob } from "../lib/src/common/utils.ts";
|
||||||
import { versionNumberString2Number } from "../lib/src/string_and_binary/strbin.ts";
|
import { versionNumberString2Number } from "../lib/src/string_and_binary/strbin.ts";
|
||||||
@@ -27,7 +28,7 @@ import { Logger } from "../lib/src/common/logger.ts";
|
|||||||
import { checkSyncInfo, isCloudantURI } from "../lib/src/pouchdb/utils_couchdb.ts";
|
import { checkSyncInfo, isCloudantURI } from "../lib/src/pouchdb/utils_couchdb.ts";
|
||||||
import { testCrypt } from "../lib/src/encryption/e2ee_v2.ts";
|
import { testCrypt } from "../lib/src/encryption/e2ee_v2.ts";
|
||||||
import ObsidianLiveSyncPlugin from "../main.ts";
|
import ObsidianLiveSyncPlugin from "../main.ts";
|
||||||
import { askYesNo, performRebuildDB, requestToCouchDB, scheduleTask } from "../common/utils.ts";
|
import { askYesNo, performRebuildDB, requestToCouchDB } from "../common/utils.ts";
|
||||||
import { request, type ButtonComponent, TFile } from "obsidian";
|
import { request, type ButtonComponent, TFile } from "obsidian";
|
||||||
import { shouldBeIgnored } from "../lib/src/string_and_binary/path.ts";
|
import { shouldBeIgnored } from "../lib/src/string_and_binary/path.ts";
|
||||||
import MultipleRegExpControl from './components/MultipleRegExpControl.svelte';
|
import MultipleRegExpControl from './components/MultipleRegExpControl.svelte';
|
||||||
@@ -50,15 +51,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await replicator.tryConnectRemote(trialSetting);
|
await replicator.tryConnectRemote(trialSetting);
|
||||||
}
|
}
|
||||||
|
|
||||||
askReload(message?: string) {
|
|
||||||
scheduleTask("configReload", 250, async () => {
|
|
||||||
if (await askYesNo(this.app, message || "Do you want to restart and reload Obsidian now?") == "yes") {
|
|
||||||
// @ts-ignore
|
|
||||||
this.app.commands.executeCommandById("app:reload")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSetting() {
|
closeSetting() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.plugin.app.setting.close()
|
this.plugin.app.setting.close()
|
||||||
@@ -137,7 +129,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
const tmpDiv = createSpan();
|
const tmpDiv = createSpan();
|
||||||
tmpDiv.addClass("sls-header-button");
|
tmpDiv.addClass("sls-header-button");
|
||||||
tmpDiv.innerHTML = `<button> OK, I read all. </button>`;
|
tmpDiv.innerHTML = `<button> OK, I read everything. </button>`;
|
||||||
if (lastVersion > this.plugin.settings.lastReadUpdates) {
|
if (lastVersion > this.plugin.settings.lastReadUpdates) {
|
||||||
const informationButtonDiv = h3El.appendChild(tmpDiv);
|
const informationButtonDiv = h3El.appendChild(tmpDiv);
|
||||||
informationButtonDiv.querySelector("button")?.addEventListener("click", async () => {
|
informationButtonDiv.querySelector("button")?.addEventListener("click", async () => {
|
||||||
@@ -211,26 +203,26 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
if (!this.plugin.settings.isConfigured) {
|
if (!this.plugin.settings.isConfigured) {
|
||||||
new Setting(setupWizardEl)
|
new Setting(setupWizardEl)
|
||||||
.setName("Enable LiveSync on this device as the set-up was completed manually")
|
.setName("Enable LiveSync on this device as the setup was completed manually")
|
||||||
.addButton((text) => {
|
.addButton((text) => {
|
||||||
text.setButtonText("Enable").onClick(async () => {
|
text.setButtonText("Enable").onClick(async () => {
|
||||||
this.plugin.settings.isConfigured = true;
|
this.plugin.settings.isConfigured = true;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.plugin.settings.isConfigured) {
|
if (this.plugin.settings.isConfigured) {
|
||||||
new Setting(setupWizardEl)
|
new Setting(setupWizardEl)
|
||||||
.setName("Discard exist settings and databases")
|
.setName("Discard existing settings and databases")
|
||||||
.addButton((text) => {
|
.addButton((text) => {
|
||||||
text.setButtonText("Discard").onClick(async () => {
|
text.setButtonText("Discard").onClick(async () => {
|
||||||
if (await askYesNo(this.plugin.app, "Do you really want to discard exist settings and databases?") == "yes") {
|
if (await askYesNo(this.plugin.app, "Do you really want to discard existing settings and databases?") == "yes") {
|
||||||
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
||||||
await this.plugin.saveSettingData();
|
await this.plugin.saveSettingData();
|
||||||
await this.plugin.resetLocalDatabase();
|
await this.plugin.resetLocalDatabase();
|
||||||
// await this.plugin.initializeDatabase();
|
// await this.plugin.initializeDatabase();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
}
|
}
|
||||||
}).setWarning()
|
}).setWarning()
|
||||||
})
|
})
|
||||||
@@ -255,7 +247,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
try {
|
try {
|
||||||
remoteTroubleShootMDSrc = await request(`${rawRepoURI}${basePath}/${filename}`);
|
remoteTroubleShootMDSrc = await request(`${rawRepoURI}${basePath}/${filename}`);
|
||||||
} catch (ex: any) {
|
} catch (ex: any) {
|
||||||
remoteTroubleShootMDSrc = "Error Occurred!!\n" + ex.toString();
|
remoteTroubleShootMDSrc = "An error occurred!!\n" + ex.toString();
|
||||||
}
|
}
|
||||||
const remoteTroubleShootMD = remoteTroubleShootMDSrc.replace(/\((.*?(.png)|(.jpg))\)/g, `(${rawRepoURI}${basePath}/$1)`)
|
const remoteTroubleShootMD = remoteTroubleShootMDSrc.replace(/\((.*?(.png)|(.jpg))\)/g, `(${rawRepoURI}${basePath}/$1)`)
|
||||||
// Render markdown
|
// Render markdown
|
||||||
@@ -333,7 +325,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
const ObjectStorageMessage = `Kindly notice: this is a pretty experimental feature, hence we have some limitations.
|
const ObjectStorageMessage = `Kindly notice: this is a pretty experimental feature, hence we have some limitations.
|
||||||
- Append only architecture. It will not shrink used storage if we do not perform a rebuild.
|
- Append only architecture. It will not shrink used storage if we do not perform a rebuild.
|
||||||
- A bit fragile.
|
- A bit fragile.
|
||||||
- During the first synchronization, the entire history to date will be transferred. For this reason, it is preferable to do this under the WiFi network.
|
- During the first synchronization, the entire history to date will be transferred. For this reason, it is preferable to do this while connected to a Wi-Fi network.
|
||||||
- From the second, we always transfer only differences.
|
- From the second, we always transfer only differences.
|
||||||
|
|
||||||
However, your report is needed to stabilise this. I appreciate you for your great dedication.
|
However, your report is needed to stabilise this. I appreciate you for your great dedication.
|
||||||
@@ -403,7 +395,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
new Setting(containerRemoteDatabaseEl)
|
new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("Apply Setting")
|
.setName("Apply Settings")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button
|
button
|
||||||
@@ -526,7 +518,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
);
|
);
|
||||||
|
|
||||||
new Setting(containerRemoteDatabaseEl)
|
new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("Check and Fix database configuration")
|
.setName("Check and fix database configuration")
|
||||||
.setDesc("Check the database configuration, and fix if there are any problems.")
|
.setDesc("Check the database configuration, and fix if there are any problems.")
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button
|
button
|
||||||
@@ -592,13 +584,13 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
}
|
}
|
||||||
// HTTP user-authorization check
|
// HTTP user-authorization check
|
||||||
if (responseConfig?.chttpd?.require_valid_user != "true") {
|
if (responseConfig?.chttpd?.require_valid_user != "true") {
|
||||||
addResult("❗ chttpd.require_valid_user looks like wrong.");
|
addResult("❗ chttpd.require_valid_user is wrong.");
|
||||||
addConfigFixButton("Set chttpd.require_valid_user = true", "chttpd/require_valid_user", "true");
|
addConfigFixButton("Set chttpd.require_valid_user = true", "chttpd/require_valid_user", "true");
|
||||||
} else {
|
} else {
|
||||||
addResult("✔ chttpd.require_valid_user is ok.");
|
addResult("✔ chttpd.require_valid_user is ok.");
|
||||||
}
|
}
|
||||||
if (responseConfig?.chttpd_auth?.require_valid_user != "true") {
|
if (responseConfig?.chttpd_auth?.require_valid_user != "true") {
|
||||||
addResult("❗ chttpd_auth.require_valid_user looks like wrong.");
|
addResult("❗ chttpd_auth.require_valid_user is wrong.");
|
||||||
addConfigFixButton("Set chttpd_auth.require_valid_user = true", "chttpd_auth/require_valid_user", "true");
|
addConfigFixButton("Set chttpd_auth.require_valid_user = true", "chttpd_auth/require_valid_user", "true");
|
||||||
} else {
|
} else {
|
||||||
addResult("✔ chttpd_auth.require_valid_user is ok.");
|
addResult("✔ chttpd_auth.require_valid_user is ok.");
|
||||||
@@ -665,9 +657,9 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
}));
|
}));
|
||||||
addResult(`Origin check:${org}`);
|
addResult(`Origin check:${org}`);
|
||||||
if (responseHeaders["access-control-allow-credentials"] != "true") {
|
if (responseHeaders["access-control-allow-credentials"] != "true") {
|
||||||
addResult("❗ CORS is not allowing credential");
|
addResult("❗ CORS is not allowing credentials");
|
||||||
} else {
|
} else {
|
||||||
addResult("✔ CORS credential OK");
|
addResult("✔ CORS credentials OK");
|
||||||
}
|
}
|
||||||
if (responseHeaders["access-control-allow-origin"] != org) {
|
if (responseHeaders["access-control-allow-origin"] != org) {
|
||||||
addResult(`❗ CORS Origin is unmatched:${origin}->${responseHeaders["access-control-allow-origin"]}`);
|
addResult(`❗ CORS Origin is unmatched:${origin}->${responseHeaders["access-control-allow-origin"]}`);
|
||||||
@@ -676,7 +668,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
addResult("--Done--", ["ob-btn-config-head"]);
|
addResult("--Done--", ["ob-btn-config-head"]);
|
||||||
addResult("If you have some trouble with Connection-check even though all Config-check has been passed, Please check your reverse proxy's configuration.", ["ob-btn-config-info"]);
|
addResult("If you have some trouble with Connection-check even though all Config-check has been passed, please check your reverse proxy's configuration.", ["ob-btn-config-info"]);
|
||||||
Logger(`Checking configuration done`, LOG_LEVEL_INFO);
|
Logger(`Checking configuration done`, LOG_LEVEL_INFO);
|
||||||
} catch (ex: any) {
|
} catch (ex: any) {
|
||||||
if (ex?.status == 401) {
|
if (ex?.status == 401) {
|
||||||
@@ -698,7 +690,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
|
|
||||||
containerRemoteDatabaseEl.createEl("h4", { text: "Effective Storage Using" }).addClass("wizardHidden")
|
containerRemoteDatabaseEl.createEl("h4", { text: "Effective Storage Using" }).addClass("wizardHidden")
|
||||||
new Setting(containerRemoteDatabaseEl)
|
new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("Incubate Chunks in Document")
|
.setName(confName("useEden"))
|
||||||
.setDesc("If enabled, newly created chunks are temporarily kept within the document, and graduated to become independent chunks once stabilised.")
|
.setDesc("If enabled, newly created chunks are temporarily kept within the document, and graduated to become independent chunks once stabilised.")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.useEden).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.useEden).onChange(async (value) => {
|
||||||
@@ -762,7 +754,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
.setClass("wizardHidden");
|
.setClass("wizardHidden");
|
||||||
}
|
}
|
||||||
new Setting(containerRemoteDatabaseEl)
|
new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("Data Compression (Experimental)")
|
.setName(confName("enableCompression"))
|
||||||
.setDesc("Compresses data during transfer, saving space in the remote database. Note: Please ensure that all devices have v0.22.18 and connected tools are also supported compression.")
|
.setDesc("Compresses data during transfer, saving space in the remote database. Note: Please ensure that all devices have v0.22.18 and connected tools are also supported compression.")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.enableCompression).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.enableCompression).onChange(async (value) => {
|
||||||
@@ -778,7 +770,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
containerRemoteDatabaseEl.createEl("h4", { text: "Confidentiality" });
|
containerRemoteDatabaseEl.createEl("h4", { text: "Confidentiality" });
|
||||||
|
|
||||||
const e2e = new Setting(containerRemoteDatabaseEl)
|
const e2e = new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("End to End Encryption")
|
.setName(confName("encrypt"))
|
||||||
.setDesc("Encrypt contents on the remote database. If you use the plugin's synchronization feature, enabling this is recommend.")
|
.setDesc("Encrypt contents on the remote database. If you use the plugin's synchronization feature, enabling this is recommend.")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(encrypt).onChange(async (value) => {
|
toggle.setValue(encrypt).onChange(async (value) => {
|
||||||
@@ -827,7 +819,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
// if (showEncryptOptionDetail) {
|
// if (showEncryptOptionDetail) {
|
||||||
const passphraseSetting = new Setting(containerRemoteDatabaseEl)
|
const passphraseSetting = new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("Passphrase")
|
.setName("Passphrase")
|
||||||
.setDesc("Encrypting passphrase. If you change the passphrase of a existing database, overwriting the remote database is strongly recommended.")
|
.setDesc("Encrypting passphrase. If you change the passphrase of an existing database, overwriting the remote database is strongly recommended.")
|
||||||
.addText((text) => {
|
.addText((text) => {
|
||||||
text.setPlaceholder("")
|
text.setPlaceholder("")
|
||||||
.setValue(passphrase)
|
.setValue(passphrase)
|
||||||
@@ -846,7 +838,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
});
|
});
|
||||||
|
|
||||||
const usePathObfuscationEl = new Setting(containerRemoteDatabaseEl)
|
const usePathObfuscationEl = new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("Path Obfuscation")
|
.setName(confName("usePathObfuscation"))
|
||||||
.setDesc("Obfuscate paths of files. If we configured, we should rebuild the database.")
|
.setDesc("Obfuscate paths of files. If we configured, we should rebuild the database.")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(usePathObfuscation).onChange(async (value) => {
|
toggle.setValue(usePathObfuscation).onChange(async (value) => {
|
||||||
@@ -863,7 +855,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
);
|
);
|
||||||
|
|
||||||
const dynamicIteration = new Setting(containerRemoteDatabaseEl)
|
const dynamicIteration = new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("Use dynamic iteration count (experimental)")
|
.setName(confName("useDynamicIterationCount"))
|
||||||
.setDesc("Balancing the encryption/decryption load against the length of the passphrase if toggled.")
|
.setDesc("Balancing the encryption/decryption load against the length of the passphrase if toggled.")
|
||||||
.addToggle((toggle) => {
|
.addToggle((toggle) => {
|
||||||
toggle.setValue(useDynamicIterationCount)
|
toggle.setValue(useDynamicIterationCount)
|
||||||
@@ -896,7 +888,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
)
|
)
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button
|
button
|
||||||
.setButtonText("Apply and Fetch")
|
.setButtonText("Apply and fetch")
|
||||||
.setWarning()
|
.setWarning()
|
||||||
.setDisabled(false)
|
.setDisabled(false)
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
@@ -905,7 +897,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
)
|
)
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button
|
button
|
||||||
.setButtonText("Apply and Rebuild")
|
.setButtonText("Apply and rebuild")
|
||||||
.setWarning()
|
.setWarning()
|
||||||
.setDisabled(false)
|
.setDisabled(false)
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
@@ -946,7 +938,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (encrypt && !(await testCrypt())) {
|
if (encrypt && !(await testCrypt())) {
|
||||||
Logger("WARNING! Your device would not support encryption.", LOG_LEVEL_NOTICE);
|
Logger("WARNING! Your device does not support encryption.", LOG_LEVEL_NOTICE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(await checkWorkingPassphrase()) && !sendToServer) {
|
if (!(await checkWorkingPassphrase()) && !sendToServer) {
|
||||||
@@ -978,7 +970,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (encrypt && !(await testCrypt())) {
|
if (encrypt && !(await testCrypt())) {
|
||||||
Logger("WARNING! Your device would not support encryption.", LOG_LEVEL_NOTICE);
|
Logger("WARNING! Your device does not support encryption.", LOG_LEVEL_NOTICE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!encrypt) {
|
if (!encrypt) {
|
||||||
@@ -991,7 +983,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
||||||
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
||||||
this.plugin.settings.isConfigured = true;
|
this.plugin.settings.isConfigured = true;
|
||||||
Logger("All 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();
|
||||||
updateE2EControls();
|
updateE2EControls();
|
||||||
applyDisplayEnabled();
|
applyDisplayEnabled();
|
||||||
@@ -1127,7 +1119,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
let buttonApplyFilename: ButtonComponent;
|
let buttonApplyFilename: ButtonComponent;
|
||||||
new Setting(containerGeneralSettingsEl)
|
new Setting(containerGeneralSettingsEl)
|
||||||
.setName("Filename")
|
.setName("Filename")
|
||||||
.setDesc("If you set this, all settings are saved in a markdown file. You will also be notified when new settings were arrived. You can set different files by the platform.")
|
.setDesc("If you set this, all settings are saved in a markdown file. You will be notified when new settings arrive. You can set different files by the platform.")
|
||||||
.addText((text) => {
|
.addText((text) => {
|
||||||
text.setPlaceholder("livesync/setting.md")
|
text.setPlaceholder("livesync/setting.md")
|
||||||
.setValue(settingSyncFile)
|
.setValue(settingSyncFile)
|
||||||
@@ -1233,7 +1225,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
|
|
||||||
let currentPreset = "NONE";
|
let currentPreset = "NONE";
|
||||||
containerSyncSettingEl.createEl("div",
|
containerSyncSettingEl.createEl("div",
|
||||||
{ text: `Please select any preset to complete wizard.` }
|
{ text: `Please select any preset to complete the wizard.` }
|
||||||
).addClasses(["op-warn-info", "wizardOnly"]);
|
).addClasses(["op-warn-info", "wizardOnly"]);
|
||||||
const options: Record<string, string> = this.plugin.settings.remoteType == REMOTE_COUCHDB ? {
|
const options: Record<string, string> = this.plugin.settings.remoteType == REMOTE_COUCHDB ? {
|
||||||
NONE: "",
|
NONE: "",
|
||||||
@@ -1298,7 +1290,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
}
|
}
|
||||||
Logger("Synchronization setting configured as Periodic sync with batch database update.", LOG_LEVEL_NOTICE);
|
Logger("Synchronization setting configured as Periodic sync with batch database update.", LOG_LEVEL_NOTICE);
|
||||||
} else {
|
} else {
|
||||||
Logger("All synchronization disabled.", LOG_LEVEL_NOTICE);
|
Logger("All synchronizations disabled.", LOG_LEVEL_NOTICE);
|
||||||
this.plugin.settings = {
|
this.plugin.settings = {
|
||||||
...this.plugin.settings,
|
...this.plugin.settings,
|
||||||
...presetAllDisabled
|
...presetAllDisabled
|
||||||
@@ -1316,7 +1308,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
Logger("All done! Please set up subsequent devices with 'Copy current settings as a new setup URI' and 'Use the copied setup URI'.", LOG_LEVEL_NOTICE);
|
Logger("All done! Please set up subsequent devices with 'Copy current settings as a new setup URI' and 'Use the copied setup URI'.", LOG_LEVEL_NOTICE);
|
||||||
await this.plugin.addOnSetup.command_copySetupURI();
|
await this.plugin.addOnSetup.command_copySetupURI();
|
||||||
} else {
|
} else {
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1382,7 +1374,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Sync on Save")
|
.setName("Sync on Save")
|
||||||
.setDesc("When you save file, sync automatically")
|
.setDesc("When you save a file, sync automatically")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.syncOnSave).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.syncOnSave).onChange(async (value) => {
|
||||||
@@ -1393,7 +1385,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
)
|
)
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Sync on Editor Save")
|
.setName("Sync on Editor Save")
|
||||||
.setDesc("When you save file on the editor, sync automatically")
|
.setDesc("When you save a file in the editor, sync automatically")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.syncOnEditorSave).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.syncOnEditorSave).onChange(async (value) => {
|
||||||
@@ -1404,7 +1396,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
)
|
)
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Sync on File Open")
|
.setName("Sync on File Open")
|
||||||
.setDesc("When you open file, sync automatically")
|
.setDesc("When you open a file, sync automatically")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.syncOnFileOpen).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.syncOnFileOpen).onChange(async (value) => {
|
||||||
@@ -1492,7 +1484,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
);
|
);
|
||||||
containerSyncSettingEl.createEl("h4", { text: "Compatibility" }).addClass("wizardHidden");
|
containerSyncSettingEl.createEl("h4", { text: "Compatibility" }).addClass("wizardHidden");
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Always resolve conflict manually")
|
.setName("Always resolve conflicts manually")
|
||||||
.setDesc("If this switch is turned on, a merge dialog will be displayed, even if the sensible-merge is possible automatically. (Turn on to previous behavior)")
|
.setDesc("If this switch is turned on, a merge dialog will be displayed, even if the sensible-merge is possible automatically. (Turn on to previous behavior)")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
@@ -1650,7 +1642,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
);
|
);
|
||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Enhance chunk size")
|
.setName(confName("customChunkSize"))
|
||||||
.setDesc("Enhance chunk size for binary files (Ratio). This cannot be increased when using IBM Cloudant.")
|
.setDesc("Enhance chunk size for binary files (Ratio). This cannot be increased when using IBM Cloudant.")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
.addText((text) => {
|
.addText((text) => {
|
||||||
@@ -1689,7 +1681,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
|
|
||||||
const syncFilesSetting = new Setting(containerSyncSettingEl)
|
const syncFilesSetting = new Setting(containerSyncSettingEl)
|
||||||
.setName("Synchronising files")
|
.setName("Synchronising files")
|
||||||
.setDesc("(RegExp) Empty to sync all files. set filter as a regular expression to limit synchronising files.")
|
.setDesc("(RegExp) Empty to sync all files. Set filter as a regular expression to limit synchronising files.")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
new MultipleRegExpControl(
|
new MultipleRegExpControl(
|
||||||
{
|
{
|
||||||
@@ -1897,7 +1889,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
responseConfig["admins"] = REDACTED;
|
responseConfig["admins"] = REDACTED;
|
||||||
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
responseConfig = "Requesting information to the remote CouchDB has been failed. If you are using IBM Cloudant, it is the normal behaviour."
|
responseConfig = "Requesting information from the remote CouchDB has failed. If you are using IBM Cloudant, this is normal behaviour."
|
||||||
}
|
}
|
||||||
} else if (this.plugin.settings.remoteType == REMOTE_MINIO) {
|
} else if (this.plugin.settings.remoteType == REMOTE_MINIO) {
|
||||||
responseConfig = "Object Storage Synchronisation";
|
responseConfig = "Object Storage Synchronisation";
|
||||||
@@ -1940,7 +1932,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
|
|
||||||
if (this.plugin.replicator.remoteLockedAndDeviceNotAccepted) {
|
if (this.plugin.replicator.remoteLockedAndDeviceNotAccepted) {
|
||||||
const c = containerHatchEl.createEl("div", {
|
const c = containerHatchEl.createEl("div", {
|
||||||
text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization, and this device was not marked as 'resolved'. it caused by some operations like this. re-initialized. Local database initialization should be required. please back your vault up, reset local database, and press 'Mark this device as resolved'. ",
|
text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization, and this device was not marked as 'resolved'. It caused by some operations like this. Re-initialized. Local database initialization should be required. Please back your vault up, reset the local database, and press 'Mark this device as resolved'. ",
|
||||||
});
|
});
|
||||||
c.createEl("button", { text: "I'm ready, mark this device 'resolved'" }, (e) => {
|
c.createEl("button", { text: "I'm ready, mark this device 'resolved'" }, (e) => {
|
||||||
e.addClass("mod-warning");
|
e.addClass("mod-warning");
|
||||||
@@ -1975,7 +1967,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
this.plugin.settings.isConfigured = false;
|
this.plugin.settings.isConfigured = false;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
}));
|
}));
|
||||||
const hatchWarn = containerHatchEl.createEl("div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` });
|
const hatchWarn = containerHatchEl.createEl("div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` });
|
||||||
hatchWarn.addClass("op-warn-info");
|
hatchWarn.addClass("op-warn-info");
|
||||||
@@ -2027,7 +2019,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
}
|
}
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
.setName("Verify and repair all files")
|
.setName("Verify and repair all files")
|
||||||
.setDesc("Compare the content of files between on local database and storage. If not matched, you will asked which one want to keep.")
|
.setDesc("Compare the content of files between on local database and storage. If not matched, you will be asked which one you want to keep.")
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button
|
button
|
||||||
.setButtonText("Verify all")
|
.setButtonText("Verify all")
|
||||||
@@ -2130,7 +2122,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger(`Something went wrong on converting ${docName}`, LOG_LEVEL_NOTICE);
|
Logger(`Something went wrong while converting ${docName}`, LOG_LEVEL_NOTICE);
|
||||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||||
// Something wrong.
|
// Something wrong.
|
||||||
}
|
}
|
||||||
@@ -2166,7 +2158,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
toggle.setValue(this.plugin.settings.suspendFileWatching).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.suspendFileWatching).onChange(async (value) => {
|
||||||
this.plugin.settings.suspendFileWatching = value;
|
this.plugin.settings.suspendFileWatching = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
@@ -2176,7 +2168,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
toggle.setValue(this.plugin.settings.suspendParseReplicationResult).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.suspendParseReplicationResult).onChange(async (value) => {
|
||||||
this.plugin.settings.suspendParseReplicationResult = value;
|
this.plugin.settings.suspendParseReplicationResult = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
@@ -2284,7 +2276,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
})
|
})
|
||||||
|
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
.setName("The Hash algorithm for chunk IDs")
|
.setName(confName("hashAlg"))
|
||||||
.setDesc("xxhash64 is the current default.")
|
.setDesc("xxhash64 is the current default.")
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
.addDropdown((dropdown) =>
|
.addDropdown((dropdown) =>
|
||||||
@@ -2313,6 +2305,15 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
new Setting(containerHatchEl)
|
||||||
|
.setName("Do not check configuration mismatch before replication")
|
||||||
|
.setDesc("")
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle.setValue(this.plugin.settings.disableCheckingConfigMismatch).onChange(async (value) => {
|
||||||
|
this.plugin.settings.disableCheckingConfigMismatch = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
addScreenElement("50", containerHatchEl);
|
addScreenElement("50", containerHatchEl);
|
||||||
|
|
||||||
|
|
||||||
@@ -2409,6 +2410,26 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
|
|
||||||
containerMaintenanceEl.createEl("h4", { text: "Remote" });
|
containerMaintenanceEl.createEl("h4", { text: "Remote" });
|
||||||
|
|
||||||
|
if (this.plugin.settings.remoteType == REMOTE_COUCHDB) {
|
||||||
|
new Setting(containerMaintenanceEl)
|
||||||
|
.setName("Perform compaction")
|
||||||
|
.setDesc("Compaction discards all of Eden in the non-latest revisions, reducing the storage usage. However, this operation requires the same free space on the remote as the current database.")
|
||||||
|
.addButton((button) =>
|
||||||
|
button
|
||||||
|
.setButtonText("Perform")
|
||||||
|
.setDisabled(false)
|
||||||
|
.onClick(async () => {
|
||||||
|
const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator;
|
||||||
|
Logger(`Compaction has been began`, LOG_LEVEL_NOTICE, "compaction")
|
||||||
|
if (await replicator.compactRemote(this.plugin.settings)) {
|
||||||
|
Logger(`Compaction has been completed!`, LOG_LEVEL_NOTICE, "compaction");
|
||||||
|
} else {
|
||||||
|
Logger(`Compaction has been failed!`, LOG_LEVEL_NOTICE, "compaction");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
new Setting(containerMaintenanceEl)
|
new Setting(containerMaintenanceEl)
|
||||||
.setName("Lock remote")
|
.setName("Lock remote")
|
||||||
.setDesc("Lock remote to prevent synchronization with other devices.")
|
.setDesc("Lock remote to prevent synchronization with other devices.")
|
||||||
@@ -2435,6 +2456,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if (this.plugin.settings.remoteType != REMOTE_COUCHDB) {
|
if (this.plugin.settings.remoteType != REMOTE_COUCHDB) {
|
||||||
new Setting(containerMaintenanceEl)
|
new Setting(containerMaintenanceEl)
|
||||||
.setName("Reset journal received history")
|
.setName("Reset journal received history")
|
||||||
|
|||||||
23
updates.md
23
updates.md
@@ -18,6 +18,29 @@ I have a lot of respect for that plugin, even though it is sometimes treated as
|
|||||||
Hooray for open source, and generous licences, and the sharing of knowledge by experts.
|
Hooray for open source, and generous licences, and the sharing of knowledge by experts.
|
||||||
|
|
||||||
#### Version history
|
#### Version history
|
||||||
|
- 0.23.7
|
||||||
|
- Fixed:
|
||||||
|
- No longer missing tasks which have queued as the same key (e.g., for the same operation to the same file).
|
||||||
|
- This occurs, for example, with hidden files that have been changed multiple times in a very short period of time, such as `appearance.json`. Thanks for the report!
|
||||||
|
- Some trivial issues have been fixed.
|
||||||
|
- New feature:
|
||||||
|
- Reloading Obsidian can be scheduled until that file and database operations are stable.
|
||||||
|
- 0.23.6:
|
||||||
|
- Fixed:
|
||||||
|
- Now the remote chunks could be decrypted even if we are using `Incubate chunks in Document`. (The note of 0.23.6 has been fixed).
|
||||||
|
- Chunk retrieving with `Incubate chunks in document` got more efficiently.
|
||||||
|
- No longer task processor misses the completed tasks.
|
||||||
|
- Replication is no longer started automatically during changes in window visibility (e.g., task switching on the desktop) when off-focused.
|
||||||
|
- 0.23.5:
|
||||||
|
- New feature:
|
||||||
|
- Now we can check configuration mismatching between clients before synchronisation.
|
||||||
|
- Default: enabled / Preferred: enabled / We can disable this by the `Do not check configuration mismatch before replication` toggle in the `Hatch` pane.
|
||||||
|
- It detects configuration mismatches and prevents synchronisation failures and wasted storage.
|
||||||
|
- Now we can perform remote database compaction from the `Maintenance` pane.
|
||||||
|
- Fixed:
|
||||||
|
- We can detect the bucket could not be reachable.
|
||||||
|
- Note:
|
||||||
|
- Known inexplicable behaviour: Recently, (Maybe while enabling `Incubate chunks in Document` and `Fetch chunks on demand` or some more toggles), our customisation sync data is sometimes corrupted. It will be addressed by the next release.
|
||||||
- 0.23.4
|
- 0.23.4
|
||||||
- Fixed:
|
- Fixed:
|
||||||
- No longer experimental configuration is shown on the Minimal Setup.
|
- No longer experimental configuration is shown on the Minimal Setup.
|
||||||
|
|||||||
Reference in New Issue
Block a user