No devices available. Waiting for other devices to connect...
+ {/if} +Please select an active P2P remote configuration to change P2P sync targets.
+ {/if} + +No devices available. Waiting for other devices to connect...
+ {:else} +Fetching status...
+ {/if} +${$msg("obsidianLiveSyncSettingTab.msgNewVersionNote")}
`; + tmpDiv.createEl("p", { text: $msg("obsidianLiveSyncSettingTab.msgNewVersionNote") }); + const readEverythingButton = tmpDiv.createEl("button", { + text: $msg("obsidianLiveSyncSettingTab.optionOkReadEverything"), + }); if (lastVersion > (this.editingSettings?.lastReadUpdates || 0)) { const informationButtonDiv = informationDivEl.appendChild(tmpDiv); - informationButtonDiv.querySelector("button")?.addEventListener("click", () => { + readEverythingButton.addEventListener("click", () => { fireAndForget(async () => { this.editingSettings.lastReadUpdates = lastVersion; await this.saveAllDirtySettings(); diff --git a/src/modules/features/SettingDialogue/PaneHatch.ts b/src/modules/features/SettingDialogue/PaneHatch.ts index ebfb377..d9ca27c 100644 --- a/src/modules/features/SettingDialogue/PaneHatch.ts +++ b/src/modules/features/SettingDialogue/PaneHatch.ts @@ -39,6 +39,7 @@ import { EVENT_REQUEST_SHOW_HISTORY } from "../../../common/obsidianEvents.ts"; import { generateCredentialObject } from "../../../lib/src/replication/httplib.ts"; import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; +import { generateReport } from "@/common/reportTool.ts"; export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void { // const hatchWarn = this.createEl(paneEl, "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"); @@ -69,140 +70,14 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, eventHub.emitEvent(EVENT_REQUEST_RUN_FIX_INCOMPLETE); }) ); + new Setting(paneEl).setName($msg("Prepare the 'report' to create an issue")).addButton((button) => button .setButtonText($msg("Copy Report to clipboard")) .setCta() .setDisabled(false) .onClick(async () => { - let responseConfig: any = {}; - const REDACTED = "𝑅𝐸𝐷𝐴𝐶𝑇𝐸𝐷"; - if (this.editingSettings.remoteType == REMOTE_COUCHDB) { - try { - const credential = generateCredentialObject(this.editingSettings); - const customHeaders = parseHeaderValues(this.editingSettings.couchDB_CustomHeaders); - const r = await requestToCouchDBWithCredentials( - this.editingSettings.couchDB_URI, - credential, - window.origin, - undefined, - undefined, - undefined, - customHeaders - ); - - Logger(JSON.stringify(r.json, null, 2)); - - responseConfig = r.json; - responseConfig["couch_httpd_auth"].secret = REDACTED; - responseConfig["couch_httpd_auth"].authentication_db = REDACTED; - responseConfig["couch_httpd_auth"].authentication_redirect = REDACTED; - responseConfig["couchdb"].uuid = REDACTED; - responseConfig["admins"] = REDACTED; - delete responseConfig["jwt_keys"]; - if ("secret" in responseConfig["chttpd_auth"]) - responseConfig["chttpd_auth"].secret = REDACTED; - } catch (ex) { - Logger(ex, LOG_LEVEL_VERBOSE); - responseConfig = { - error: "Requesting information from the remote CouchDB has failed. If you are using IBM Cloudant, this is normal behaviour.", - }; - } - } else if (this.editingSettings.remoteType == REMOTE_MINIO) { - responseConfig = { error: "Object Storage Synchronisation" }; - // - } - const defaultKeys = Object.keys(DEFAULT_SETTINGS) as (keyof ObsidianLiveSyncSettings)[]; - const pluginConfig = JSON.parse(JSON.stringify(this.editingSettings)) as ObsidianLiveSyncSettings; - const pluginKeys = Object.keys(pluginConfig); - for (const key of pluginKeys) { - if (defaultKeys.includes(key as any)) continue; - delete pluginConfig[key as keyof ObsidianLiveSyncSettings]; - } - - pluginConfig.couchDB_DBNAME = REDACTED; - pluginConfig.couchDB_PASSWORD = REDACTED; - const scheme = pluginConfig.couchDB_URI.startsWith("http:") - ? "(HTTP)" - : pluginConfig.couchDB_URI.startsWith("https:") - ? "(HTTPS)" - : ""; - pluginConfig.couchDB_URI = isCloudantURI(pluginConfig.couchDB_URI) - ? "cloudant" - : `self-hosted${scheme}`; - pluginConfig.couchDB_USER = REDACTED; - pluginConfig.passphrase = REDACTED; - pluginConfig.encryptedPassphrase = REDACTED; - pluginConfig.encryptedCouchDBConnection = REDACTED; - pluginConfig.accessKey = REDACTED; - pluginConfig.secretKey = REDACTED; - const redact = (source: string) => `${REDACTED}(${source.length} letters)`; - const toSchemeOnly = (uri: string) => { - try { - return `${new URL(uri).protocol}//`; - } catch { - const matched = uri.match(/^[A-Za-z][A-Za-z0-9+.-]*:\/\//); - return matched?.[0] ?? REDACTED; - } - }; - pluginConfig.remoteConfigurations = Object.fromEntries( - Object.entries(pluginConfig.remoteConfigurations || {}).map(([id, config]) => [ - id, - { - ...config, - uri: toSchemeOnly(config.uri), - }, - ]) - ); - 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); - pluginConfig.jwtKey = redact(pluginConfig.jwtKey); - pluginConfig.jwtSub = redact(pluginConfig.jwtSub); - pluginConfig.jwtKid = redact(pluginConfig.jwtKid); - pluginConfig.bucketCustomHeaders = redact(pluginConfig.bucketCustomHeaders); - pluginConfig.couchDB_CustomHeaders = redact(pluginConfig.couchDB_CustomHeaders); - pluginConfig.P2P_turnCredential = redact(pluginConfig.P2P_turnCredential); - pluginConfig.P2P_turnUsername = redact(pluginConfig.P2P_turnUsername); - pluginConfig.P2P_turnServers = `(${pluginConfig.P2P_turnServers.split(",").length} servers configured)`; - const endpoint = pluginConfig.endpoint; - if (endpoint == "") { - pluginConfig.endpoint = "Not configured or AWS"; - } else { - const endpointScheme = pluginConfig.endpoint.startsWith("http:") - ? "(HTTP)" - : pluginConfig.endpoint.startsWith("https:") - ? "(HTTPS)" - : ""; - pluginConfig.endpoint = `${endpoint.indexOf(".r2.cloudflarestorage.") !== -1 ? "R2" : "self-hosted?"}(${endpointScheme})`; - } - const obsidianInfo = { - navigator: navigator.userAgent, - fileSystem: this.core.services.vault.isStorageInsensitive() ? "insensitive" : "sensitive", - }; - const msgConfig = `# ---- Obsidian info ---- -${stringifyYaml(obsidianInfo)} ---- -# ---- remote config ---- -${stringifyYaml(responseConfig)} ---- -# ---- Plug-in config ---- -${stringifyYaml({ - version: this.manifestVersion, - ...pluginConfig, -})}`; - console.log(msgConfig); - if ((await this.services.UI.promptCopyToClipboard("Generated report", msgConfig)) == true) { - // await navigator.clipboard.writeText(msgConfig); - // Logger( - // `Generated report has been copied to clipboard. Please report the issue with this! Thank you for your cooperation!`, - // LOG_LEVEL_NOTICE - // ); - } + await this.app.commands.executeCommandById("obsidian-livesync:dump-debug-info"); }) ); new Setting(paneEl) diff --git a/src/modules/features/SettingDialogue/PaneSetup.ts b/src/modules/features/SettingDialogue/PaneSetup.ts index de996bc..4e365c9 100644 --- a/src/modules/features/SettingDialogue/PaneSetup.ts +++ b/src/modules/features/SettingDialogue/PaneSetup.ts @@ -121,13 +121,13 @@ export function paneSetup( const repo = "vrtmrz/obsidian-livesync"; const topPath = $msg("obsidianLiveSyncSettingTab.linkTroubleshooting"); const rawRepoURI = `https://raw.githubusercontent.com/${repo}/main`; - this.createEl( - paneEl, - "div", - "", - (el) => - (el.innerHTML = `${$msg("obsidianLiveSyncSettingTab.linkOpenInBrowser")}`) - ); + this.createEl(paneEl, "div", "", (el) => { + el.createEl("a", { text: $msg("obsidianLiveSyncSettingTab.linkOpenInBrowser") }, (anchor) => { + anchor.href = `https://github.com/${repo}/blob/main${topPath}`; + anchor.target = "_blank"; + anchor.rel = "noopener"; + }); + }); const troubleShootEl = this.createEl(paneEl, "div", { text: "", cls: "sls-troubleshoot-preview", diff --git a/src/modules/features/SettingDialogue/utilFixCouchDBSetting.ts b/src/modules/features/SettingDialogue/utilFixCouchDBSetting.ts index 041c923..d061643 100644 --- a/src/modules/features/SettingDialogue/utilFixCouchDBSetting.ts +++ b/src/modules/features/SettingDialogue/utilFixCouchDBSetting.ts @@ -13,7 +13,7 @@ export const checkConfig = async ( Logger($msg("obsidianLiveSyncSettingTab.logCheckingDbConfig"), LOG_LEVEL_INFO); let isSuccessful = true; const emptyDiv = createDiv(); - emptyDiv.innerHTML = ""; + emptyDiv.createSpan(); checkResultDiv?.replaceChildren(...[emptyDiv]); const addResult = (msg: string, classes?: string[]) => { const tmpDiv = createDiv(); @@ -21,7 +21,7 @@ export const checkConfig = async ( if (classes) { tmpDiv.addClasses(classes); } - tmpDiv.innerHTML = `${msg}`; + tmpDiv.textContent = msg; checkResultDiv?.appendChild(tmpDiv); }; try { @@ -47,9 +47,10 @@ export const checkConfig = async ( if (!checkResultDiv) return; const tmpDiv = createDiv(); tmpDiv.addClass("ob-btn-config-fix"); - tmpDiv.innerHTML = ``; + tmpDiv.createEl("label", { text: title }); + const fixButton = tmpDiv.createEl("button", { text: $msg("obsidianLiveSyncSettingTab.btnFix") }); const x = checkResultDiv.appendChild(tmpDiv); - x.querySelector("button")?.addEventListener("click", () => { + fixButton.addEventListener("click", () => { fireAndForget(async () => { Logger($msg("obsidianLiveSyncSettingTab.logCouchDbConfigSet", { title, key, value })); const res = await requestToCouchDBWithCredentials( diff --git a/src/modules/features/SetupManager.ts b/src/modules/features/SetupManager.ts index 1bd40be..3d69968 100644 --- a/src/modules/features/SetupManager.ts +++ b/src/modules/features/SetupManager.ts @@ -7,7 +7,7 @@ import { REMOTE_MINIO, REMOTE_P2P, } from "../../lib/src/common/types.ts"; -import { generatePatchObj, isObjectDifferent } from "../../lib/src/common/utils.ts"; +import { isObjectDifferent } from "@lib/common/utils.ts"; import Intro from "./SetupWizard/dialogs/Intro.svelte"; import SelectMethodNewUser from "./SetupWizard/dialogs/SelectMethodNewUser.svelte"; import SelectMethodExisting from "./SetupWizard/dialogs/SelectMethodExisting.svelte"; @@ -23,6 +23,7 @@ import SetupRemoteP2P from "./SetupWizard/dialogs/SetupRemoteP2P.svelte"; import SetupRemoteE2EE from "./SetupWizard/dialogs/SetupRemoteE2EE.svelte"; import { decodeSettingsFromQRCodeData } from "../../lib/src/API/processSetting.ts"; import { AbstractModule } from "../AbstractModule.ts"; +import { ConnectionStringParser } from "@lib/common/ConnectionString.ts"; /** * User modes for onboarding and setup @@ -194,8 +195,24 @@ export class SetupManager extends AbstractModule { return await this.onOnboard(userMode); } const newSetting = { ...currentSetting, ...p2pConf } as ObsidianLiveSyncSettings; + // Apply remoteConfigurations + if (newSetting.P2P_ActiveRemoteConfigurationId) { + const id = newSetting.P2P_ActiveRemoteConfigurationId; + const merged = { + ...newSetting, + ...p2pConf, + } as ObsidianLiveSyncSettings; + const uri = ConnectionStringParser.serialize({ type: "p2p", settings: merged }); + newSetting.remoteConfigurations[id] = { + ...newSetting.remoteConfigurations[id], + uri, + isEncrypted: false, + }; + newSetting.P2P_ActiveRemoteConfigurationId = id; + } if (activate) { newSetting.remoteType = REMOTE_P2P; + newSetting.activeConfigurationId = newSetting.P2P_ActiveRemoteConfigurationId; } return await this.onConfirmApplySettingsFromWizard(newSetting, userMode, activate); } @@ -285,9 +302,9 @@ export class SetupManager extends AbstractModule { this._log("No changes in settings detected. Skipping applying settings from wizard.", LOG_LEVEL_NOTICE); return true; } - const patch = generatePatchObj(this.settings, newConf); - console.log(`Changes:`); - console.dir(patch); + // const patch = generatePatchObj(this.settings, newConf); + // console.log(`Changes:`); + // console.dir(patch); if (!activate) { extra(); await this.applySetting(newConf, UserMode.ExistingUser); diff --git a/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte b/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte index 58ad125..57c0621 100644 --- a/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte +++ b/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte @@ -4,10 +4,10 @@ import Decision from "@/lib/src/UI/components/Decision.svelte"; import Instruction from "@/lib/src/UI/components/Instruction.svelte"; import UserDecisions from "@/lib/src/UI/components/UserDecisions.svelte"; - const TYPE_CLOSE = "close"; + const TYPE_CLOSE = "close"; type ResultType = typeof TYPE_CLOSE; type Props = { - setResult: (result: ResultType) => void; + setResult: (_result: ResultType) => void; }; const { setResult }: Props = $props(); diff --git a/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte b/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte index 2478521..b4dd9ea 100644 --- a/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte +++ b/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte @@ -48,6 +48,8 @@ bind:value={userType} > This is an advanced option for users who do not have a URI or who wish to configure detailed settings. + You can also select this option if you intend to use P2P (Peer-to-Peer) synchronisation + instead of a CouchDB/S3 server — P2P requires no server setup at all. diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte index b07c157..cd2020e 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte @@ -1,13 +1,13 @@