feat: add WebDAV and PostgREST journal remotes

This commit is contained in:
vorotamoroz
2026-07-31 03:21:36 +00:00
parent b21c3114e5
commit 5acad15eba
30 changed files with 1317 additions and 237 deletions
@@ -8913,6 +8913,12 @@ export const allMessages: Readonly<Record<string, Readonly<Record<string, string
def: "This enables direct synchronisation between devices. No server is required, but both devices must be online at the same time and some features may be limited. Internet connectivity is required only for signalling, not for data transfer.",
zh: "启用设备之间的直接同步。无需服务器,但两台设备必须同时在线,且部分功能可能受限。互联网连接仅用于信令,不用于传输数据。",
},
"Ui.SetupWizard.SetupRemote.PostgRESTOption": {
def: "PostgREST Journal Storage",
},
"Ui.SetupWizard.SetupRemote.PostgRESTOptionDesc": {
def: "Synchronisation using Journal objects stored in PostgreSQL through the packaged PostgREST RPC schema. The server SQL and a Vault-scoped JWT must already be configured.",
},
"Ui.SetupWizard.SetupRemote.ProceedBucket": {
def: "Continue to Object Storage setup",
zh: "继续配置 S3/MinIO/R2",
@@ -8925,10 +8931,22 @@ export const allMessages: Readonly<Record<string, Readonly<Record<string, string
def: "Continue to P2P setup",
zh: "继续配置仅点对点模式",
},
"Ui.SetupWizard.SetupRemote.ProceedPostgREST": {
def: "Continue to PostgREST setup",
},
"Ui.SetupWizard.SetupRemote.ProceedWebDAV": {
def: "Continue to WebDAV setup",
},
"Ui.SetupWizard.SetupRemote.Title": {
def: "Choose a synchronisation remote",
zh: "输入服务器信息",
},
"Ui.SetupWizard.SetupRemote.WebDAVOption": {
def: "WebDAV Journal Storage",
},
"Ui.SetupWizard.SetupRemote.WebDAVOptionDesc": {
def: "Synchronisation using Journal files in a dedicated WebDAV collection. Listing cost grows with the number of retained Journal files.",
},
"Unique name between all synchronized devices. To edit this setting, please disable customization sync once.": {
def: "Unique name between all synchronized devices. To edit this setting, please disable customization sync once.",
es: "Nombre único entre dispositivos sincronizados. Para editarlo, desactive sincronización de personalización",
+6
View File
@@ -1100,10 +1100,16 @@
"Ui.SetupWizard.SetupRemote.Guidance": "Select the remote type for this synchronisation setup.",
"Ui.SetupWizard.SetupRemote.P2POption": "Peer-to-Peer (P2P)",
"Ui.SetupWizard.SetupRemote.P2POptionDesc": "This enables direct synchronisation between devices. No server is required, but both devices must be online at the same time and some features may be limited. Internet connectivity is required only for signalling, not for data transfer.",
"Ui.SetupWizard.SetupRemote.PostgRESTOption": "PostgREST Journal Storage",
"Ui.SetupWizard.SetupRemote.PostgRESTOptionDesc": "Synchronisation using Journal objects stored in PostgreSQL through the packaged PostgREST RPC schema. The server SQL and a Vault-scoped JWT must already be configured.",
"Ui.SetupWizard.SetupRemote.ProceedBucket": "Continue to Object Storage setup",
"Ui.SetupWizard.SetupRemote.ProceedCouchDb": "Continue to CouchDB setup",
"Ui.SetupWizard.SetupRemote.ProceedP2P": "Continue to P2P setup",
"Ui.SetupWizard.SetupRemote.ProceedPostgREST": "Continue to PostgREST setup",
"Ui.SetupWizard.SetupRemote.ProceedWebDAV": "Continue to WebDAV setup",
"Ui.SetupWizard.SetupRemote.Title": "Choose a synchronisation remote",
"Ui.SetupWizard.SetupRemote.WebDAVOption": "WebDAV Journal Storage",
"Ui.SetupWizard.SetupRemote.WebDAVOptionDesc": "Synchronisation using Journal files in a dedicated WebDAV collection. Listing cost grows with the number of retained Journal files.",
"Unique name between all synchronized devices. To edit this setting, please disable customization sync once.": "Unique name between all synchronized devices. To edit this setting, please disable customization sync once.",
"Use a custom passphrase": "Use a custom passphrase",
"Use a Setup URI (Recommended)": "Use a Setup URI (Recommended)",
+6
View File
@@ -1957,7 +1957,13 @@ Ui:
Guidance: Select the remote type for this synchronisation setup.
P2POption: Peer-to-Peer (P2P)
P2POptionDesc: This enables direct synchronisation between devices. No server is required, but both devices must be online at the same time and some features may be limited. Internet connectivity is required only for signalling, not for data transfer.
PostgRESTOption: PostgREST Journal Storage
PostgRESTOptionDesc: Synchronisation using Journal objects stored in PostgreSQL through the packaged PostgREST RPC schema. The server SQL and a Vault-scoped JWT must already be configured.
ProceedBucket: Continue to Object Storage setup
ProceedCouchDb: Continue to CouchDB setup
ProceedP2P: Continue to P2P setup
ProceedPostgREST: Continue to PostgREST setup
ProceedWebDAV: Continue to WebDAV setup
Title: Choose a synchronisation remote
WebDAVOption: WebDAV Journal Storage
WebDAVOptionDesc: Synchronisation using Journal files in a dedicated WebDAV collection. Listing cost grows with the number of retained Journal files.
+6 -3
View File
@@ -1,4 +1,5 @@
import { REMOTE_COUCHDB, REMOTE_MINIO } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const";
import { REMOTE_COUCHDB } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const";
import { isJournalRemoteType } from "@vrtmrz/livesync-commonlib/journal-storage";
import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/settings";
import { generateCredentialObject } from "@vrtmrz/livesync-commonlib/compat/replication/httplib";
import { parseHeaderValues } from "@vrtmrz/livesync-commonlib/compat/common/utils";
@@ -55,8 +56,8 @@ export async function generateReport(settings: ObsidianLiveSyncSettings, core: L
error: "Requesting information from the remote CouchDB has failed. If you are using IBM Cloudant, this is normal behaviour.",
};
}
} else if (settings.remoteType == REMOTE_MINIO) {
responseConfig = { error: "Object Storage Synchronisation" };
} else if (isJournalRemoteType(settings.remoteType)) {
responseConfig = { error: "Journal Storage Synchronisation" };
//
}
const defaultKeys = Object.keys(DEFAULT_SETTINGS) as (keyof ObsidianLiveSyncSettings)[];
@@ -81,6 +82,8 @@ export async function generateReport(settings: ObsidianLiveSyncSettings, core: L
pluginConfig.encryptedCouchDBConnection = REDACTED;
pluginConfig.accessKey = REDACTED;
pluginConfig.secretKey = REDACTED;
pluginConfig.webDAVactiveConnectionURI = REDACTED;
pluginConfig.postgrestActiveConnectionURI = REDACTED;
const redact = (source: string) => `${REDACTED}(${source.length} letters)`;
const toSchemeOnly = (uri: string) => {
try {
+64
View File
@@ -0,0 +1,64 @@
import { describe, expect, it, vi } from "vitest";
import { DEFAULT_SETTINGS } from "@vrtmrz/livesync-commonlib/settings";
import {
REMOTE_POSTGREST,
REMOTE_WEBDAV,
serialisePostgRESTConnectionURI,
serialiseWebDAVConnectionURI,
} from "@vrtmrz/livesync-commonlib/journal-storage";
import { generateReport } from "./reportTool";
vi.mock("./utils", () => ({
requestToCouchDBWithCredentials: vi.fn(),
}));
describe("generateReport Journal connection redaction", () => {
it.each([
{
remoteType: REMOTE_WEBDAV,
settingKey: "webDAVactiveConnectionURI",
secret: "webdav-secret",
uri: serialiseWebDAVConnectionURI({
endpoint: "https://dav.example/vault",
username: "alice",
password: "webdav-secret",
prefix: "journal/",
useCustomRequestHandler: false,
customHeaders: "x-private-header: private-value",
}),
},
{
remoteType: REMOTE_POSTGREST,
settingKey: "postgrestActiveConnectionURI",
secret: "signed-jwt-secret",
uri: serialisePostgRESTConnectionURI({
endpoint: "https://journal.example",
bearerToken: "signed-jwt-secret",
vaultId: "private-vault-id",
schema: "livesync_api",
useCustomRequestHandler: false,
customHeaders: "x-private-header: private-value",
}),
},
] as const)("redacts the flat $remoteType connection URI", async (provider) => {
const settings = {
...DEFAULT_SETTINGS,
remoteType: provider.remoteType,
[provider.settingKey]: provider.uri,
};
const core = {
services: {
vault: {
isStorageInsensitive: () => false,
},
},
} as any;
const report = await generateReport(settings, core);
const serialised = JSON.stringify(report);
expect(serialised).not.toContain(provider.secret);
expect(serialised).not.toContain("private-value");
expect(report.pluginConfig[provider.settingKey]).toBe("𝑅𝐸𝐷𝐴𝐶𝑇𝐸𝐷");
});
});