You've already forked obsidian-livesync
mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-08-10 22:11:45 +02:00
### New Feature
- Now, we can send custom headers to the server. - Authentication with JWT in CouchDB is now supported. ### Improved - The QR Code for set-up can be shown also from the setting dialogue now. - Conflict checking for preventing unexpected overwriting on the boot-up process has been quite faster. ### Fixed - Some bugs on Dev and Testing modules have been fixed.
This commit is contained in:
14
package-lock.json
generated
14
package-lock.json
generated
@@ -18,7 +18,7 @@
|
|||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"idb": "^8.0.2",
|
"idb": "^8.0.2",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
"octagonal-wheels": "^0.1.24",
|
"octagonal-wheels": "^0.1.25",
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
"svelte-check": "^4.1.4",
|
"svelte-check": "^4.1.4",
|
||||||
"trystero": "^0.20.1",
|
"trystero": "^0.20.1",
|
||||||
@@ -8514,9 +8514,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/octagonal-wheels": {
|
"node_modules/octagonal-wheels": {
|
||||||
"version": "0.1.24",
|
"version": "0.1.25",
|
||||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.24.tgz",
|
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.25.tgz",
|
||||||
"integrity": "sha512-ywzcq3FyW/xM37RhXkgwkERzgO6hG7uQHfpqHKcvPaT0H54e0/WoWEV65A2ttGp7Kxrq+UgXxVM1UT+KcSbPkA==",
|
"integrity": "sha512-WXe+AKgDlYU9FZ2/CbRXeTvpL0xknrL1NuG27+E6Vzg4RmeyWh8hM4lItGCiSqvAGbm8atn50WtaMSY7y5qfGg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"idb": "^8.0.2"
|
"idb": "^8.0.2"
|
||||||
@@ -17577,9 +17577,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"octagonal-wheels": {
|
"octagonal-wheels": {
|
||||||
"version": "0.1.24",
|
"version": "0.1.25",
|
||||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.24.tgz",
|
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.25.tgz",
|
||||||
"integrity": "sha512-ywzcq3FyW/xM37RhXkgwkERzgO6hG7uQHfpqHKcvPaT0H54e0/WoWEV65A2ttGp7Kxrq+UgXxVM1UT+KcSbPkA==",
|
"integrity": "sha512-WXe+AKgDlYU9FZ2/CbRXeTvpL0xknrL1NuG27+E6Vzg4RmeyWh8hM4lItGCiSqvAGbm8atn50WtaMSY7y5qfGg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"idb": "^8.0.2"
|
"idb": "^8.0.2"
|
||||||
}
|
}
|
||||||
|
@@ -77,7 +77,7 @@
|
|||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"idb": "^8.0.2",
|
"idb": "^8.0.2",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
"octagonal-wheels": "^0.1.24",
|
"octagonal-wheels": "^0.1.25",
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
"svelte-check": "^4.1.4",
|
"svelte-check": "^4.1.4",
|
||||||
"trystero": "^0.20.1",
|
"trystero": "^0.20.1",
|
||||||
|
@@ -10,6 +10,7 @@ export const EVENT_REQUEST_OPEN_SETTINGS = "request-open-settings";
|
|||||||
export const EVENT_REQUEST_OPEN_SETTING_WIZARD = "request-open-setting-wizard";
|
export const EVENT_REQUEST_OPEN_SETTING_WIZARD = "request-open-setting-wizard";
|
||||||
export const EVENT_REQUEST_OPEN_SETUP_URI = "request-open-setup-uri";
|
export const EVENT_REQUEST_OPEN_SETUP_URI = "request-open-setup-uri";
|
||||||
export const EVENT_REQUEST_COPY_SETUP_URI = "request-copy-setup-uri";
|
export const EVENT_REQUEST_COPY_SETUP_URI = "request-copy-setup-uri";
|
||||||
|
export const EVENT_REQUEST_SHOW_SETUP_QR = "request-show-setup-qr";
|
||||||
|
|
||||||
export const EVENT_REQUEST_RELOAD_SETTING_TAB = "reload-setting-tab";
|
export const EVENT_REQUEST_RELOAD_SETTING_TAB = "reload-setting-tab";
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ declare global {
|
|||||||
[EVENT_REQUEST_OPEN_P2P]: undefined;
|
[EVENT_REQUEST_OPEN_P2P]: undefined;
|
||||||
[EVENT_REQUEST_OPEN_SETUP_URI]: undefined;
|
[EVENT_REQUEST_OPEN_SETUP_URI]: undefined;
|
||||||
[EVENT_REQUEST_COPY_SETUP_URI]: undefined;
|
[EVENT_REQUEST_COPY_SETUP_URI]: undefined;
|
||||||
|
[EVENT_REQUEST_SHOW_SETUP_QR]: undefined;
|
||||||
[EVENT_REQUEST_RUN_DOCTOR]: string;
|
[EVENT_REQUEST_RUN_DOCTOR]: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
src/lib
2
src/lib
Submodule src/lib updated: dc4a27611d...ad3f7ee995
@@ -18,6 +18,7 @@ import {
|
|||||||
type AUTO_MERGED,
|
type AUTO_MERGED,
|
||||||
type RemoteDBSettings,
|
type RemoteDBSettings,
|
||||||
type TweakValues,
|
type TweakValues,
|
||||||
|
type CouchDBCredentials,
|
||||||
} from "./lib/src/common/types.ts";
|
} from "./lib/src/common/types.ts";
|
||||||
import { type FileEventItem } from "./common/types.ts";
|
import { type FileEventItem } from "./common/types.ts";
|
||||||
import { type SimpleStore } from "./lib/src/common/utils.ts";
|
import { type SimpleStore } from "./lib/src/common/utils.ts";
|
||||||
@@ -283,16 +284,14 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
|
|
||||||
$$connectRemoteCouchDB(
|
$$connectRemoteCouchDB(
|
||||||
uri: string,
|
uri: string,
|
||||||
auth: {
|
auth: CouchDBCredentials,
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
},
|
|
||||||
disableRequestURI: boolean,
|
disableRequestURI: boolean,
|
||||||
passphrase: string | false,
|
passphrase: string | false,
|
||||||
useDynamicIterationCount: boolean,
|
useDynamicIterationCount: boolean,
|
||||||
performSetup: boolean,
|
performSetup: boolean,
|
||||||
skipInfo: boolean,
|
skipInfo: boolean,
|
||||||
compression: boolean
|
compression: boolean,
|
||||||
|
customHeaders: Record<string, string>
|
||||||
): Promise<
|
): Promise<
|
||||||
| string
|
| string
|
||||||
| {
|
| {
|
||||||
|
@@ -81,9 +81,9 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
|||||||
|
|
||||||
this._log("Collecting local files on the DB", LOG_LEVEL_VERBOSE);
|
this._log("Collecting local files on the DB", LOG_LEVEL_VERBOSE);
|
||||||
const _DBEntries = [] as MetaEntry[];
|
const _DBEntries = [] as MetaEntry[];
|
||||||
// const _DBEntriesTask = [] as (() => Promise<MetaEntry | false>)[];
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for await (const doc of this.localDatabase.findAllNormalDocs()) {
|
// Fetch all documents from the database (including conflicts to prevent overwriting).
|
||||||
|
for await (const doc of this.localDatabase.findAllNormalDocs({ conflicts: true })) {
|
||||||
count++;
|
count++;
|
||||||
if (count % 25 == 0)
|
if (count % 25 == 0)
|
||||||
this._log(
|
this._log(
|
||||||
@@ -200,9 +200,8 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
|||||||
if (w && !(w.deleted || w._deleted)) {
|
if (w && !(w.deleted || w._deleted)) {
|
||||||
if (!this.core.$$isFileSizeExceeded(w.size)) {
|
if (!this.core.$$isFileSizeExceeded(w.size)) {
|
||||||
// Prevent applying the conflicted state to the storage.
|
// Prevent applying the conflicted state to the storage.
|
||||||
const conflicted = await this.core.databaseFileAccess.getConflictedRevs(path);
|
if (w._conflicts?.length ?? 0 > 0) {
|
||||||
if (conflicted.length > 0) {
|
this._log(`UPDATE STORAGE: ${path} has conflicts. skipped (x)`, LOG_LEVEL_INFO);
|
||||||
this._log(`UPDATE STORAGE: ${path} has conflicts. skipped`, LOG_LEVEL_INFO);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// await this.pullFile(path, undefined, false, undefined, false);
|
// await this.pullFile(path, undefined, false, undefined, false);
|
||||||
@@ -237,8 +236,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
|||||||
runAll("SYNC DATABASE AND STORAGE", fileMap, async (e) => {
|
runAll("SYNC DATABASE AND STORAGE", fileMap, async (e) => {
|
||||||
const { file, doc } = e;
|
const { file, doc } = e;
|
||||||
// Prevent applying the conflicted state to the storage.
|
// Prevent applying the conflicted state to the storage.
|
||||||
const conflicted = await this.core.databaseFileAccess.getConflictedRevs(file.path);
|
if (doc._conflicts?.length ?? 0 > 0) {
|
||||||
if (conflicted.length > 0) {
|
|
||||||
this._log(`SYNC DATABASE AND STORAGE: ${file.path} has conflicts. skipped`, LOG_LEVEL_INFO);
|
this._log(`SYNC DATABASE AND STORAGE: ${file.path} has conflicts. skipped`, LOG_LEVEL_INFO);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,16 @@
|
|||||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||||
import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||||
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
|
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
|
||||||
import { type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
import {
|
||||||
|
type CouchDBCredentials,
|
||||||
|
type EntryDoc,
|
||||||
|
type FilePathWithPrefix,
|
||||||
|
type JWTCredentials,
|
||||||
|
type JWTHeader,
|
||||||
|
type JWTParams,
|
||||||
|
type JWTPayload,
|
||||||
|
type PreparedJWT,
|
||||||
|
} from "../../lib/src/common/types.ts";
|
||||||
import { getPathFromTFile } from "../../common/utils.ts";
|
import { getPathFromTFile } from "../../common/utils.ts";
|
||||||
import {
|
import {
|
||||||
disableEncryption,
|
disableEncryption,
|
||||||
@@ -13,7 +22,9 @@ import {
|
|||||||
import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts";
|
import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts";
|
||||||
import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts";
|
import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts";
|
||||||
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
|
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
|
||||||
import { reactive, reactiveSource } from "octagonal-wheels/dataobject/reactive";
|
import { reactive, reactiveSource } from "octagonal-wheels/dataobject/reactive.js";
|
||||||
|
import { arrayBufferToBase64Single, writeString } from "../../lib/src/string_and_binary/convert.ts";
|
||||||
|
import { Refiner } from "octagonal-wheels/dataobject/Refiner";
|
||||||
|
|
||||||
setNoticeClass(Notice);
|
setNoticeClass(Notice);
|
||||||
|
|
||||||
@@ -99,29 +110,166 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_importKey(auth: JWTCredentials) {
|
||||||
|
if (auth.jwtAlgorithm == "HS256" || auth.jwtAlgorithm == "HS512") {
|
||||||
|
const key = (auth.jwtKey || "").trim();
|
||||||
|
if (key == "") {
|
||||||
|
throw new Error("JWT key is empty");
|
||||||
|
}
|
||||||
|
const binaryDerString = window.atob(key);
|
||||||
|
const binaryDer = new Uint8Array(binaryDerString.length);
|
||||||
|
for (let i = 0; i < binaryDerString.length; i++) {
|
||||||
|
binaryDer[i] = binaryDerString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
const hashName = auth.jwtAlgorithm == "HS256" ? "SHA-256" : "SHA-512";
|
||||||
|
return crypto.subtle.importKey("raw", binaryDer, { name: "HMAC", hash: { name: hashName } }, true, [
|
||||||
|
"sign",
|
||||||
|
]);
|
||||||
|
} else if (auth.jwtAlgorithm == "ES256" || auth.jwtAlgorithm == "ES512") {
|
||||||
|
const pem = auth.jwtKey
|
||||||
|
.replace(/-----BEGIN [^-]+-----/, "")
|
||||||
|
.replace(/-----END [^-]+-----/, "")
|
||||||
|
.replace(/\s+/g, "");
|
||||||
|
// const pem = key.replace(/\s/g, "");
|
||||||
|
const binaryDerString = window.atob(pem);
|
||||||
|
const binaryDer = new Uint8Array(binaryDerString.length);
|
||||||
|
for (let i = 0; i < binaryDerString.length; i++) {
|
||||||
|
binaryDer[i] = binaryDerString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
// const binaryDer = base64ToArrayBuffer(pem);
|
||||||
|
const namedCurve = auth.jwtAlgorithm == "ES256" ? "P-256" : "P-521";
|
||||||
|
const param = { name: "ECDSA", namedCurve };
|
||||||
|
return crypto.subtle.importKey("pkcs8", binaryDer, param, true, ["sign"]);
|
||||||
|
} else {
|
||||||
|
throw new Error("Supplied JWT algorithm is not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentCryptoKey = new Refiner<JWTCredentials, CryptoKey>({
|
||||||
|
evaluation: async (auth, previous) => {
|
||||||
|
return await this._importKey(auth);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
_jwt = new Refiner<JWTParams, PreparedJWT>({
|
||||||
|
evaluation: async (params, previous) => {
|
||||||
|
const encodedHeader = btoa(JSON.stringify(params.header));
|
||||||
|
const encodedPayload = btoa(JSON.stringify(params.payload));
|
||||||
|
const buff = `${encodedHeader}.${encodedPayload}`.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||||
|
|
||||||
|
const key = await this._currentCryptoKey.update(params.credentials).value;
|
||||||
|
let token = "";
|
||||||
|
if (params.header.alg == "ES256" || params.header.alg == "ES512") {
|
||||||
|
const jwt = await crypto.subtle.sign(
|
||||||
|
{ name: "ECDSA", hash: { name: "SHA-256" } },
|
||||||
|
key,
|
||||||
|
writeString(buff)
|
||||||
|
);
|
||||||
|
token = (await arrayBufferToBase64Single(jwt))
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=/g, "");
|
||||||
|
} else if (params.header.alg == "HS256" || params.header.alg == "HS512") {
|
||||||
|
const jwt = await crypto.subtle.sign(
|
||||||
|
{ name: "HMAC", hash: { name: params.header.alg } },
|
||||||
|
key,
|
||||||
|
writeString(buff)
|
||||||
|
);
|
||||||
|
token = (await arrayBufferToBase64Single(jwt))
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=/g, "");
|
||||||
|
} else {
|
||||||
|
throw new Error("JWT algorithm is not supported.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...params,
|
||||||
|
token: `${buff}.${token}`,
|
||||||
|
} as PreparedJWT;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
_jwtParams = new Refiner<JWTCredentials, JWTParams>({
|
||||||
|
evaluation(source, previous) {
|
||||||
|
const kid = source.jwtKid || undefined;
|
||||||
|
const sub = (source.jwtSub || "").trim();
|
||||||
|
if (sub == "") {
|
||||||
|
throw new Error("JWT sub is empty");
|
||||||
|
}
|
||||||
|
const algorithm = source.jwtAlgorithm || "";
|
||||||
|
if (!algorithm) {
|
||||||
|
throw new Error("JWT algorithm is not configured.");
|
||||||
|
}
|
||||||
|
if (algorithm != "HS256" && algorithm != "HS512" && algorithm != "ES256" && algorithm != "ES512") {
|
||||||
|
throw new Error("JWT algorithm is not supported.");
|
||||||
|
}
|
||||||
|
const header: JWTHeader = {
|
||||||
|
alg: source.jwtAlgorithm || "HS256",
|
||||||
|
typ: "JWT",
|
||||||
|
kid,
|
||||||
|
};
|
||||||
|
const iat = ~~(new Date().getTime() / 1000);
|
||||||
|
const exp = iat + (source.jwtExpDuration || 5) * 60; // 5 minutes
|
||||||
|
const payload = {
|
||||||
|
exp,
|
||||||
|
iat,
|
||||||
|
sub: source.jwtSub || "",
|
||||||
|
"_couchdb.roles": ["_admin"],
|
||||||
|
} satisfies JWTPayload;
|
||||||
|
return {
|
||||||
|
header,
|
||||||
|
payload,
|
||||||
|
credentials: source,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
shouldUpdate(isDifferent, source, previous) {
|
||||||
|
if (isDifferent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!previous) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// if expired.
|
||||||
|
const d = ~~(new Date().getTime() / 1000);
|
||||||
|
if (previous.payload.exp < d) {
|
||||||
|
// console.warn(`jwt expired ${previous.payload.exp} < ${d}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
async $$connectRemoteCouchDB(
|
async $$connectRemoteCouchDB(
|
||||||
uri: string,
|
uri: string,
|
||||||
auth: { username: string; password: string },
|
auth: CouchDBCredentials,
|
||||||
disableRequestURI: boolean,
|
disableRequestURI: boolean,
|
||||||
passphrase: string | false,
|
passphrase: string | false,
|
||||||
useDynamicIterationCount: boolean,
|
useDynamicIterationCount: boolean,
|
||||||
performSetup: boolean,
|
performSetup: boolean,
|
||||||
skipInfo: boolean,
|
skipInfo: boolean,
|
||||||
compression: boolean
|
compression: boolean,
|
||||||
|
customHeaders: Record<string, string>
|
||||||
): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
||||||
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
||||||
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
||||||
if (uri.indexOf(" ") !== -1) return "Remote URI and database name could not contain spaces.";
|
if (uri.indexOf(" ") !== -1) return "Remote URI and database name could not contain spaces.";
|
||||||
const userNameAndPassword = auth.username && auth.password ? `${auth.username}:${auth.password}` : "";
|
let authHeader = "";
|
||||||
if (this.authHeaderSource.value != userNameAndPassword) {
|
if ("username" in auth) {
|
||||||
this.authHeaderSource.value = userNameAndPassword;
|
const userNameAndPassword = auth.username && auth.password ? `${auth.username}:${auth.password}` : "";
|
||||||
|
if (this.authHeaderSource.value != userNameAndPassword) {
|
||||||
|
this.authHeaderSource.value = userNameAndPassword;
|
||||||
|
}
|
||||||
|
authHeader = this.authHeader.value;
|
||||||
|
} else if ("jwtAlgorithm" in auth) {
|
||||||
|
const params = await this._jwtParams.update(auth).value;
|
||||||
|
const jwt = await this._jwt.update(params).value;
|
||||||
|
const token = jwt.token;
|
||||||
|
authHeader = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
const authHeader = this.authHeader.value;
|
|
||||||
// const _this = this;
|
|
||||||
|
|
||||||
const conf: PouchDB.HttpAdapter.HttpAdapterConfiguration = {
|
const conf: PouchDB.HttpAdapter.HttpAdapterConfiguration = {
|
||||||
adapter: "http",
|
adapter: "http",
|
||||||
auth,
|
auth: "username" in auth ? auth : undefined,
|
||||||
skip_setup: !performSetup,
|
skip_setup: !performSetup,
|
||||||
fetch: async (url: string | Request, opts?: RequestInit) => {
|
fetch: async (url: string | Request, opts?: RequestInit) => {
|
||||||
let size = "";
|
let size = "";
|
||||||
@@ -146,9 +294,18 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
// --> native Fetch API.
|
// --> native Fetch API.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.settings.enableDebugTools) {
|
if (customHeaders) {
|
||||||
// Issue #407
|
for (const [key, value] of Object.entries(customHeaders)) {
|
||||||
(opts!.headers as Headers).append("ngrok-skip-browser-warning", "123");
|
if (key && value) {
|
||||||
|
(opts!.headers as Headers).append(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// // Issue #407
|
||||||
|
// (opts!.headers as Headers).append("ngrok-skip-browser-warning", "123");
|
||||||
|
}
|
||||||
|
// debugger;
|
||||||
|
if (!("username" in auth)) {
|
||||||
|
(opts!.headers as Headers).append("authorization", authHeader);
|
||||||
}
|
}
|
||||||
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
||||||
const response: Response = await fetch(url, opts);
|
const response: Response = await fetch(url, opts);
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { fireAndForget } from "octagonal-wheels/promises";
|
import { delay, fireAndForget } from "octagonal-wheels/promises";
|
||||||
import { __onMissingTranslation } from "../../lib/src/common/i18n";
|
import { __onMissingTranslation } from "../../lib/src/common/i18n";
|
||||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||||
import { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
import { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||||
@@ -6,6 +6,7 @@ import { eventHub } from "../../common/events";
|
|||||||
import { enableTestFunction } from "./devUtil/testUtils.ts";
|
import { enableTestFunction } from "./devUtil/testUtils.ts";
|
||||||
import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts";
|
import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
import type { FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||||
|
|
||||||
export class ModuleDev extends AbstractObsidianModule implements IObsidianModule {
|
export class ModuleDev extends AbstractObsidianModule implements IObsidianModule {
|
||||||
$everyOnloadStart(): Promise<boolean> {
|
$everyOnloadStart(): Promise<boolean> {
|
||||||
@@ -98,9 +99,41 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
|
|||||||
}
|
}
|
||||||
async $everyOnLayoutReady(): Promise<boolean> {
|
async $everyOnLayoutReady(): Promise<boolean> {
|
||||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||||
if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) {
|
// if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) {
|
||||||
void this.core.$$showView(VIEW_TYPE_TEST);
|
// void this.core.$$showView(VIEW_TYPE_TEST);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
this.addCommand({
|
||||||
|
id: "test-create-conflict",
|
||||||
|
name: "Create conflict",
|
||||||
|
callback: async () => {
|
||||||
|
const filename = "test-create-conflict.md";
|
||||||
|
const content = `# Test create conflict\n\n`;
|
||||||
|
const w = await this.core.databaseFileAccess.store({
|
||||||
|
name: filename as FilePathWithPrefix,
|
||||||
|
path: filename as FilePathWithPrefix,
|
||||||
|
body: new Blob([content], { type: "text/markdown" }),
|
||||||
|
stat: {
|
||||||
|
ctime: new Date().getTime(),
|
||||||
|
mtime: new Date().getTime(),
|
||||||
|
size: content.length,
|
||||||
|
type: "file",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (w) {
|
||||||
|
const id = await this.core.$$path2id(filename as FilePathWithPrefix);
|
||||||
|
const f = await this.core.localDatabase.getRaw(id);
|
||||||
|
console.log(f);
|
||||||
|
console.log(f._rev);
|
||||||
|
const revConflict = f._rev.split("-")[0] + "-" + (parseInt(f._rev.split("-")[1]) + 1).toString();
|
||||||
|
console.log(await this.core.localDatabase.bulkDocsRaw([f], { new_edits: false }));
|
||||||
|
console.log(
|
||||||
|
await this.core.localDatabase.bulkDocsRaw([{ ...f, _rev: revConflict }], { new_edits: false })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await delay(1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
testResults = writable<[boolean, string, string][]>([]);
|
testResults = writable<[boolean, string, string][]>([]);
|
||||||
|
@@ -160,6 +160,10 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _dumpFileList(outFile?: string) {
|
async _dumpFileList(outFile?: string) {
|
||||||
|
if (!this.core || !this.core.storageAccess) {
|
||||||
|
this._log("No storage access", LOG_LEVEL_INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const files = this.core.storageAccess.getFiles();
|
const files = this.core.storageAccess.getFiles();
|
||||||
const out = [] as any[];
|
const out = [] as any[];
|
||||||
const webcrypto = await getWebCrypto();
|
const webcrypto = await getWebCrypto();
|
||||||
|
@@ -94,6 +94,14 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
region: settings.region,
|
region: settings.region,
|
||||||
secretKey: settings.secretKey,
|
secretKey: settings.secretKey,
|
||||||
useCustomRequestHandler: settings.useCustomRequestHandler,
|
useCustomRequestHandler: settings.useCustomRequestHandler,
|
||||||
|
bucketCustomHeaders: settings.bucketCustomHeaders,
|
||||||
|
couchDB_CustomHeaders: settings.couchDB_CustomHeaders,
|
||||||
|
useJWT: settings.useJWT,
|
||||||
|
jwtKey: settings.jwtKey,
|
||||||
|
jwtAlgorithm: settings.jwtAlgorithm,
|
||||||
|
jwtKid: settings.jwtKid,
|
||||||
|
jwtExpDuration: settings.jwtExpDuration,
|
||||||
|
jwtSub: settings.jwtSub,
|
||||||
};
|
};
|
||||||
settings.encryptedCouchDBConnection = await this.encryptConfigurationItem(
|
settings.encryptedCouchDBConnection = await this.encryptConfigurationItem(
|
||||||
JSON.stringify(connectionSetting),
|
JSON.stringify(connectionSetting),
|
||||||
|
@@ -191,6 +191,11 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
|||||||
delete saveData.couchDB_USER;
|
delete saveData.couchDB_USER;
|
||||||
delete saveData.couchDB_PASSWORD;
|
delete saveData.couchDB_PASSWORD;
|
||||||
delete saveData.passphrase;
|
delete saveData.passphrase;
|
||||||
|
delete saveData.jwtKey;
|
||||||
|
delete saveData.jwtKid;
|
||||||
|
delete saveData.jwtSub;
|
||||||
|
delete saveData.couchDB_CustomHeaders;
|
||||||
|
delete saveData.bucketCustomHeaders;
|
||||||
}
|
}
|
||||||
return saveData;
|
return saveData;
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,12 @@ import { configURIBase, configURIBaseQR } from "../../common/types.ts";
|
|||||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.js";
|
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.js";
|
||||||
import { decrypt, encrypt } from "../../lib/src/encryption/e2ee_v2.ts";
|
import { decrypt, encrypt } from "../../lib/src/encryption/e2ee_v2.ts";
|
||||||
import { fireAndForget } from "../../lib/src/common/utils.ts";
|
import { fireAndForget } from "../../lib/src/common/utils.ts";
|
||||||
import { EVENT_REQUEST_COPY_SETUP_URI, EVENT_REQUEST_OPEN_SETUP_URI, eventHub } from "../../common/events.ts";
|
import {
|
||||||
|
EVENT_REQUEST_COPY_SETUP_URI,
|
||||||
|
EVENT_REQUEST_OPEN_SETUP_URI,
|
||||||
|
EVENT_REQUEST_SHOW_SETUP_QR,
|
||||||
|
eventHub,
|
||||||
|
} from "../../common/events.ts";
|
||||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||||
import { decodeAnyArray, encodeAnyArray } from "../../common/utils.ts";
|
import { decodeAnyArray, encodeAnyArray } from "../../common/utils.ts";
|
||||||
import qrcode from "qrcode-generator";
|
import qrcode from "qrcode-generator";
|
||||||
@@ -54,6 +59,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
|||||||
});
|
});
|
||||||
eventHub.onEvent(EVENT_REQUEST_OPEN_SETUP_URI, () => fireAndForget(() => this.command_openSetupURI()));
|
eventHub.onEvent(EVENT_REQUEST_OPEN_SETUP_URI, () => fireAndForget(() => this.command_openSetupURI()));
|
||||||
eventHub.onEvent(EVENT_REQUEST_COPY_SETUP_URI, () => fireAndForget(() => this.command_copySetupURI()));
|
eventHub.onEvent(EVENT_REQUEST_COPY_SETUP_URI, () => fireAndForget(() => this.command_copySetupURI()));
|
||||||
|
eventHub.onEvent(EVENT_REQUEST_SHOW_SETUP_QR, () => fireAndForget(() => this.encodeQR()));
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
async encodeQR() {
|
async encodeQR() {
|
||||||
|
@@ -35,7 +35,7 @@ import {
|
|||||||
readAsBlob,
|
readAsBlob,
|
||||||
sizeToHumanReadable,
|
sizeToHumanReadable,
|
||||||
} from "../../../lib/src/common/utils.ts";
|
} from "../../../lib/src/common/utils.ts";
|
||||||
import { versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
import { arrayBufferToBase64Single, versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
||||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||||
import {
|
import {
|
||||||
balanceChunkPurgedDBs,
|
balanceChunkPurgedDBs,
|
||||||
@@ -72,6 +72,7 @@ import {
|
|||||||
EVENT_REQUEST_OPEN_SETUP_URI,
|
EVENT_REQUEST_OPEN_SETUP_URI,
|
||||||
EVENT_REQUEST_RELOAD_SETTING_TAB,
|
EVENT_REQUEST_RELOAD_SETTING_TAB,
|
||||||
EVENT_REQUEST_RUN_DOCTOR,
|
EVENT_REQUEST_RUN_DOCTOR,
|
||||||
|
EVENT_REQUEST_SHOW_SETUP_QR,
|
||||||
eventHub,
|
eventHub,
|
||||||
} from "../../../common/events.ts";
|
} from "../../../common/events.ts";
|
||||||
import { skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
import { skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||||
@@ -81,6 +82,7 @@ import { HiddenFileSync } from "../../../features/HiddenFileSync/CmdHiddenFileSy
|
|||||||
import { EVENT_REQUEST_SHOW_HISTORY } from "../../../common/obsidianEvents.ts";
|
import { EVENT_REQUEST_SHOW_HISTORY } from "../../../common/obsidianEvents.ts";
|
||||||
import { LocalDatabaseMaintenance } from "../../../features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
|
import { LocalDatabaseMaintenance } from "../../../features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
|
||||||
import { mount } from "svelte";
|
import { mount } from "svelte";
|
||||||
|
import { getWebCrypto } from "../../../lib/src/mods.ts";
|
||||||
|
|
||||||
export type OnUpdateResult = {
|
export type OnUpdateResult = {
|
||||||
visibility?: boolean;
|
visibility?: boolean;
|
||||||
@@ -814,6 +816,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const enableOnlySyncDisabled = enableOnly(() => !isAnySyncEnabled());
|
const enableOnlySyncDisabled = enableOnly(() => !isAnySyncEnabled());
|
||||||
|
const combineOnUpdate = (func1: OnUpdateFunc, func2: OnUpdateFunc): OnUpdateFunc => {
|
||||||
|
return () => ({
|
||||||
|
...func1(),
|
||||||
|
...func2(),
|
||||||
|
});
|
||||||
|
};
|
||||||
const onlyOnP2POrCouchDB = () =>
|
const onlyOnP2POrCouchDB = () =>
|
||||||
({
|
({
|
||||||
visibility:
|
visibility:
|
||||||
@@ -979,7 +987,16 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
eventHub.emitEvent(EVENT_REQUEST_COPY_SETUP_URI);
|
eventHub.emitEvent(EVENT_REQUEST_COPY_SETUP_URI);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
new Setting(paneEl)
|
||||||
|
.setName($msg("Setup.ShowQRCode"))
|
||||||
|
.setDesc($msg("Setup.ShowQRCode.Desc"))
|
||||||
|
.addButton((text) => {
|
||||||
|
text.setButtonText($msg("Setup.ShowQRCode")).onClick(() => {
|
||||||
|
eventHub.emitEvent(EVENT_REQUEST_SHOW_SETUP_QR);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleReset")).then((paneEl) => {
|
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleReset")).then((paneEl) => {
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
.setName($msg("obsidianLiveSyncSettingTab.nameDiscardSettings"))
|
.setName($msg("obsidianLiveSyncSettingTab.nameDiscardSettings"))
|
||||||
@@ -1444,6 +1461,10 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
new Setting(paneEl).autoWireText("bucket", { holdValue: true });
|
new Setting(paneEl).autoWireText("bucket", { holdValue: true });
|
||||||
|
|
||||||
new Setting(paneEl).autoWireToggle("useCustomRequestHandler", { holdValue: true });
|
new Setting(paneEl).autoWireToggle("useCustomRequestHandler", { holdValue: true });
|
||||||
|
new Setting(paneEl).autoWireTextArea("bucketCustomHeaders", {
|
||||||
|
holdValue: true,
|
||||||
|
placeHolder: "x-custom-header: value\n x-custom-header2: value2",
|
||||||
|
});
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
.setName($msg("obsidianLiveSyncSettingTab.nameTestConnection"))
|
.setName($msg("obsidianLiveSyncSettingTab.nameTestConnection"))
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
@@ -1465,6 +1486,7 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
"secretKey",
|
"secretKey",
|
||||||
"bucket",
|
"bucket",
|
||||||
"useCustomRequestHandler",
|
"useCustomRequestHandler",
|
||||||
|
"bucketCustomHeaders",
|
||||||
])
|
])
|
||||||
.addOnUpdate(onlyOnMinIO);
|
.addOnUpdate(onlyOnMinIO);
|
||||||
});
|
});
|
||||||
@@ -1511,20 +1533,119 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
holdValue: true,
|
holdValue: true,
|
||||||
onUpdate: enableOnlySyncDisabled,
|
onUpdate: enableOnlySyncDisabled,
|
||||||
});
|
});
|
||||||
new Setting(paneEl).autoWireText("couchDB_USER", {
|
new Setting(paneEl).autoWireToggle("useJWT", {
|
||||||
holdValue: true,
|
holdValue: true,
|
||||||
onUpdate: enableOnlySyncDisabled,
|
onUpdate: enableOnlySyncDisabled,
|
||||||
});
|
});
|
||||||
|
new Setting(paneEl).autoWireText("couchDB_USER", {
|
||||||
|
holdValue: true,
|
||||||
|
onUpdate: combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => !this.editingSettings.useJWT)
|
||||||
|
),
|
||||||
|
});
|
||||||
new Setting(paneEl).autoWireText("couchDB_PASSWORD", {
|
new Setting(paneEl).autoWireText("couchDB_PASSWORD", {
|
||||||
holdValue: true,
|
holdValue: true,
|
||||||
isPassword: true,
|
isPassword: true,
|
||||||
onUpdate: enableOnlySyncDisabled,
|
onUpdate: combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => !this.editingSettings.useJWT)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const algorithms = {
|
||||||
|
["HS256"]: "HS256",
|
||||||
|
["HS512"]: "HS512",
|
||||||
|
["ES256"]: "ES256",
|
||||||
|
["ES512"]: "ES512",
|
||||||
|
} as const;
|
||||||
|
new Setting(paneEl).autoWireDropDown("jwtAlgorithm", {
|
||||||
|
options: algorithms,
|
||||||
|
onUpdate: combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => this.editingSettings.useJWT)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
new Setting(paneEl).autoWireTextArea("jwtKey", {
|
||||||
|
holdValue: true,
|
||||||
|
onUpdate: combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => this.editingSettings.useJWT)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let generatedKeyDivEl: HTMLDivElement;
|
||||||
|
new Setting(paneEl)
|
||||||
|
.setDesc("Generate ES256 Keypair for testing")
|
||||||
|
.addButton((button) =>
|
||||||
|
button.setButtonText("Generate").onClick(async () => {
|
||||||
|
const crypto = await getWebCrypto();
|
||||||
|
const keyPair = await crypto.subtle.generateKey(
|
||||||
|
{ name: "ECDSA", namedCurve: "P-256" },
|
||||||
|
true,
|
||||||
|
["sign", "verify"]
|
||||||
|
);
|
||||||
|
const pubKey = await crypto.subtle.exportKey("spki", keyPair.publicKey);
|
||||||
|
const privateKey = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
||||||
|
const encodedPublicKey = await arrayBufferToBase64Single(pubKey);
|
||||||
|
const encodedPrivateKey = await arrayBufferToBase64Single(privateKey);
|
||||||
|
|
||||||
|
const privateKeyPem = `> -----BEGIN PRIVATE KEY-----\n> ${encodedPrivateKey}\n> -----END PRIVATE KEY-----`;
|
||||||
|
const publicKeyPem = `> -----BEGIN PUBLIC KEY-----\\n${encodedPublicKey}\\n-----END PUBLIC KEY-----`;
|
||||||
|
|
||||||
|
const title = $msg("Setting.GenerateKeyPair.Title");
|
||||||
|
const msg = $msg("Setting.GenerateKeyPair.Desc", {
|
||||||
|
public_key: publicKeyPem,
|
||||||
|
private_key: privateKeyPem,
|
||||||
|
});
|
||||||
|
await MarkdownRenderer.render(
|
||||||
|
this.plugin.app,
|
||||||
|
"## " + title + "\n\n" + msg,
|
||||||
|
generatedKeyDivEl,
|
||||||
|
"/",
|
||||||
|
this.plugin
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.addOnUpdate(
|
||||||
|
combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => this.editingSettings.useJWT)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
generatedKeyDivEl = this.createEl(
|
||||||
|
paneEl,
|
||||||
|
"div",
|
||||||
|
{ text: "" },
|
||||||
|
(el) => {},
|
||||||
|
visibleOnly(() => this.editingSettings.useJWT)
|
||||||
|
);
|
||||||
|
|
||||||
|
new Setting(paneEl).autoWireText("jwtKid", {
|
||||||
|
holdValue: true,
|
||||||
|
onUpdate: combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => this.editingSettings.useJWT)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
new Setting(paneEl).autoWireText("jwtSub", {
|
||||||
|
holdValue: true,
|
||||||
|
onUpdate: combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => this.editingSettings.useJWT)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
new Setting(paneEl).autoWireNumeric("jwtExpDuration", {
|
||||||
|
holdValue: true,
|
||||||
|
onUpdate: combineOnUpdate(
|
||||||
|
enableOnlySyncDisabled,
|
||||||
|
visibleOnly(() => this.editingSettings.useJWT)
|
||||||
|
),
|
||||||
});
|
});
|
||||||
new Setting(paneEl).autoWireText("couchDB_DBNAME", {
|
new Setting(paneEl).autoWireText("couchDB_DBNAME", {
|
||||||
holdValue: true,
|
holdValue: true,
|
||||||
onUpdate: enableOnlySyncDisabled,
|
onUpdate: enableOnlySyncDisabled,
|
||||||
});
|
});
|
||||||
|
new Setting(paneEl).autoWireTextArea("couchDB_CustomHeaders", { holdValue: true });
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
.setName($msg("obsidianLiveSyncSettingTab.nameTestDatabaseConnection"))
|
.setName($msg("obsidianLiveSyncSettingTab.nameTestDatabaseConnection"))
|
||||||
.setClass("wizardHidden")
|
.setClass("wizardHidden")
|
||||||
@@ -1562,6 +1683,13 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
"couchDB_USER",
|
"couchDB_USER",
|
||||||
"couchDB_PASSWORD",
|
"couchDB_PASSWORD",
|
||||||
"couchDB_DBNAME",
|
"couchDB_DBNAME",
|
||||||
|
"jwtAlgorithm",
|
||||||
|
"jwtExpDuration",
|
||||||
|
"jwtKey",
|
||||||
|
"jwtSub",
|
||||||
|
"jwtKid",
|
||||||
|
"useJWT",
|
||||||
|
"couchDB_CustomHeaders",
|
||||||
])
|
])
|
||||||
.addOnUpdate(onlyOnCouchDB);
|
.addOnUpdate(onlyOnCouchDB);
|
||||||
});
|
});
|
||||||
@@ -1985,7 +2113,6 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
LEVEL_ADVANCED
|
LEVEL_ADVANCED
|
||||||
).then((paneEl) => {
|
).then((paneEl) => {
|
||||||
paneEl.addClass("wizardHidden");
|
paneEl.addClass("wizardHidden");
|
||||||
|
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
.autoWireText("settingSyncFile", { holdValue: true })
|
.autoWireText("settingSyncFile", { holdValue: true })
|
||||||
.addApplyButton(["settingSyncFile"]);
|
.addApplyButton(["settingSyncFile"]);
|
||||||
@@ -2330,6 +2457,11 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
|||||||
pluginConfig.P2P_passphrase = redact(pluginConfig.P2P_passphrase);
|
pluginConfig.P2P_passphrase = redact(pluginConfig.P2P_passphrase);
|
||||||
pluginConfig.P2P_roomID = redact(pluginConfig.P2P_roomID);
|
pluginConfig.P2P_roomID = redact(pluginConfig.P2P_roomID);
|
||||||
pluginConfig.P2P_relays = redact(pluginConfig.P2P_relays);
|
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);
|
||||||
const endpoint = pluginConfig.endpoint;
|
const endpoint = pluginConfig.endpoint;
|
||||||
if (endpoint == "") {
|
if (endpoint == "") {
|
||||||
pluginConfig.endpoint = "Not configured or AWS";
|
pluginConfig.endpoint = "Not configured or AWS";
|
||||||
@@ -3383,6 +3515,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
const region = this.plugin.settings.region;
|
const region = this.plugin.settings.region;
|
||||||
const endpoint = this.plugin.settings.endpoint;
|
const endpoint = this.plugin.settings.endpoint;
|
||||||
const useCustomRequestHandler = this.plugin.settings.useCustomRequestHandler;
|
const useCustomRequestHandler = this.plugin.settings.useCustomRequestHandler;
|
||||||
|
const customHeaders = this.plugin.settings.bucketCustomHeaders;
|
||||||
return new JournalSyncMinio(
|
return new JournalSyncMinio(
|
||||||
id,
|
id,
|
||||||
key,
|
key,
|
||||||
@@ -3391,7 +3524,8 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
this.plugin.simpleStore,
|
this.plugin.simpleStore,
|
||||||
this.plugin,
|
this.plugin,
|
||||||
useCustomRequestHandler,
|
useCustomRequestHandler,
|
||||||
region
|
region,
|
||||||
|
customHeaders
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
async resetRemoteBucket() {
|
async resetRemoteBucket() {
|
||||||
|
@@ -444,3 +444,11 @@ span.ls-mark-cr::after {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
max-width: max-content;
|
max-width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sls-keypair pre {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user