mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-08-29 06:47:06 +00:00
feat: add WebDAV and PostgREST journal remotes
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
||||
import FetchEverything from "@/modules/features/SetupWizard/dialogs/FetchEverything.svelte";
|
||||
import RebuildEverything from "@/modules/features/SetupWizard/dialogs/RebuildEverything.svelte";
|
||||
import { extractObject } from "octagonal-wheels/object";
|
||||
import { REMOTE_MINIO, REMOTE_P2P } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const";
|
||||
import { REMOTE_P2P } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const";
|
||||
import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/settings";
|
||||
import { TweakValuesShouldMatchedTemplate } from "@vrtmrz/livesync-commonlib/compat/common/models/tweak.definition";
|
||||
import type {
|
||||
@@ -17,7 +17,11 @@ import type {
|
||||
} from "@/modules/features/SetupWizard/dialogs/setupDialogTypes";
|
||||
import { askAndPerformFastSetupOnScheduledFetchAll } from "./redFlag.simpleFetch";
|
||||
import { ConnectionStringParser } from "@vrtmrz/livesync-commonlib/compat/common/ConnectionString";
|
||||
import { activateRemoteConfiguration } from "@vrtmrz/livesync-commonlib/remote-configurations";
|
||||
import {
|
||||
activateRemoteConfiguration,
|
||||
suggestRemoteConfigurationName,
|
||||
} from "@vrtmrz/livesync-commonlib/remote-configurations";
|
||||
import { isJournalRemoteType } from "@vrtmrz/livesync-commonlib/journal-storage";
|
||||
import { isP2PMainRemote } from "@/common/remoteConfiguration";
|
||||
|
||||
/**
|
||||
@@ -61,7 +65,7 @@ async function askAndActivateRemoteDatabase(host: NecessaryServices<"UI" | "sett
|
||||
"Multiple remote configurations detected. Please select the remote configuration you want to fetch from.";
|
||||
const options = Object.entries(settings.remoteConfigurations).map(([id, config]) => {
|
||||
const parsed = ConnectionStringParser.parse(config.uri);
|
||||
const displayURI = (config.uri.split("@").pop() || "").substring(0, 20) + "..."; // Show only the last part of URI for better readability and privacy.
|
||||
const displayURI = suggestRemoteConfigurationName(parsed);
|
||||
return {
|
||||
name: `${config.name} - ${parsed.type} (${displayURI})`,
|
||||
id: id,
|
||||
@@ -164,8 +168,8 @@ export function createFetchAllFlagHandler(
|
||||
}
|
||||
const { vault, extra } = method;
|
||||
const settings = await Promise.resolve(host.services.setting.currentSettings());
|
||||
// If remote is MinIO, makeLocalChunkBeforeSync is not available. (because no-deduplication on sending).
|
||||
const makeLocalChunkBeforeSyncAvailable = settings.remoteType !== REMOTE_MINIO;
|
||||
// Journal Storage remotes do not deduplicate chunks while sending.
|
||||
const makeLocalChunkBeforeSyncAvailable = !isJournalRemoteType(settings.remoteType);
|
||||
const mapVaultStateToAction = {
|
||||
identical: {
|
||||
makeLocalChunkBeforeSync: makeLocalChunkBeforeSyncAvailable,
|
||||
|
||||
@@ -41,6 +41,11 @@ import {
|
||||
askSimpleFetchMode,
|
||||
} from "./redFlag.simpleFetch";
|
||||
import { activateRemoteConfiguration } from "@vrtmrz/livesync-commonlib/remote-configurations";
|
||||
import {
|
||||
REMOTE_POSTGREST,
|
||||
REMOTE_WEBDAV,
|
||||
serialiseWebDAVConnectionURI,
|
||||
} from "@vrtmrz/livesync-commonlib/journal-storage";
|
||||
//Mock synchroniseAllFilesBetweenDBandStorage
|
||||
vi.mock("@vrtmrz/livesync-commonlib/compat/serviceFeatures/offlineScanner", async (importOriginal) => {
|
||||
const originalModule = (await importOriginal()) as any;
|
||||
@@ -50,8 +55,10 @@ vi.mock("@vrtmrz/livesync-commonlib/compat/serviceFeatures/offlineScanner", asyn
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@vrtmrz/livesync-commonlib/compat/serviceFeatures/remoteConfig", () => {
|
||||
vi.mock("@vrtmrz/livesync-commonlib/compat/serviceFeatures/remoteConfig", async (importOriginal) => {
|
||||
const originalModule = (await importOriginal()) as any;
|
||||
return {
|
||||
...originalModule,
|
||||
activateRemoteConfiguration: vi.fn((settings: any, configurationId: string) => {
|
||||
if (!settings?.remoteConfigurations?.[configurationId]) return false;
|
||||
return {
|
||||
@@ -579,6 +586,40 @@ describe("Red Flag Feature", () => {
|
||||
expect(host.mocks.ui.confirm.confirmWithMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not expose unauthenticated WebDAV headers in the remote selection", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
const privateHeader = "x-private-header: private-value";
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
Object.assign(host.mocks.setting.settings, {
|
||||
remoteConfigurations: {
|
||||
alpha: {
|
||||
name: "Alpha",
|
||||
uri: serialiseWebDAVConnectionURI({
|
||||
endpoint: "https://dav.example/alpha",
|
||||
username: "",
|
||||
password: "",
|
||||
prefix: "",
|
||||
useCustomRequestHandler: false,
|
||||
customHeaders: privateHeader,
|
||||
}),
|
||||
},
|
||||
beta: {
|
||||
name: "Beta",
|
||||
uri: "sls+https://user:pass@example.com/db2",
|
||||
},
|
||||
},
|
||||
});
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockResolvedValueOnce("Cancel");
|
||||
|
||||
await createFetchAllFlagHandler(host as any, log).handle();
|
||||
|
||||
const selections = host.mocks.ui.confirm.askSelectStringDialogue.mock.calls[0]?.[1] as string[];
|
||||
expect(selections.join("\n")).not.toContain("private-value");
|
||||
expect(selections).toContain("Alpha - webdav (WebDAV dav.example)");
|
||||
});
|
||||
|
||||
it("should activate selected remote configuration", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
@@ -1140,25 +1181,44 @@ describe("Red Flag Feature", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("MinIO configuration handling", () => {
|
||||
it("should not enable makeLocalChunkBeforeSync when remote is MinIO", () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.setting.settings.remoteType = REMOTE_MINIO;
|
||||
describe("Journal Storage configuration handling", () => {
|
||||
it.each([REMOTE_MINIO, REMOTE_WEBDAV, REMOTE_POSTGREST])(
|
||||
"does not prepare deduplicated local chunks for %s",
|
||||
async (remoteType) => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
host.mocks.setting.settings.remoteType = remoteType;
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_DETAILED);
|
||||
host.mocks.ui.dialogManager.openWithExplicitCancel.mockResolvedValueOnce({
|
||||
vault: "identical",
|
||||
backup: "backup_skipped",
|
||||
extra: { preventFetchingConfig: true },
|
||||
});
|
||||
|
||||
const settings = host.mocks.setting.currentSettings();
|
||||
const isMinIO = settings.remoteType === REMOTE_MINIO;
|
||||
const result = await createFetchAllFlagHandler(host as any, log).handle();
|
||||
|
||||
expect(isMinIO).toBe(true);
|
||||
});
|
||||
expect(result).toBe(true);
|
||||
expect(host.mocks.rebuilder.$fetchLocal).toHaveBeenCalledWith(false, true);
|
||||
}
|
||||
);
|
||||
|
||||
it("should enable makeLocalChunkBeforeSync for non-MinIO remotes", () => {
|
||||
it("prepares deduplicated local chunks for CouchDB", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
host.mocks.setting.settings.remoteType = "CouchDB";
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_DETAILED);
|
||||
host.mocks.ui.dialogManager.openWithExplicitCancel.mockResolvedValueOnce({
|
||||
vault: "identical",
|
||||
backup: "backup_skipped",
|
||||
extra: { preventFetchingConfig: true },
|
||||
});
|
||||
|
||||
const settings = host.mocks.setting.currentSettings();
|
||||
const isMinIO = settings.remoteType === REMOTE_MINIO;
|
||||
const result = await createFetchAllFlagHandler(host as any, log).handle();
|
||||
|
||||
expect(isMinIO).toBe(false);
|
||||
expect(result).toBe(true);
|
||||
expect(host.mocks.rebuilder.$fetchLocal).toHaveBeenCalledWith(true, true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user