mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-13 15:50:36 +00:00
0.24.11
Improved - New Translation: `es` (Spanish) by @zeedif (Thank you so much)! - Now all of messages can be selectable and copyable, also on the iPhone, iPad, and Android devices. Now we can copy or share the messages easily. New Feature - Peer-to-Peer Synchronisation has been implemented! Fixed - No longer memory or resource leaks when the plug-in is disabled. - Now deleted chunks are correctly detected on conflict resolution, and we are guided to resurrect them. - Hanging issue during the initial synchronisation has been fixed. - Some unnecessary logs have been removed. - Now all modal dialogues are correctly closed when the plug-in is disabled. Refactor - Several interfaces have been moved to the separated library. - Translations have been moved to each language file, and during the build, they are merged into one file. - Non-mobile friendly code has been removed and replaced with the safer code. - Started writing Platform impedance-matching-layer. - Svelte has been updated to v5. - Some function have got more robust type definitions. - Terser optimisation has slightly improved. - During the build, analysis meta-file of the bundled codes will be generated.
This commit is contained in:
@@ -66,7 +66,10 @@
|
||||
|
||||
for (const revInfo of reversedRevs) {
|
||||
if (revInfo.status == "available") {
|
||||
const doc = (!isPlain && showDiffInfo) || (checkStorageDiff && revInfo.rev == docA._rev) ? await db.getDBEntry(path, { rev: revInfo.rev }, false, false, true) : await db.getDBEntryMeta(path, { rev: revInfo.rev }, true);
|
||||
const doc =
|
||||
(!isPlain && showDiffInfo) || (checkStorageDiff && revInfo.rev == docA._rev)
|
||||
? await db.getDBEntry(path, { rev: revInfo.rev }, false, false, true)
|
||||
: await db.getDBEntryMeta(path, { rev: revInfo.rev }, true);
|
||||
if (doc === false) continue;
|
||||
const rev = revInfo.rev;
|
||||
|
||||
@@ -94,7 +97,10 @@
|
||||
[DIFF_EQUAL]: 0,
|
||||
[DIFF_INSERT]: 0,
|
||||
} as { [key: number]: number };
|
||||
const px = diff.reduce((p, c) => ({ ...p, [c[0]]: (p[c[0]] ?? 0) + c[1].length }), pxInit);
|
||||
const px = diff.reduce(
|
||||
(p, c) => ({ ...p, [c[0]]: (p[c[0]] ?? 0) + c[1].length }),
|
||||
pxInit
|
||||
);
|
||||
diffDetail = `-${px[DIFF_DELETE]}, +${px[DIFF_INSERT]}`;
|
||||
}
|
||||
}
|
||||
@@ -104,9 +110,13 @@
|
||||
}
|
||||
if (rev == docA._rev) {
|
||||
if (checkStorageDiff) {
|
||||
const isExist = await plugin.storageAccess.isExistsIncludeHidden(stripAllPrefixes(getPath(docA)));
|
||||
const isExist = await plugin.storageAccess.isExistsIncludeHidden(
|
||||
stripAllPrefixes(getPath(docA))
|
||||
);
|
||||
if (isExist) {
|
||||
const data = await plugin.storageAccess.readHiddenFileBinary(stripAllPrefixes(getPath(docA)));
|
||||
const data = await plugin.storageAccess.readHiddenFileBinary(
|
||||
stripAllPrefixes(getPath(docA))
|
||||
);
|
||||
const d = readAsBlob(doc);
|
||||
const result = await isDocContentSame(data, d);
|
||||
if (result) {
|
||||
@@ -187,19 +197,28 @@
|
||||
<div class="globalhistory">
|
||||
<h1>Vault history</h1>
|
||||
<div class="control">
|
||||
<div class="row"><label for="">From:</label><input type="date" bind:value={dispDateFrom} disabled={loading} /></div>
|
||||
<div class="row">
|
||||
<label for="">From:</label><input type="date" bind:value={dispDateFrom} disabled={loading} />
|
||||
</div>
|
||||
<div class="row"><label for="">To:</label><input type="date" bind:value={dispDateTo} disabled={loading} /></div>
|
||||
<div class="row">
|
||||
<label for="">Info:</label>
|
||||
<label><input type="checkbox" bind:checked={showDiffInfo} disabled={loading} /><span>Diff</span></label>
|
||||
<label><input type="checkbox" bind:checked={showChunkCorrected} disabled={loading} /><span>Chunks</span></label>
|
||||
<label><input type="checkbox" bind:checked={checkStorageDiff} disabled={loading} /><span>File integrity</span></label>
|
||||
<label
|
||||
><input type="checkbox" bind:checked={showChunkCorrected} disabled={loading} /><span>Chunks</span
|
||||
></label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" bind:checked={checkStorageDiff} disabled={loading} /><span>File integrity</span
|
||||
></label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{#if loading}
|
||||
<div class="">Gathering information...</div>
|
||||
{/if}
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th> Date </th>
|
||||
<th> Path </th>
|
||||
@@ -212,7 +231,7 @@
|
||||
<tr>
|
||||
<td colspan="5" class="more">
|
||||
{#if loading}
|
||||
<div class="" />
|
||||
<div class=""></div>
|
||||
{:else}
|
||||
<div><button on:click={() => nextWeek()}>+1 week</button></div>
|
||||
{/if}
|
||||
@@ -257,12 +276,13 @@
|
||||
<tr>
|
||||
<td colspan="5" class="more">
|
||||
{#if loading}
|
||||
<div class="" />
|
||||
<div class=""></div>
|
||||
{:else}
|
||||
<div><button on:click={() => prevWeek()}>+1 week</button></div>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
import { ItemView, WorkspaceLeaf } from "../../../deps.ts";
|
||||
import { WorkspaceLeaf } from "../../../deps.ts";
|
||||
import GlobalHistoryComponent from "./GlobalHistory.svelte";
|
||||
import type ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||
import { SvelteItemView } from "../../../common/SvelteItemView.ts";
|
||||
import { mount } from "svelte";
|
||||
|
||||
export const VIEW_TYPE_GLOBAL_HISTORY = "global-history";
|
||||
export class GlobalHistoryView extends ItemView {
|
||||
component?: GlobalHistoryComponent;
|
||||
export class GlobalHistoryView extends SvelteItemView {
|
||||
instantiateComponent(target: HTMLElement) {
|
||||
return mount(GlobalHistoryComponent, {
|
||||
target: target,
|
||||
props: {
|
||||
plugin: this.plugin,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
icon = "clock";
|
||||
title: string = "";
|
||||
@@ -26,19 +36,4 @@ export class GlobalHistoryView extends ItemView {
|
||||
getDisplayText() {
|
||||
return "Vault history";
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
this.component = new GlobalHistoryComponent({
|
||||
target: this.contentEl,
|
||||
props: {
|
||||
plugin: this.plugin,
|
||||
},
|
||||
});
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
this.component?.$destroy();
|
||||
await Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
import { $msg as msg, currentLang as lang } from "../../../lib/src/common/i18n.ts";
|
||||
|
||||
let unsubscribe: () => void;
|
||||
let messages = [] as string[];
|
||||
let wrapRight = false;
|
||||
let autoScroll = true;
|
||||
let suspended = false;
|
||||
let messages = $state([] as string[]);
|
||||
let wrapRight = $state(false);
|
||||
let autoScroll = $state(true);
|
||||
let suspended = $state(false);
|
||||
|
||||
type Props = {
|
||||
close: () => void;
|
||||
};
|
||||
let { close }: Props = $props();
|
||||
// export let close: () => void;
|
||||
function updateLog(logs: ReactiveInstance<string[]>) {
|
||||
const e = logs.value;
|
||||
if (!suspended) {
|
||||
@@ -29,6 +35,9 @@
|
||||
if (unsubscribe) unsubscribe();
|
||||
});
|
||||
let scroll: HTMLDivElement;
|
||||
function closeDialogue() {
|
||||
close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="logpane">
|
||||
@@ -47,6 +56,8 @@
|
||||
<input type="checkbox" bind:checked={suspended} />
|
||||
<span>{msg("logPane.pause", {}, lang)}</span>
|
||||
</label>
|
||||
<span class="spacer"></span>
|
||||
<button onclick={() => closeDialogue()}>Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log" bind:this={scroll}>
|
||||
@@ -68,6 +79,7 @@
|
||||
.log {
|
||||
overflow-y: scroll;
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
.log > pre {
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
import { ItemView, WorkspaceLeaf } from "obsidian";
|
||||
import { WorkspaceLeaf } from "obsidian";
|
||||
import LogPaneComponent from "./LogPane.svelte";
|
||||
import type ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||
import { SvelteItemView } from "../../../common/SvelteItemView.ts";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import { mount } from "svelte";
|
||||
export const VIEW_TYPE_LOG = "log-log";
|
||||
//Log view
|
||||
export class LogPaneView extends ItemView {
|
||||
component?: LogPaneComponent;
|
||||
export class LogPaneView extends SvelteItemView {
|
||||
instantiateComponent(target: HTMLElement) {
|
||||
return mount(LogPaneComponent, {
|
||||
target: target,
|
||||
props: {
|
||||
close: () => {
|
||||
this.leaf.detach();
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
icon = "view-log";
|
||||
title: string = "";
|
||||
navigation = true;
|
||||
navigation = false;
|
||||
|
||||
getIcon(): string {
|
||||
return "view-log";
|
||||
@@ -28,17 +40,4 @@ export class LogPaneView extends ItemView {
|
||||
// TODO: This function is not reactive and does not update the title based on the current language
|
||||
return $msg("logPane.title");
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
this.component = new LogPaneComponent({
|
||||
target: this.contentEl,
|
||||
props: {},
|
||||
});
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
this.component?.$destroy();
|
||||
await Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,14 @@ import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
|
||||
import { LogPaneView, VIEW_TYPE_LOG } from "./Log/LogPaneView.ts";
|
||||
import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import type { P2PReplicationProgress } from "../../lib/src/replication/trystero/TrysteroReplicator.ts";
|
||||
import {
|
||||
EVENT_ADVERTISEMENT_RECEIVED,
|
||||
EVENT_DEVICE_LEAVED,
|
||||
EVENT_P2P_CONNECTED,
|
||||
EVENT_P2P_DISCONNECTED,
|
||||
EVENT_P2P_REPLICATOR_PROGRESS,
|
||||
} from "src/lib/src/replication/trystero/TrysteroReplicatorP2PServer.ts";
|
||||
|
||||
// This module cannot be a core module because it depends on the Obsidian UI.
|
||||
|
||||
@@ -168,10 +176,12 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
const queued = queueCountLabel();
|
||||
const waiting = waitingLabel();
|
||||
const networkActivity = requestingStatLabel();
|
||||
const p2p = this.p2pReplicationLine.value;
|
||||
return {
|
||||
message: `${networkActivity}Sync: ${w} ↑ ${sent}${pushLast} ↓ ${arrived}${pullLast}${waiting}${queued}`,
|
||||
message: `${networkActivity}Sync: ${w} ↑ ${sent}${pushLast} ↓ ${arrived}${pullLast}${waiting}${queued}${p2p == "" ? "" : "\n" + p2p}`,
|
||||
};
|
||||
});
|
||||
|
||||
const statusBarLabels = reactive(() => {
|
||||
const scheduleMessage = this.core.$$isReloadingScheduled()
|
||||
? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n`
|
||||
@@ -193,9 +203,90 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
statusBarLabels.onChanged((label) => applyToDisplay(label.value));
|
||||
}
|
||||
|
||||
p2pReplicationResult = new Map<string, P2PReplicationProgress>();
|
||||
updateP2PReplicationLine() {
|
||||
const p2pReplicationResultX = [...this.p2pReplicationResult.values()].sort((a, b) =>
|
||||
a.peerId.localeCompare(b.peerId)
|
||||
);
|
||||
const renderProgress = (current: number, max: number) => {
|
||||
if (current == max) return `${current}`;
|
||||
return `${current} (${max})`;
|
||||
};
|
||||
const line = p2pReplicationResultX
|
||||
.map(
|
||||
(e) =>
|
||||
`${e.fetching.isActive || e.sending.isActive ? "⚡" : "💤"} ${e.peerName} ↑ ${renderProgress(e.sending.current, e.sending.max)} ↓ ${renderProgress(e.fetching.current, e.fetching.max)} `
|
||||
)
|
||||
.join("\n");
|
||||
this.p2pReplicationLine.value = line;
|
||||
}
|
||||
// p2pReplicationResultX = reactiveSource([] as P2PReplicationProgress[]);
|
||||
p2pReplicationLine = reactiveSource("");
|
||||
|
||||
$everyOnload(): Promise<boolean> {
|
||||
eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange());
|
||||
eventHub.onceEvent(EVENT_LAYOUT_READY, () => this.onActiveLeafChange());
|
||||
eventHub.onEvent(EVENT_ADVERTISEMENT_RECEIVED, (data) => {
|
||||
this.p2pReplicationResult.set(data.peerId, {
|
||||
peerId: data.peerId,
|
||||
peerName: data.name,
|
||||
fetching: {
|
||||
current: 0,
|
||||
max: 0,
|
||||
isActive: false,
|
||||
},
|
||||
sending: {
|
||||
current: 0,
|
||||
max: 0,
|
||||
isActive: false,
|
||||
},
|
||||
});
|
||||
this.updateP2PReplicationLine();
|
||||
});
|
||||
eventHub.onEvent(EVENT_P2P_CONNECTED, () => {
|
||||
this.p2pReplicationResult.clear();
|
||||
this.updateP2PReplicationLine();
|
||||
});
|
||||
eventHub.onEvent(EVENT_P2P_DISCONNECTED, () => {
|
||||
this.p2pReplicationResult.clear();
|
||||
this.updateP2PReplicationLine();
|
||||
});
|
||||
eventHub.onEvent(EVENT_DEVICE_LEAVED, (peerId) => {
|
||||
this.p2pReplicationResult.delete(peerId);
|
||||
this.updateP2PReplicationLine();
|
||||
});
|
||||
eventHub.onEvent(EVENT_P2P_REPLICATOR_PROGRESS, (data) => {
|
||||
const prev = this.p2pReplicationResult.get(data.peerId) || {
|
||||
peerId: data.peerId,
|
||||
peerName: data.peerName,
|
||||
fetching: {
|
||||
current: 0,
|
||||
max: 0,
|
||||
isActive: false,
|
||||
},
|
||||
sending: {
|
||||
current: 0,
|
||||
max: 0,
|
||||
isActive: false,
|
||||
},
|
||||
};
|
||||
if ("fetching" in data) {
|
||||
if (data.fetching.isActive) {
|
||||
prev.fetching = data.fetching;
|
||||
} else {
|
||||
prev.fetching.isActive = false;
|
||||
}
|
||||
}
|
||||
if ("sending" in data) {
|
||||
if (data.sending.isActive) {
|
||||
prev.sending = data.sending;
|
||||
} else {
|
||||
prev.sending.isActive = false;
|
||||
}
|
||||
}
|
||||
this.p2pReplicationResult.set(data.peerId, prev);
|
||||
this.updateP2PReplicationLine();
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
adjustStatusDivPosition() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
LEVEL_EDGE_CASE,
|
||||
type MetaEntry,
|
||||
type FilePath,
|
||||
REMOTE_P2P,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import {
|
||||
createBlob,
|
||||
@@ -78,6 +79,7 @@ import { ICHeader, ICXHeader, PSCHeader } from "../../../common/types.ts";
|
||||
import { HiddenFileSync } from "../../../features/HiddenFileSync/CmdHiddenFileSync.ts";
|
||||
import { EVENT_REQUEST_SHOW_HISTORY } from "../../../common/obsidianEvents.ts";
|
||||
import { LocalDatabaseMaintenance } from "../../../features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
|
||||
import { mount } from "svelte";
|
||||
|
||||
export type OnUpdateResult = {
|
||||
visibility?: boolean;
|
||||
@@ -811,6 +813,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
return false;
|
||||
};
|
||||
const enableOnlySyncDisabled = enableOnly(() => !isAnySyncEnabled());
|
||||
const onlyOnP2POrCouchDB = () =>
|
||||
({
|
||||
visibility:
|
||||
this.isConfiguredAs("remoteType", REMOTE_P2P) || this.isConfiguredAs("remoteType", REMOTE_COUCHDB),
|
||||
}) as OnUpdateResult;
|
||||
|
||||
const onlyOnCouchDB = () =>
|
||||
({
|
||||
visibility: this.isConfiguredAs("remoteType", REMOTE_COUCHDB),
|
||||
@@ -819,7 +827,16 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
({
|
||||
visibility: this.isConfiguredAs("remoteType", REMOTE_MINIO),
|
||||
}) as OnUpdateResult;
|
||||
|
||||
const onlyOnOnlyP2P = () =>
|
||||
({
|
||||
visibility: this.isConfiguredAs("remoteType", REMOTE_P2P),
|
||||
}) as OnUpdateResult;
|
||||
const onlyOnCouchDBOrMinIO = () =>
|
||||
({
|
||||
visibility:
|
||||
this.isConfiguredAs("remoteType", REMOTE_COUCHDB) ||
|
||||
this.isConfiguredAs("remoteType", REMOTE_MINIO),
|
||||
}) as OnUpdateResult;
|
||||
// E2EE Function
|
||||
const checkWorkingPassphrase = async (): Promise<boolean> => {
|
||||
if (this.editingSettings.remoteType == REMOTE_MINIO) return true;
|
||||
@@ -1364,10 +1381,35 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
options: {
|
||||
[REMOTE_COUCHDB]: $msg("obsidianLiveSyncSettingTab.optionCouchDB"),
|
||||
[REMOTE_MINIO]: $msg("obsidianLiveSyncSettingTab.optionMinioS3R2"),
|
||||
[REMOTE_P2P]: "Only Peer-to-Peer",
|
||||
},
|
||||
onUpdate: enableOnlySyncDisabled,
|
||||
});
|
||||
void addPanel(paneEl, "Peer-to-Peer", undefined, onlyOnOnlyP2P).then((paneEl) => {
|
||||
const syncWarnP2P = this.createEl(paneEl, "div", {
|
||||
text: "",
|
||||
});
|
||||
const p2pMessage = `This feature is a Work In Progress, and configurable on \`P2P Replicator\` Pane.
|
||||
The pane also can be launched by \`P2P Replicator\` command from the Command Palette.
|
||||
`;
|
||||
|
||||
void MarkdownRenderer.render(this.plugin.app, p2pMessage, syncWarnP2P, "/", this.plugin);
|
||||
syncWarnP2P.addClass("op-warn-info");
|
||||
new Setting(paneEl)
|
||||
.setName("Apply Settings")
|
||||
.setClass("wizardHidden")
|
||||
.addApplyButton(["remoteType"]);
|
||||
// .addOnUpdate(onlyOnMinIO);
|
||||
// new Setting(paneEl).addButton((button) =>
|
||||
// button
|
||||
// .setButtonText("Open P2P Replicator")
|
||||
// .onClick(() => {
|
||||
// const addOn = this.plugin.getAddOn<P2PReplicator>(P2PReplicator.name);
|
||||
// void addOn?.openPane();
|
||||
// this.closeSetting();
|
||||
// })
|
||||
// );
|
||||
});
|
||||
void addPanel(
|
||||
paneEl,
|
||||
$msg("obsidianLiveSyncSettingTab.titleMinioS3R2"),
|
||||
@@ -1523,7 +1565,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
.addOnUpdate(onlyOnCouchDB);
|
||||
});
|
||||
});
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleNotification")).then((paneEl) => {
|
||||
void addPanel(
|
||||
paneEl,
|
||||
$msg("obsidianLiveSyncSettingTab.titleNotification"),
|
||||
() => {},
|
||||
onlyOnCouchDB
|
||||
).then((paneEl) => {
|
||||
paneEl.addClass("wizardHidden");
|
||||
new Setting(paneEl)
|
||||
.autoWireNumeric("notifyThresholdOfRemoteStorageSize", {})
|
||||
@@ -2001,7 +2048,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
"(RegExp) Empty to sync all files. Set filter as a regular expression to limit synchronising files."
|
||||
)
|
||||
.setClass("wizardHidden");
|
||||
new MultipleRegExpControl({
|
||||
mount(MultipleRegExpControl, {
|
||||
target: syncFilesSetting.controlEl,
|
||||
props: {
|
||||
patterns: this.editingSettings.syncOnlyRegEx.split("|[]|"),
|
||||
@@ -2024,7 +2071,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
)
|
||||
.setClass("wizardHidden");
|
||||
|
||||
new MultipleRegExpControl({
|
||||
mount(MultipleRegExpControl, {
|
||||
target: nonSyncFilesSetting.controlEl,
|
||||
props: {
|
||||
patterns: this.editingSettings.syncIgnoreRegEx.split("|[]|"),
|
||||
@@ -2057,7 +2104,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
.filter((x) => x != "");
|
||||
const patSetting = new Setting(paneEl).setName("Ignore patterns").setClass("wizardHidden").setDesc("");
|
||||
|
||||
new MultipleRegExpControl({
|
||||
mount(MultipleRegExpControl, {
|
||||
target: patSetting.controlEl,
|
||||
props: {
|
||||
patterns: pat,
|
||||
@@ -2233,9 +2280,14 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
pluginConfig.encryptedCouchDBConnection = REDACTED;
|
||||
pluginConfig.accessKey = REDACTED;
|
||||
pluginConfig.secretKey = REDACTED;
|
||||
pluginConfig.region = `${REDACTED}(${pluginConfig.region.length} letters)`;
|
||||
pluginConfig.bucket = `${REDACTED}(${pluginConfig.bucket.length} letters)`;
|
||||
const redact = (source: string) => `${REDACTED}(${source.length} letters)`;
|
||||
pluginConfig.region = redact(pluginConfig.region);
|
||||
pluginConfig.bucket = redact(pluginConfig.bucket);
|
||||
pluginConfig.pluginSyncExtendedSetting = {};
|
||||
pluginConfig.P2P_AppID = redact(pluginConfig.P2P_AppID);
|
||||
pluginConfig.P2P_passphrase = redact(pluginConfig.P2P_passphrase);
|
||||
pluginConfig.P2P_roomID = redact(pluginConfig.P2P_roomID);
|
||||
pluginConfig.P2P_relays = redact(pluginConfig.P2P_relays);
|
||||
const endpoint = pluginConfig.endpoint;
|
||||
if (endpoint == "") {
|
||||
pluginConfig.endpoint = "Not configured or AWS";
|
||||
@@ -2918,7 +2970,8 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
.onClick(async () => {
|
||||
await this.plugin.$$markRemoteLocked();
|
||||
})
|
||||
);
|
||||
)
|
||||
.addOnUpdate(onlyOnCouchDBOrMinIO);
|
||||
|
||||
new Setting(paneEl)
|
||||
.setName("Emergency restart")
|
||||
@@ -2935,7 +2988,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
);
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Syncing").then((paneEl) => {
|
||||
void addPanel(paneEl, "Syncing", () => {}, onlyOnCouchDBOrMinIO).then((paneEl) => {
|
||||
new Setting(paneEl)
|
||||
.setName("Resend")
|
||||
.setDesc("Resend all chunks to the remote.")
|
||||
@@ -2951,6 +3004,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnCouchDB);
|
||||
|
||||
new Setting(paneEl)
|
||||
.setName("Reset journal received history")
|
||||
.setDesc(
|
||||
@@ -2994,7 +3048,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
)
|
||||
.addOnUpdate(onlyOnMinIO);
|
||||
});
|
||||
void addPanel(paneEl, "Garbage Collection (Beta)", (e) => e, onlyOnCouchDB).then((paneEl) => {
|
||||
void addPanel(paneEl, "Garbage Collection (Beta)", (e) => e, onlyOnP2POrCouchDB).then((paneEl) => {
|
||||
new Setting(paneEl)
|
||||
.setName("Remove all orphaned chunks")
|
||||
.setDesc("Remove all orphaned chunks from the local database.")
|
||||
@@ -3080,7 +3134,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
.addOnUpdate(onlyOnCouchDB);
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Total Overhaul").then((paneEl) => {
|
||||
void addPanel(paneEl, "Total Overhaul", () => {}, onlyOnCouchDBOrMinIO).then((paneEl) => {
|
||||
new Setting(paneEl)
|
||||
.setName("Rebuild everything")
|
||||
.setDesc("Rebuild local and remote database with local files.")
|
||||
@@ -3104,94 +3158,98 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
})
|
||||
);
|
||||
});
|
||||
void addPanel(paneEl, "Rebuilding Operations (Remote Only)").then((paneEl) => {
|
||||
new Setting(paneEl)
|
||||
.setName("Perform cleanup")
|
||||
.setDesc(
|
||||
"Reduces storage space by discarding all non-latest revisions. This requires the same amount of free space on the remote server and the local client."
|
||||
)
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Perform")
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator;
|
||||
Logger(`Cleanup has been began`, LOG_LEVEL_NOTICE, "compaction");
|
||||
if (await replicator.compactRemote(this.editingSettings)) {
|
||||
Logger(`Cleanup has been completed!`, LOG_LEVEL_NOTICE, "compaction");
|
||||
} else {
|
||||
Logger(`Cleanup has been failed!`, LOG_LEVEL_NOTICE, "compaction");
|
||||
}
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnCouchDB);
|
||||
void addPanel(paneEl, "Rebuilding Operations (Remote Only)", () => {}, onlyOnCouchDBOrMinIO).then(
|
||||
(paneEl) => {
|
||||
new Setting(paneEl)
|
||||
.setName("Perform cleanup")
|
||||
.setDesc(
|
||||
"Reduces storage space by discarding all non-latest revisions. This requires the same amount of free space on the remote server and the local client."
|
||||
)
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Perform")
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator;
|
||||
Logger(`Cleanup has been began`, LOG_LEVEL_NOTICE, "compaction");
|
||||
if (await replicator.compactRemote(this.editingSettings)) {
|
||||
Logger(`Cleanup has been completed!`, LOG_LEVEL_NOTICE, "compaction");
|
||||
} else {
|
||||
Logger(`Cleanup has been failed!`, LOG_LEVEL_NOTICE, "compaction");
|
||||
}
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnCouchDB);
|
||||
|
||||
new Setting(paneEl)
|
||||
.setName("Overwrite remote")
|
||||
.setDesc("Overwrite remote with local DB and passphrase.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Send")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await rebuildDB("remoteOnly");
|
||||
})
|
||||
);
|
||||
new Setting(paneEl)
|
||||
.setName("Overwrite remote")
|
||||
.setDesc("Overwrite remote with local DB and passphrase.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Send")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await rebuildDB("remoteOnly");
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(paneEl)
|
||||
.setName("Reset all journal counter")
|
||||
.setDesc("Initialise all journal history, On the next sync, every item will be received and sent.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Reset all")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.getMinioJournalSyncClient().resetCheckpointInfo();
|
||||
Logger(`Journal exchange history has been cleared.`, LOG_LEVEL_NOTICE);
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnMinIO);
|
||||
new Setting(paneEl)
|
||||
.setName("Reset all journal counter")
|
||||
.setDesc(
|
||||
"Initialise all journal history, On the next sync, every item will be received and sent."
|
||||
)
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Reset all")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.getMinioJournalSyncClient().resetCheckpointInfo();
|
||||
Logger(`Journal exchange history has been cleared.`, LOG_LEVEL_NOTICE);
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnMinIO);
|
||||
|
||||
new Setting(paneEl)
|
||||
.setName("Purge all journal counter")
|
||||
.setDesc("Purge all download/upload cache.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Reset all")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.getMinioJournalSyncClient().resetAllCaches();
|
||||
Logger(`Journal download/upload cache has been cleared.`, LOG_LEVEL_NOTICE);
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnMinIO);
|
||||
new Setting(paneEl)
|
||||
.setName("Purge all journal counter")
|
||||
.setDesc("Purge all download/upload cache.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Reset all")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.getMinioJournalSyncClient().resetAllCaches();
|
||||
Logger(`Journal download/upload cache has been cleared.`, LOG_LEVEL_NOTICE);
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnMinIO);
|
||||
|
||||
new Setting(paneEl)
|
||||
.setName("Fresh Start Wipe")
|
||||
.setDesc("Delete all data on the remote server.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Delete")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({
|
||||
...info,
|
||||
receivedFiles: new Set(),
|
||||
knownIDs: new Set(),
|
||||
lastLocalSeq: 0,
|
||||
sentIDs: new Set(),
|
||||
sentFiles: new Set(),
|
||||
}));
|
||||
await this.resetRemoteBucket();
|
||||
Logger(`Deleted all data on remote server`, LOG_LEVEL_NOTICE);
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnMinIO);
|
||||
});
|
||||
new Setting(paneEl)
|
||||
.setName("Fresh Start Wipe")
|
||||
.setDesc("Delete all data on the remote server.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Delete")
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({
|
||||
...info,
|
||||
receivedFiles: new Set(),
|
||||
knownIDs: new Set(),
|
||||
lastLocalSeq: 0,
|
||||
sentIDs: new Set(),
|
||||
sentFiles: new Set(),
|
||||
}));
|
||||
await this.resetRemoteBucket();
|
||||
Logger(`Deleted all data on remote server`, LOG_LEVEL_NOTICE);
|
||||
})
|
||||
)
|
||||
.addOnUpdate(onlyOnMinIO);
|
||||
}
|
||||
);
|
||||
|
||||
void addPanel(paneEl, "Reset").then((paneEl) => {
|
||||
new Setting(paneEl)
|
||||
|
||||
Reference in New Issue
Block a user