mirror of
https://github.com/winters27/obsidian-byoc.git
synced 2026-07-22 06:53:46 +00:00
chore(lint): mirror ReviewBot config + fix OAuth DefinePlugin regression
Add eslint.bot.config.mjs that runs the obsidianmd ReviewBot's stricter ruleset locally (require-await, no-misused-promises, restrict-template-expressions, ui/sentence-case, undescribed-disable checks, plus its disallow-list of eslint-disable rules). Sweeps every remaining Required-tier finding the bot flagged: async-no-await across fs*/settings*/main.ts, sentence-case UI text, undescribed eslint-disable directives, encryptRClone.worker.ts any-typing and never-template, plus the worker's async-callback-as-event-listener-return mismatch. Also fixes a build regression introduced by the same sweep: rewriting baseTypes.ts from `globalThis.DEFAULT_X` access to a bare-identifier `typeof` guard broke webpack's DefinePlugin substitution because the plugin keys still pattern-matched on "globalThis.X". Result was every OAuth client_id (Dropbox, OneDrive, OneDriveFull, GoogleDrive, Box, pCloud, Yandex, Koofr) shipped as "" and OAuth flows returned "invalid client_id". DefinePlugin keys now match the bare identifier. Verified by grepping the deployed main.js for the pCloud client_id literal post-build.
This commit is contained in:
parent
4e55cd761b
commit
203238db84
38 changed files with 449 additions and 316 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -31,6 +31,12 @@ upstream_tracker.md
|
|||
upstream_issues.md
|
||||
issues*.json
|
||||
|
||||
# Lint output snapshots and one-shot fix scripts (local-only)
|
||||
lint-bot*.json
|
||||
lint-report.json
|
||||
sentence-case-fixes.json
|
||||
temp-fix-script*.js
|
||||
|
||||
# Ignore internal docs/markdown
|
||||
**/*.md
|
||||
!README.md
|
||||
|
|
|
|||
90
eslint.bot.config.mjs
Normal file
90
eslint.bot.config.mjs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Strict lint config that mirrors what obsidianmd ReviewBot uses.
|
||||
// We pass this with --config so it's used instead of the relaxed local config.
|
||||
import tsparser from "@typescript-eslint/parser";
|
||||
import { defineConfig } from "eslint/config";
|
||||
import obsidianmd from "eslint-plugin-obsidianmd";
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
ignores: [
|
||||
"*.js",
|
||||
"*.cjs",
|
||||
"main.js",
|
||||
"927.main.js",
|
||||
"node_modules/**",
|
||||
"dist/**",
|
||||
"tests/**",
|
||||
"scripts/**",
|
||||
"docs/**",
|
||||
"esbuild.config.mjs",
|
||||
"esbuild.injecthelper.mjs",
|
||||
"eslint.config.mjs",
|
||||
"eslint.bot.config.mjs",
|
||||
"webpack.config.js",
|
||||
"vitest.config.ts",
|
||||
"biome.json",
|
||||
"versions.json",
|
||||
"tsconfig.json",
|
||||
"package-lock.json",
|
||||
"issues*.json",
|
||||
"lint-report.json",
|
||||
"lint-bot.json",
|
||||
],
|
||||
},
|
||||
...obsidianmd.configs.recommended,
|
||||
{
|
||||
files: ["package.json"],
|
||||
rules: {
|
||||
"obsidianmd/no-plugin-as-component": "off",
|
||||
"obsidianmd/no-tfile-tfolder-cast": "off",
|
||||
"obsidianmd/no-view-references-in-plugin": "off",
|
||||
"obsidianmd/prefer-instanceof": "off",
|
||||
"obsidianmd/prefer-active-doc": "off",
|
||||
"obsidianmd/no-unsupported-api": "off",
|
||||
"obsidianmd/object-assign": "off",
|
||||
"obsidianmd/prefer-file-manager-trash-file": "off",
|
||||
"obsidianmd/regex-lookbehind": "off",
|
||||
"obsidianmd/platform": "off",
|
||||
"obsidianmd/editor-drop-paste": "off",
|
||||
"obsidianmd/detach-leaves": "off",
|
||||
"obsidianmd/no-forbidden-elements": "off",
|
||||
"obsidianmd/no-static-styles-assignment": "off",
|
||||
"obsidianmd/prefer-active-window-timers": "off",
|
||||
"obsidianmd/prefer-abstract-input-suggest": "off",
|
||||
"obsidianmd/no-sample-code": "off",
|
||||
"obsidianmd/sample-names": "off",
|
||||
"obsidianmd/hardcoded-config-path": "off",
|
||||
"obsidianmd/prefer-get-language": "off",
|
||||
"obsidianmd/commands/no-command-in-command-id": "off",
|
||||
"obsidianmd/commands/no-command-in-command-name": "off",
|
||||
"obsidianmd/commands/no-default-hotkeys": "off",
|
||||
"obsidianmd/commands/no-plugin-id-in-command-id": "off",
|
||||
"obsidianmd/commands/no-plugin-name-in-command-name": "off",
|
||||
"obsidianmd/settings-tab/no-manual-html-headings": "off",
|
||||
"obsidianmd/settings-tab/no-problematic-settings-headings": "off",
|
||||
"obsidianmd/vault/iterate": "off",
|
||||
"obsidianmd/ui/sentence-case": "off",
|
||||
"obsidianmd/rule-custom-message": "off",
|
||||
// ReviewBot doesn't flag these in its actual output — keep the bot
|
||||
// config in sync with reality so we don't chase phantom errors.
|
||||
"depend/ban-dependencies": ["error", {
|
||||
presets: ["native", "microutilities", "preferred"],
|
||||
allowed: ["lodash", "emoji-regex", "dotenv", "builtin-modules", "rimraf", "readable-stream"],
|
||||
}],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: { project: "./tsconfig.json" },
|
||||
globals: { Buffer: "readonly" },
|
||||
},
|
||||
rules: {
|
||||
// Bot enforces these even though obsidianmd recommended turns them off.
|
||||
"@typescript-eslint/require-await": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
"@typescript-eslint/restrict-template-expressions": "error",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
@ -11,6 +11,9 @@
|
|||
"dev": "webpack --mode development --watch",
|
||||
"format": "npx @biomejs/biome check --write .",
|
||||
"clean": "npx rimraf main.js",
|
||||
"lint": "eslint .",
|
||||
"lint:bot": "eslint --config eslint.bot.config.mjs .",
|
||||
"lint:bot:fix": "eslint --config eslint.bot.config.mjs . --fix",
|
||||
"test": "mocha --require ./tests/_obsidianStub.cjs --import=tsx 'tests/**/*.ts'"
|
||||
},
|
||||
"browser": {
|
||||
|
|
@ -37,8 +40,10 @@
|
|||
"@types/mocha": "^10.0.7",
|
||||
"@types/mustache": "^4.2.5",
|
||||
"@types/node": "^20.14.12",
|
||||
"@types/path-browserify": "^1.0.3",
|
||||
"@types/picomatch": "^4.0.3",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/readable-stream": "^4.0.23",
|
||||
"builtin-modules": "^4.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^16.4.5",
|
||||
|
|
@ -102,6 +107,7 @@
|
|||
"picomatch": "^4.0.4",
|
||||
"process": "^0.11.10",
|
||||
"qrcode": "^1.5.3",
|
||||
"readable-stream": "^4.7.0",
|
||||
"rfc4648": "^1.5.3",
|
||||
"rimraf": "^6.0.1",
|
||||
"stream-browserify": "^3.0.0",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
import type { LangTypeAndAuto } from "./i18n";
|
||||
|
||||
/* global DEFAULT_DROPBOX_APP_KEY, DEFAULT_ONEDRIVE_CLIENT_ID, DEFAULT_ONEDRIVE_AUTHORITY, DEFAULT_GOOGLEDRIVE_CLIENT_ID, DEFAULT_GOOGLEDRIVE_CLIENT_SECRET, DEFAULT_BOX_CLIENT_ID, DEFAULT_BOX_CLIENT_SECRET, DEFAULT_PCLOUD_CLIENT_ID, DEFAULT_PCLOUD_CLIENT_SECRET, DEFAULT_YANDEXDISK_CLIENT_ID, DEFAULT_YANDEXDISK_CLIENT_SECRET, DEFAULT_KOOFR_CLIENT_ID, DEFAULT_KOOFR_CLIENT_SECRET */
|
||||
|
||||
/* global DEFAULT_DROPBOX_APP_KEY, DEFAULT_ONEDRIVE_CLIENT_ID, DEFAULT_ONEDRIVE_AUTHORITY, DEFAULT_GOOGLEDRIVE_CLIENT_ID, DEFAULT_GOOGLEDRIVE_CLIENT_SECRET, DEFAULT_BOX_CLIENT_ID, DEFAULT_BOX_CLIENT_SECRET, DEFAULT_PCLOUD_CLIENT_ID, DEFAULT_PCLOUD_CLIENT_SECRET, DEFAULT_YANDEXDISK_CLIENT_ID, DEFAULT_YANDEXDISK_CLIENT_SECRET, DEFAULT_KOOFR_CLIENT_ID, DEFAULT_KOOFR_CLIENT_SECRET */
|
||||
|
||||
declare global {
|
||||
var DEFAULT_DROPBOX_APP_KEY: string;
|
||||
var DEFAULT_ONEDRIVE_CLIENT_ID: string;
|
||||
|
|
@ -24,21 +28,19 @@ declare global {
|
|||
// These globals are injected by webpack's DefinePlugin at build time, not
|
||||
// related to popout windows. The activeWindow/activeDocument rule doesn't
|
||||
// apply to module-load-time constants.
|
||||
/* eslint-disable obsidianmd/prefer-active-doc */
|
||||
export const DROPBOX_APP_KEY = globalThis.DEFAULT_DROPBOX_APP_KEY || "";
|
||||
export const ONEDRIVE_CLIENT_ID = globalThis.DEFAULT_ONEDRIVE_CLIENT_ID || "";
|
||||
export const ONEDRIVE_AUTHORITY = globalThis.DEFAULT_ONEDRIVE_AUTHORITY || "https://login.microsoftonline.com/consumers/";
|
||||
export const GOOGLEDRIVE_CLIENT_ID = globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_ID || "";
|
||||
export const GOOGLEDRIVE_CLIENT_SECRET = globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_SECRET || "";
|
||||
export const BOX_CLIENT_ID = globalThis.DEFAULT_BOX_CLIENT_ID || "";
|
||||
export const BOX_CLIENT_SECRET = globalThis.DEFAULT_BOX_CLIENT_SECRET || "";
|
||||
export const PCLOUD_CLIENT_ID = globalThis.DEFAULT_PCLOUD_CLIENT_ID || "";
|
||||
export const PCLOUD_CLIENT_SECRET = globalThis.DEFAULT_PCLOUD_CLIENT_SECRET || "";
|
||||
export const YANDEXDISK_CLIENT_ID = globalThis.DEFAULT_YANDEXDISK_CLIENT_ID || "";
|
||||
export const YANDEXDISK_CLIENT_SECRET = globalThis.DEFAULT_YANDEXDISK_CLIENT_SECRET || "";
|
||||
export const KOOFR_CLIENT_ID = globalThis.DEFAULT_KOOFR_CLIENT_ID || "";
|
||||
export const KOOFR_CLIENT_SECRET = globalThis.DEFAULT_KOOFR_CLIENT_SECRET || "";
|
||||
/* eslint-enable obsidianmd/prefer-active-doc */
|
||||
export const DROPBOX_APP_KEY: string = typeof DEFAULT_DROPBOX_APP_KEY !== "undefined" ? DEFAULT_DROPBOX_APP_KEY : "";
|
||||
export const ONEDRIVE_CLIENT_ID: string = typeof DEFAULT_ONEDRIVE_CLIENT_ID !== "undefined" ? DEFAULT_ONEDRIVE_CLIENT_ID : "";
|
||||
export const ONEDRIVE_AUTHORITY: string = typeof DEFAULT_ONEDRIVE_AUTHORITY !== "undefined" ? DEFAULT_ONEDRIVE_AUTHORITY : "https://login.microsoftonline.com/consumers/";
|
||||
export const GOOGLEDRIVE_CLIENT_ID: string = typeof DEFAULT_GOOGLEDRIVE_CLIENT_ID !== "undefined" ? DEFAULT_GOOGLEDRIVE_CLIENT_ID : "";
|
||||
export const GOOGLEDRIVE_CLIENT_SECRET: string = typeof DEFAULT_GOOGLEDRIVE_CLIENT_SECRET !== "undefined" ? DEFAULT_GOOGLEDRIVE_CLIENT_SECRET : "";
|
||||
export const BOX_CLIENT_ID: string = typeof DEFAULT_BOX_CLIENT_ID !== "undefined" ? DEFAULT_BOX_CLIENT_ID : "";
|
||||
export const BOX_CLIENT_SECRET: string = typeof DEFAULT_BOX_CLIENT_SECRET !== "undefined" ? DEFAULT_BOX_CLIENT_SECRET : "";
|
||||
export const PCLOUD_CLIENT_ID: string = typeof DEFAULT_PCLOUD_CLIENT_ID !== "undefined" ? DEFAULT_PCLOUD_CLIENT_ID : "";
|
||||
export const PCLOUD_CLIENT_SECRET: string = typeof DEFAULT_PCLOUD_CLIENT_SECRET !== "undefined" ? DEFAULT_PCLOUD_CLIENT_SECRET : "";
|
||||
export const YANDEXDISK_CLIENT_ID: string = typeof DEFAULT_YANDEXDISK_CLIENT_ID !== "undefined" ? DEFAULT_YANDEXDISK_CLIENT_ID : "";
|
||||
export const YANDEXDISK_CLIENT_SECRET: string = typeof DEFAULT_YANDEXDISK_CLIENT_SECRET !== "undefined" ? DEFAULT_YANDEXDISK_CLIENT_SECRET : "";
|
||||
export const KOOFR_CLIENT_ID: string = typeof DEFAULT_KOOFR_CLIENT_ID !== "undefined" ? DEFAULT_KOOFR_CLIENT_ID : "";
|
||||
export const KOOFR_CLIENT_SECRET: string = typeof DEFAULT_KOOFR_CLIENT_SECRET !== "undefined" ? DEFAULT_KOOFR_CLIENT_SECRET : "";
|
||||
|
||||
export const DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export const decryptArrayBuffer = async (
|
|||
password: string,
|
||||
rounds: number = DEFAULT_ITER
|
||||
) => {
|
||||
const _prefix = arrBuf.slice(0, 8);
|
||||
|
||||
const salt = arrBuf.slice(8, 16);
|
||||
const derivedKey = await getKeyIVFromPassword(
|
||||
new Uint8Array(salt),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,21 @@
|
|||
import { Cipher as CipherRCloneCryptPack } from "@fyears/rclone-crypt";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
const ctx: WorkerGlobalScope = self as any;
|
||||
const ctx = self as unknown as DedicatedWorkerGlobalScope;
|
||||
|
||||
interface WorkerMessageData {
|
||||
action:
|
||||
| "prepare"
|
||||
| "encryptContent"
|
||||
| "decryptContent"
|
||||
| "encryptName"
|
||||
| "decryptName";
|
||||
dataKeyBuf?: ArrayBuffer;
|
||||
nameKeyBuf?: ArrayBuffer;
|
||||
nameTweakBuf?: ArrayBuffer;
|
||||
inputName?: string;
|
||||
inputContent?: ArrayBuffer;
|
||||
}
|
||||
|
||||
const workerNanoID = nanoid();
|
||||
const cipher = new CipherRCloneCryptPack("base64");
|
||||
|
|
@ -25,8 +39,10 @@ async function decryptContentBuf(input: ArrayBuffer) {
|
|||
return (await cipher.decryptData(new Uint8Array(input))).buffer;
|
||||
}
|
||||
|
||||
ctx.addEventListener("message", async (event: any) => {
|
||||
ctx.addEventListener("message", (event: MessageEvent) => {
|
||||
void (async () => {
|
||||
const port: MessagePort = event.ports[0];
|
||||
const data = event.data as WorkerMessageData;
|
||||
const {
|
||||
action,
|
||||
dataKeyBuf,
|
||||
|
|
@ -34,19 +50,7 @@ ctx.addEventListener("message", async (event: any) => {
|
|||
nameTweakBuf,
|
||||
inputName,
|
||||
inputContent,
|
||||
} = event.data as {
|
||||
action:
|
||||
| "prepare"
|
||||
| "encryptContent"
|
||||
| "decryptContent"
|
||||
| "encryptName"
|
||||
| "decryptName";
|
||||
dataKeyBuf?: ArrayBuffer;
|
||||
nameKeyBuf?: ArrayBuffer;
|
||||
nameTweakBuf?: ArrayBuffer;
|
||||
inputName?: string;
|
||||
inputContent?: ArrayBuffer;
|
||||
};
|
||||
} = data;
|
||||
|
||||
// console.debug(`worker [${workerNanoID}]: receiving action=${action}`);
|
||||
|
||||
|
|
@ -178,7 +182,8 @@ ctx.addEventListener("message", async (event: any) => {
|
|||
} else {
|
||||
port.postMessage({
|
||||
status: "error",
|
||||
error: `worker [${workerNanoID}]: unknown action=${action}`,
|
||||
error: `worker [${workerNanoID}]: unknown action=${String(action)}`,
|
||||
});
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
12
src/fsAll.ts
12
src/fsAll.ts
|
|
@ -75,8 +75,10 @@ export abstract class FakeFs {
|
|||
*
|
||||
* Default: throws. Override in providers that support OAuth folder picking.
|
||||
*/
|
||||
async listFoldersAtRoot(): Promise<string[]> {
|
||||
throw new Error(`[BYOC] listFoldersAtRoot not implemented for ${this.kind}`);
|
||||
listFoldersAtRoot(): Promise<string[]> {
|
||||
return Promise.reject(
|
||||
new Error(`[BYOC] listFoldersAtRoot not implemented for ${this.kind}`)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -85,7 +87,9 @@ export abstract class FakeFs {
|
|||
*
|
||||
* Default: throws. Override in providers that support OAuth folder picking.
|
||||
*/
|
||||
async createFolderAtRoot(name: string): Promise<void> {
|
||||
throw new Error(`[BYOC] createFolderAtRoot not implemented for ${this.kind}`);
|
||||
createFolderAtRoot(name: string): Promise<void> {
|
||||
return Promise.reject(
|
||||
new Error(`[BYOC] createFolderAtRoot not implemented for ${this.kind}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,17 +304,20 @@ export class FakeFsAzureBlobStorage extends FakeFs {
|
|||
return this.checkConnectCommonOps(callbackFunc);
|
||||
}
|
||||
|
||||
async getUserDisplayName(): Promise<string> {
|
||||
getUserDisplayName(): Promise<string> {
|
||||
if (!this.config.containerSasUrl) {
|
||||
return "Azure Blob Storage (not configured)";
|
||||
return Promise.resolve("Azure Blob Storage (not configured)");
|
||||
}
|
||||
return `Azure Blob: ${this.config.containerName || "container"}`;
|
||||
return Promise.resolve(
|
||||
`Azure Blob: ${this.config.containerName || "container"}`
|
||||
);
|
||||
}
|
||||
|
||||
async revokeAuth(): Promise<void> {
|
||||
revokeAuth(): Promise<void> {
|
||||
// SAS tokens don't have a revoke flow — user regenerates on Azure portal
|
||||
this.config.containerSasUrl = "";
|
||||
this.config.containerName = "";
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
allowEmptyFile(): boolean {
|
||||
|
|
|
|||
|
|
@ -652,7 +652,6 @@ export class FakeFsDropbox extends FakeFs {
|
|||
}
|
||||
|
||||
const mtimeFixed = Math.floor(mtime / 1000.0) * 1000;
|
||||
const _ctimeFixed = Math.floor(ctime / 1000.0) * 1000;
|
||||
const mtimeStr = new Date(mtimeFixed)
|
||||
.toISOString()
|
||||
.replace(/\.\d{3}Z$/, "Z");
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ export class FakeFsEncrypt extends FakeFs {
|
|||
ok: true,
|
||||
reason: "password_matched",
|
||||
};
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return {
|
||||
ok: false,
|
||||
reason: "password_or_method_not_matched_or_remote_not_encrypted",
|
||||
|
|
@ -418,10 +418,11 @@ export class FakeFsEncrypt extends FakeFs {
|
|||
return await this.innerFs.checkConnect(callbackFunc);
|
||||
}
|
||||
|
||||
async closeResources() {
|
||||
closeResources(): Promise<void> {
|
||||
if (this.method === "rclone-base64" && this.cipherRClone !== undefined) {
|
||||
this.cipherRClone.closeResources();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async encryptEntity(input: Entity): Promise<Entity> {
|
||||
|
|
@ -542,7 +543,7 @@ export class FakeFsEncrypt extends FakeFs {
|
|||
} else {
|
||||
throw Error(`cannot decrypt name=${name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
throw Error(`cannot decrypt name=${name}`);
|
||||
}
|
||||
} else if (name.startsWith(openssl.MAGIC_ENCRYPTED_PREFIX_BASE64URL)) {
|
||||
|
|
@ -556,7 +557,7 @@ export class FakeFsEncrypt extends FakeFs {
|
|||
} else {
|
||||
throw Error(`cannot decrypt name=${name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
throw Error(`cannot decrypt name=${name}`);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -181,16 +181,16 @@ export class FakeFsLocal extends FakeFs {
|
|||
}
|
||||
}
|
||||
}
|
||||
async checkConnect(callbackFunc?: (err: unknown) => unknown): Promise<boolean> {
|
||||
return true;
|
||||
checkConnect(callbackFunc?: (err: unknown) => unknown): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async getUserDisplayName(): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
getUserDisplayName(): Promise<string> {
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
async revokeAuth(): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
revokeAuth(): Promise<void> {
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
supportsRename(): boolean { return true; }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import type { Entity } from "./baseTypes";
|
||||
import { FakeFs } from "./fsAll";
|
||||
|
||||
const notImplemented = () =>
|
||||
Promise.reject(new Error("Method not implemented."));
|
||||
|
||||
export class FakeFsMock extends FakeFs {
|
||||
kind: "mock";
|
||||
|
||||
|
|
@ -9,53 +12,53 @@ export class FakeFsMock extends FakeFs {
|
|||
this.kind = "mock";
|
||||
}
|
||||
|
||||
async walk(): Promise<Entity[]> {
|
||||
throw new Error("Method not implemented.");
|
||||
walk(): Promise<Entity[]> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async walkPartial(): Promise<Entity[]> {
|
||||
return await this.walk();
|
||||
}
|
||||
|
||||
async stat(key: string): Promise<Entity> {
|
||||
throw new Error("Method not implemented.");
|
||||
stat(key: string): Promise<Entity> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async mkdir(key: string, mtime: number, ctime: number): Promise<Entity> {
|
||||
throw new Error("Method not implemented.");
|
||||
mkdir(key: string, mtime: number, ctime: number): Promise<Entity> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async writeFile(
|
||||
writeFile(
|
||||
key: string,
|
||||
content: ArrayBuffer,
|
||||
mtime: number,
|
||||
ctime: number
|
||||
): Promise<Entity> {
|
||||
throw new Error("Method not implemented.");
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async readFile(key: string): Promise<ArrayBuffer> {
|
||||
throw new Error("Method not implemented.");
|
||||
readFile(key: string): Promise<ArrayBuffer> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async rename(key1: string, key2: string): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
rename(key1: string, key2: string): Promise<void> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async rm(key: string): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
rm(key: string): Promise<void> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async checkConnect(callbackFunc?: (err: unknown) => unknown): Promise<boolean> {
|
||||
return await this.checkConnectCommonOps(callbackFunc);
|
||||
}
|
||||
|
||||
async getUserDisplayName(): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
getUserDisplayName(): Promise<string> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
async revokeAuth(): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
revokeAuth(): Promise<void> {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
supportsRename(): boolean { return true; }
|
||||
|
|
|
|||
|
|
@ -1089,14 +1089,14 @@ export class FakeFsOnedrive extends FakeFs {
|
|||
* https://docs.microsoft.com/en-us/graph/api/user-revokesigninsessions
|
||||
* https://docs.microsoft.com/en-us/graph/api/user-invalidateallrefreshtokens
|
||||
*/
|
||||
async revokeAuth() {
|
||||
revokeAuth(): Promise<void> {
|
||||
// await this._init();
|
||||
// await this._postJson("/me/revokeSignInSessions", {});
|
||||
throw new Error("Method not implemented.");
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
async getRevokeAddr() {
|
||||
return "https://account.live.com/consent/Manage";
|
||||
getRevokeAddr(): Promise<string> {
|
||||
return Promise.resolve("https://account.live.com/consent/Manage");
|
||||
}
|
||||
|
||||
allowEmptyFile(): boolean {
|
||||
|
|
|
|||
|
|
@ -705,12 +705,14 @@ export class FakeFsOnedriveFull extends FakeFs {
|
|||
return res.displayName ?? "<unknown>";
|
||||
}
|
||||
|
||||
async revokeAuth(): Promise<void> {
|
||||
throw new Error("Visit https://account.live.com/consent/Manage to revoke.");
|
||||
revokeAuth(): Promise<void> {
|
||||
return Promise.reject(
|
||||
new Error("Visit https://account.live.com/consent/Manage to revoke.")
|
||||
);
|
||||
}
|
||||
|
||||
async getRevokeAddr(): Promise<string> {
|
||||
return "https://account.live.com/consent/Manage";
|
||||
getRevokeAddr(): Promise<string> {
|
||||
return Promise.resolve("https://account.live.com/consent/Manage");
|
||||
}
|
||||
|
||||
supportsRename(): boolean { return true; }
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export interface AuthAllowFirstRes {
|
|||
/**
|
||||
* https://docs.pcloud.com/methods/oauth_2.0/authorize.html
|
||||
*/
|
||||
export const generateAuthUrl = async (hasCallback: boolean) => {
|
||||
export const generateAuthUrl = (hasCallback: boolean) => {
|
||||
const clientID = PCLOUD_CLIENT_ID;
|
||||
const state = nanoid();
|
||||
let authUrl = `https://my.pcloud.com/oauth2/authorize?response_type=code&client_id=${clientID}&state=${state}`;
|
||||
|
|
@ -381,12 +381,12 @@ export class FakeFsPCloud extends FakeFs {
|
|||
return this.client!;
|
||||
}
|
||||
|
||||
async _getAccessToken() {
|
||||
_getAccessToken(): Promise<string> {
|
||||
if (this.pCloudConfig.accessToken === "") {
|
||||
throw Error("The user has not manually auth yet.");
|
||||
return Promise.reject(Error("The user has not manually auth yet."));
|
||||
}
|
||||
|
||||
return this.pCloudConfig.accessToken;
|
||||
return Promise.resolve(this.pCloudConfig.accessToken);
|
||||
|
||||
// TODO: no expire date?
|
||||
// https://docs.pcloud.com/methods/intro/authentication.html
|
||||
|
|
@ -516,10 +516,6 @@ async _getAccessToken() {
|
|||
|
||||
await this._init();
|
||||
|
||||
const prevCachedEntity: PCloudEntity | undefined =
|
||||
this.keyToPCloudEntity[key];
|
||||
const _prevFileID: number | undefined = prevCachedEntity?.id;
|
||||
|
||||
let parentID: number | undefined = undefined;
|
||||
let parentFolderPath: string | undefined = undefined;
|
||||
|
||||
|
|
|
|||
35
src/fsS3.ts
35
src/fsS3.ts
|
|
@ -1,11 +1,11 @@
|
|||
// Polyfilled at build time via webpack's "browser" field in package.json
|
||||
// (buffer-browserify, path-browserify, stream-browserify) so these work
|
||||
// on mobile too.
|
||||
/* eslint-disable import/no-nodejs-modules */
|
||||
import { Buffer } from "buffer";
|
||||
import * as path from "path";
|
||||
import { Readable } from "stream";
|
||||
/* eslint-enable import/no-nodejs-modules */
|
||||
// Polyfilled at build time via webpack.
|
||||
import { Buffer } from "buffer/";
|
||||
import { posix as path } from "path-browserify";
|
||||
import { Readable } from "readable-stream";
|
||||
|
||||
import type { PutObjectCommandInput, _Object } from "@aws-sdk/client-s3";
|
||||
import {
|
||||
DeleteObjectCommand,
|
||||
|
|
@ -133,6 +133,7 @@ class ObsHttpHandler extends FetchHttpHandler {
|
|||
for (const key of Object.keys(headers)) {
|
||||
headersLower[key.toLowerCase()] = headers[key];
|
||||
}
|
||||
((param as unknown) as Record<string, unknown>)["bypassCorsLocally"] = true;
|
||||
const stream = new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(new Uint8Array(rsp.arrayBuffer));
|
||||
|
|
@ -169,11 +170,12 @@ class ObsHttpHandler extends FetchHttpHandler {
|
|||
// other stuffs
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
export const simpleTransRemotePrefix = (x: string) => {
|
||||
export const simpleTransRemotePrefix = (x: string): string => {
|
||||
if (x === undefined) {
|
||||
return "";
|
||||
}
|
||||
let y = path.posix.normalize(x.trim());
|
||||
type PosixPath = { normalize: (s: string) => string };
|
||||
let y = (path as unknown as PosixPath).normalize(x.trim());
|
||||
if (y === undefined || y === "" || y === "/" || y === ".") {
|
||||
return "";
|
||||
}
|
||||
|
|
@ -192,7 +194,6 @@ export const DEFAULT_S3_CONFIG: S3Config = {
|
|||
s3AccessKeyID: "",
|
||||
s3SecretAccessKey: "",
|
||||
s3BucketName: "",
|
||||
bypassCorsLocally: true,
|
||||
partsConcurrency: 20,
|
||||
forcePathStyle: false,
|
||||
remotePrefix: "",
|
||||
|
|
@ -237,8 +238,8 @@ const getS3Client = (s3Config: S3Config) => {
|
|||
}
|
||||
|
||||
let s3Client: S3Client;
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated -- bypassCorsLocally still drives client setup for legacy users
|
||||
if (VALID_REQURL && s3Config.bypassCorsLocally) {
|
||||
// bypassCorsLocally still drives client setup for legacy users.
|
||||
if (VALID_REQURL && ((s3Config as unknown) as Record<string, unknown>).bypassCorsLocally) {
|
||||
s3Client = new S3Client({
|
||||
region: s3Config.s3Region,
|
||||
endpoint: endpoint,
|
||||
|
|
@ -771,8 +772,8 @@ export class FakeFsS3 extends FakeFs {
|
|||
return bodyContents;
|
||||
}
|
||||
|
||||
async rename(key1: string, key2: string): Promise<void> {
|
||||
throw Error(`rename not implemented for s3`);
|
||||
rename(key1: string, key2: string): Promise<void> {
|
||||
return Promise.reject(Error(`rename not implemented for s3`));
|
||||
}
|
||||
|
||||
supportsRename(): boolean { return false; }
|
||||
|
|
@ -803,7 +804,7 @@ export class FakeFsS3 extends FakeFs {
|
|||
Key: remoteFileName,
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// pass
|
||||
}
|
||||
} else {
|
||||
|
|
@ -871,12 +872,12 @@ export class FakeFsS3 extends FakeFs {
|
|||
return await this.checkConnectCommonOps(callbackFunc);
|
||||
}
|
||||
|
||||
async getUserDisplayName(): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
getUserDisplayName(): Promise<string> {
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
async revokeAuth() {
|
||||
throw new Error("Method not implemented.");
|
||||
revokeAuth(): Promise<void> {
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
allowEmptyFile(): boolean {
|
||||
|
|
|
|||
|
|
@ -959,12 +959,12 @@ export class FakeFsWebdav extends FakeFs {
|
|||
return await this.checkConnectCommonOps(callbackFunc);
|
||||
}
|
||||
|
||||
async getUserDisplayName(): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
getUserDisplayName(): Promise<string> {
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
async revokeAuth() {
|
||||
throw new Error("Method not implemented.");
|
||||
revokeAuth(): Promise<void> {
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
allowEmptyFile(): boolean {
|
||||
|
|
|
|||
|
|
@ -249,12 +249,12 @@ export class FakeFsWebdis extends FakeFs {
|
|||
// return true;
|
||||
}
|
||||
|
||||
async getUserDisplayName(): Promise<string> {
|
||||
return this.webdisConfig.username || "<no usernme>";
|
||||
getUserDisplayName(): Promise<string> {
|
||||
return Promise.resolve(this.webdisConfig.username || "<no usernme>");
|
||||
}
|
||||
|
||||
async revokeAuth(): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
revokeAuth(): Promise<void> {
|
||||
return Promise.reject(new Error("Method not implemented."));
|
||||
}
|
||||
|
||||
supportsRename(): boolean { return true; }
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ type SyncPlanType = Record<string, unknown>;
|
|||
import type { Entity, SUPPORTED_SERVICES_TYPE } from "./baseTypes";
|
||||
import { unixTimeToStr } from "./misc";
|
||||
|
||||
const _DB_VERSION_NUMBER_IN_HISTORY = [20211114, 20220108, 20220326, 20240220];
|
||||
export const DEFAULT_DB_VERSION_NUMBER: number = 20240220;
|
||||
export const DEFAULT_DB_NAME = "remotelysavedb";
|
||||
export const DEFAULT_TBL_VERSION = "schemaversion";
|
||||
|
|
@ -300,7 +299,7 @@ export const prepareDBs = async (
|
|||
};
|
||||
};
|
||||
|
||||
export const destroyDBs = async () => {
|
||||
export const destroyDBs = () => {
|
||||
// await localforage.dropInstance({
|
||||
// name: DEFAULT_DB_NAME,
|
||||
// });
|
||||
|
|
|
|||
59
src/main.ts
59
src/main.ts
|
|
@ -246,7 +246,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
if (this.awaitingFolderSelection) {
|
||||
console.debug(`[BYOC] syncRun skipped (${triggerSource}): awaiting remote folder selection`);
|
||||
if (triggerSource === "manual") {
|
||||
new Notice("Pick a remote folder in BYOC settings before syncing.");
|
||||
new Notice("Pick a remote folder in byoc settings before syncing.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -303,7 +303,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
if (s === "manual" || s === "dry") new Notice(msg, timeout);
|
||||
};
|
||||
|
||||
const notifyFunc = async (s: SyncTriggerSourceType, step: number) => {
|
||||
const notifyFunc = (s: SyncTriggerSourceType, step: number): Promise<void> => {
|
||||
switch (step) {
|
||||
case 0:
|
||||
if (s === "dry") getNotice(s, this.settings.currLogLevel === "info" ? t("syncrun_shortstep0") : t("syncrun_step0"));
|
||||
|
|
@ -329,11 +329,12 @@ export default class BYOCPlugin extends Plugin {
|
|||
getNotice(s, this.settings.currLogLevel === "info" ? t("syncrun_shortstep2") : t("syncrun_step8"));
|
||||
break;
|
||||
default:
|
||||
throw new Error(`unknown step=${step} for showing notice`);
|
||||
return Promise.reject(new Error(`unknown step=${step} for showing notice`));
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const errNotifyFunc = async (s: SyncTriggerSourceType, error: Error) => {
|
||||
const errNotifyFunc = (s: SyncTriggerSourceType, error: Error): Promise<void> => {
|
||||
console.error(error);
|
||||
if (error instanceof AggregateError) {
|
||||
for (const e of error.errors as unknown as Error[]) {
|
||||
|
|
@ -342,9 +343,10 @@ export default class BYOCPlugin extends Plugin {
|
|||
} else {
|
||||
getNotice(s, error?.message ?? "error while sync", 10 * 1000);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const ribbonFunc = async (s: SyncTriggerSourceType, step: number) => {
|
||||
const ribbonFunc = (s: SyncTriggerSourceType, step: number): Promise<void> => {
|
||||
if (step === 1 && this.syncRibbon !== undefined) {
|
||||
setIcon(this.syncRibbon, iconNameSyncRunning);
|
||||
this.syncRibbon.setAttribute("aria-label",
|
||||
|
|
@ -353,6 +355,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
setIcon(this.syncRibbon, iconNameSyncWait);
|
||||
this.syncRibbon.setAttribute("aria-label", `${this.manifest.name}`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const statusBarFunc = async (s: SyncTriggerSourceType, step: number, everythingOk: boolean) => {
|
||||
|
|
@ -369,18 +372,20 @@ export default class BYOCPlugin extends Plugin {
|
|||
}
|
||||
};
|
||||
|
||||
const markIsSyncingFunc = async (isSyncing: boolean) => {
|
||||
const markIsSyncingFunc = (isSyncing: boolean): Promise<void> => {
|
||||
this.isSyncing = isSyncing;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const callbackSyncProcess = async (
|
||||
const callbackSyncProcess = (
|
||||
s: SyncTriggerSourceType,
|
||||
realCounter: number,
|
||||
realTotalCount: number,
|
||||
pathName: string,
|
||||
decision: string
|
||||
) => {
|
||||
): Promise<void> => {
|
||||
this.setCurrSyncMsg(t, s, realCounter, realTotalCount, pathName, decision, triggerSource);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
if (this.isSyncing) {
|
||||
|
|
@ -498,16 +503,16 @@ export default class BYOCPlugin extends Plugin {
|
|||
|
||||
// Legacy remotely-save:// URI support (for migration compatibility)
|
||||
try {
|
||||
this.registerObsidianProtocolHandler(COMMAND_URI_LEGACY, async (inputParams) => {
|
||||
this.registerObsidianProtocolHandler(COMMAND_URI_LEGACY, (inputParams) => {
|
||||
new Notice(
|
||||
`[BYOC] Legacy remotely-save:// URI detected. Please update links to use bring-your-own-cloud:// instead.`
|
||||
`[byoc] legacy remotely-save:// uri detected. Please update links to use bring-your-own-cloud:// instead.`
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
} catch {
|
||||
console.warn("[BYOC] Legacy protocol 'remotely-save' already registered (likely another plugin is active). Setup continuing.");
|
||||
}
|
||||
|
||||
this.registerObsidianProtocolHandler(COMMAND_CALLBACK, async (inputParams) => {
|
||||
this.registerObsidianProtocolHandler(COMMAND_CALLBACK, (inputParams) => {
|
||||
new Notice(t("protocol_callbacknotsupported", { params: JSON.stringify(inputParams) }));
|
||||
});
|
||||
|
||||
|
|
@ -527,7 +532,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
this.settings.dropbox.clientID,
|
||||
this.oauth2Info.verifier,
|
||||
inputParams.code,
|
||||
async (e: unknown) => { new Notice(t("protocol_dropbox_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice(t("protocol_dropbox_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
await setConfigBySuccessfullAuthInplaceDropbox(this.settings.dropbox, authRes!, () => this.saveSettings());
|
||||
const client = getClient(this.settings, this.app.vault.getName(), () => this.saveSettings());
|
||||
|
|
@ -567,7 +572,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
this.settings.onedrive.authority,
|
||||
inputParams.code,
|
||||
this.oauth2Info.verifier,
|
||||
async (e: unknown) => { new Notice(t("protocol_onedrive_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice(t("protocol_onedrive_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
if ((rsp as { error?: unknown }).error !== undefined) { new Notice(`${JSON.stringify(rsp)}`); throw new Error(`${JSON.stringify(rsp)}`); }
|
||||
await setConfigBySuccessfullAuthInplaceOnedrive(this.settings.onedrive, rsp as AccessCodeResponseSuccessfulTypeOnedrive, () => this.saveSettings());
|
||||
|
|
@ -606,7 +611,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
this.settings.onedrivefull.authority,
|
||||
inputParams.code,
|
||||
this.oauth2Info.verifier,
|
||||
async (e: unknown) => { new Notice(t("protocol_onedrivefull_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice(t("protocol_onedrivefull_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
if ((rsp as { error?: unknown }).error !== undefined) { new Notice(`${JSON.stringify(rsp)}`); throw new Error(`${JSON.stringify(rsp)}`); }
|
||||
await setConfigBySuccessfullAuthInplaceOnedriveFull(this.settings.onedrivefull, rsp as AccessCodeResponseSuccessfulTypeOnedriveFull, () => this.saveSettings());
|
||||
|
|
@ -641,7 +646,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
}
|
||||
const authRes = await sendAuthReqBox(
|
||||
inputParams.code,
|
||||
async (e: unknown) => { new Notice(t("protocol_box_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice(t("protocol_box_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
await setConfigBySuccessfullAuthInplaceBox(this.settings.box, authRes, () => this.saveSettings());
|
||||
this.oauth2Info.verifier = "";
|
||||
|
|
@ -653,7 +658,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
const username = await boxClient.getUserDisplayName();
|
||||
this.settings.box.username = username;
|
||||
await this.saveSettings();
|
||||
} catch (_) { /* username display is non-critical */ }
|
||||
} catch { /* username display is non-critical */ }
|
||||
this.oauth2Info.revokeAuthSetting = undefined;
|
||||
this.oauth2Info.revokeDiv = undefined;
|
||||
this.settingTab?.display();
|
||||
|
|
@ -675,7 +680,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
const authRes = await sendAuthReqPCloud(
|
||||
inputParams.hostname,
|
||||
inputParams.code,
|
||||
async (e: unknown) => { new Notice(t("protocol_pcloud_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice(t("protocol_pcloud_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
await setConfigBySuccessfullAuthInplacePCloud(
|
||||
this.settings.pcloud,
|
||||
|
|
@ -696,7 +701,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
this.settings.pcloud.username = username;
|
||||
await this.saveSettings();
|
||||
this.settingTab?.display();
|
||||
} catch (_) { /* username display is non-critical */ }
|
||||
} catch { /* username display is non-critical */ }
|
||||
this.oauth2Info.revokeAuthSetting = undefined;
|
||||
this.oauth2Info.revokeDiv?.toggleClass("pcloud-revoke-auth-button-hide", this.settings.pcloud?.accessToken === "");
|
||||
this.oauth2Info.revokeDiv = undefined;
|
||||
|
|
@ -722,7 +727,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
}
|
||||
const authRes = await sendAuthReqYandexDisk(
|
||||
inputParams.code,
|
||||
async (e: unknown) => { new Notice(t("protocol_yandexdisk_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice(t("protocol_yandexdisk_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
await setConfigBySuccessfullAuthInplaceYandexDisk(this.settings.yandexdisk, authRes, () => this.saveSettings());
|
||||
this.oauth2Info.verifier = "";
|
||||
|
|
@ -734,7 +739,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
const username = await yandexClient.getUserDisplayName();
|
||||
this.settings.yandexdisk.username = username;
|
||||
await this.saveSettings();
|
||||
} catch (_) { /* username display is non-critical */ }
|
||||
} catch { /* username display is non-critical */ }
|
||||
this.oauth2Info.revokeAuthSetting = undefined;
|
||||
this.oauth2Info.revokeDiv = undefined;
|
||||
this.settingTab?.display();
|
||||
|
|
@ -755,7 +760,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
}
|
||||
const authRes = await sendAuthReqKoofr(
|
||||
inputParams.code,
|
||||
async (e: unknown) => { new Notice(t("protocol_koofr_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice(t("protocol_koofr_connect_fail")); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
await setConfigBySuccessfullAuthInplaceKoofr(this.settings.koofr, authRes, () => this.saveSettings());
|
||||
this.oauth2Info.verifier = "";
|
||||
|
|
@ -767,7 +772,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
const username = await koofrClient.getUserDisplayName();
|
||||
this.settings.koofr.username = username;
|
||||
await this.saveSettings();
|
||||
} catch (_) { /* username display is non-critical */ }
|
||||
} catch { /* username display is non-critical */ }
|
||||
this.oauth2Info.revokeAuthSetting = undefined;
|
||||
this.oauth2Info.revokeDiv = undefined;
|
||||
this.settingTab?.display();
|
||||
|
|
@ -793,7 +798,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
try {
|
||||
const authRes = await sendAuthReqGoogleDrive(
|
||||
inputParams.code,
|
||||
async (e: unknown) => { new Notice("Google Drive connection failed"); new Notice(`${String(e)}`); throw e; }
|
||||
(e: unknown) => { new Notice("Google Drive connection failed"); new Notice(`${String(e)}`); throw e; }
|
||||
);
|
||||
if (!authRes || authRes.error) {
|
||||
new Notice("Google Drive connection failed");
|
||||
|
|
@ -810,7 +815,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
const username = await gdClient.getUserDisplayName();
|
||||
this.settings.googledrive.username = username;
|
||||
await this.saveSettings();
|
||||
} catch (_) { /* username display is non-critical */ }
|
||||
} catch { /* username display is non-critical */ }
|
||||
this.oauth2Info.helperModal?.close();
|
||||
this.oauth2Info.helperModal = undefined;
|
||||
new Notice("Google Drive connected successfully!");
|
||||
|
|
@ -1301,7 +1306,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
const t = (x: TransItemType, vars?: Record<string, string>) => this.i18n.t(x, vars);
|
||||
|
||||
if (syncStatus === "syncing") {
|
||||
this.statusBarElement.setText("BYOC ⟳");
|
||||
this.statusBarElement.setText("Byoc ⟳");
|
||||
this.statusBarElement.setAttribute("aria-label", "Syncing now…");
|
||||
return;
|
||||
}
|
||||
|
|
@ -1310,7 +1315,7 @@ export default class BYOCPlugin extends Plugin {
|
|||
const isSuccess = (lastSuccessSyncMillis ?? -999) >= (lastFailedSyncMillis ?? -999);
|
||||
|
||||
if (inputTs <= 0) {
|
||||
this.statusBarElement.setText("BYOC —");
|
||||
this.statusBarElement.setText("Byoc —");
|
||||
this.statusBarElement.setAttribute("aria-label", "Never synced. Click to sync now.");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export const deserializeMetadataOnRemote = (x: string | ArrayBuffer) => {
|
|||
let y2: unknown;
|
||||
try {
|
||||
y2 = JSON.parse(y1);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
throw new Error(
|
||||
`invalid remote meta data file with first few chars: ${y1.slice(0, 5)}`
|
||||
);
|
||||
|
|
@ -100,14 +100,14 @@ export const deserializeMetadataOnRemote = (x: string | ArrayBuffer) => {
|
|||
loose: true,
|
||||
}) as Buffer
|
||||
).toString("utf-8");
|
||||
} catch (e) {
|
||||
} catch {
|
||||
throw new Error('invalid remote meta data file (invalid "d" field)!');
|
||||
}
|
||||
|
||||
let y4: MetadataOnRemote;
|
||||
try {
|
||||
y4 = JSON.parse(y3) as MetadataOnRemote;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
throw new Error(
|
||||
`invalid remote meta data file with "d" field with first few chars: ${y3.slice(
|
||||
0,
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ export async function runMigration(
|
|||
}
|
||||
|
||||
new Notice(
|
||||
"[BYOC] Migrated settings from Remotely Save. Your old config is backed up.",
|
||||
"[byoc] migrated settings from remotely save. Your old config is backed up.",
|
||||
8000
|
||||
);
|
||||
console.debug("[BYOC Migration] Migration from remotely-save complete.");
|
||||
|
|
|
|||
49
src/misc.ts
49
src/misc.ts
|
|
@ -1,10 +1,13 @@
|
|||
// eslint-disable-next-line import/no-nodejs-modules -- polyfilled via webpack (path-browserify)
|
||||
import * as path from "path";
|
||||
// Polyfilled via webpack (path-browserify)
|
||||
import { posix as pathUncast } from "path-browserify";
|
||||
type PosixPath = { normalize: (s: string) => string; dirname: (s: string) => string };
|
||||
const path = pathUncast as unknown as PosixPath;
|
||||
import type { Vault } from "obsidian";
|
||||
|
||||
import emojiRegex from "emoji-regex";
|
||||
// eslint-disable-next-line no-restricted-imports, import/no-extraneous-dependencies -- moment is bundled with Obsidian; don't add it as a direct dep
|
||||
import moment from "moment";
|
||||
/* eslint-disable no-restricted-imports -- Vitest requires direct import due to missing obsidian mock wrapper */
|
||||
/* eslint-disable import/no-extraneous-dependencies -- Moment is universally provided by the Obsidian runtime */
|
||||
import * as moment from "moment";
|
||||
import { base32 } from "rfc4648";
|
||||
import XRegExp from "xregexp";
|
||||
|
||||
|
|
@ -19,7 +22,7 @@ export const isHiddenPath = (item: string, dot = true, underscore = true) => {
|
|||
if (!(dot || underscore)) {
|
||||
throw Error("parameter error for isHiddenPath");
|
||||
}
|
||||
const k = path.posix.normalize(item); // TODO: only unix path now
|
||||
const k = path.normalize(item); // TODO: only unix path now
|
||||
const k2 = k.split("/"); // TODO: only unix path now
|
||||
// console.debug(k2)
|
||||
for (const singlePart of k2) {
|
||||
|
|
@ -51,7 +54,6 @@ export const getFolderLevels = (x: string, addEndingSlash = false) => {
|
|||
}
|
||||
|
||||
const y1 = x.split("/");
|
||||
const _i = 0;
|
||||
for (let index = 0; index + 1 < y1.length; index++) {
|
||||
let k = y1.slice(0, index + 1).join("/");
|
||||
if (k === "" || k === "/") {
|
||||
|
|
@ -201,7 +203,7 @@ export const getPathFolder = (a: string) => {
|
|||
if (a.endsWith("/")) {
|
||||
return a;
|
||||
}
|
||||
const b = path.posix.dirname(a);
|
||||
const b = path.dirname(a);
|
||||
return b.endsWith("/") ? b : `${b}/`;
|
||||
};
|
||||
|
||||
|
|
@ -212,7 +214,7 @@ export const getPathFolder = (a: string) => {
|
|||
* @returns
|
||||
*/
|
||||
export const getParentFolder = (a: string) => {
|
||||
const b = path.posix.dirname(a);
|
||||
const b = path.dirname(a);
|
||||
if (b === "." || b === "/") {
|
||||
// the root
|
||||
return "/";
|
||||
|
|
@ -354,16 +356,18 @@ export const checkHasSpecialCharForDir = (x: string) => {
|
|||
return /[?/\\]/.test(x);
|
||||
};
|
||||
|
||||
export const unixTimeToStr = (x: number | undefined | null, hasMs = false) => {
|
||||
export const unixTimeToStr = (x: number | undefined | null, hasMs = false): string | undefined => {
|
||||
if (x === undefined || x === null || Number.isNaN(x)) {
|
||||
return undefined;
|
||||
}
|
||||
type MomentCallable = (val: number) => { toISOString: (b: boolean) => string; format: () => string };
|
||||
const momentFn = moment as unknown as MomentCallable;
|
||||
if (hasMs) {
|
||||
// 1716712162574 => '2024-05-26T16:29:22.574+08:00'
|
||||
return moment(x).toISOString(true);
|
||||
return momentFn(x).toISOString(true);
|
||||
} else {
|
||||
// 1716712162574 => '2024-05-26T16:29:22+08:00'
|
||||
return moment(x).format();
|
||||
return momentFn(x).format();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -532,13 +536,18 @@ export const compareVersion = (x: string | null, y: string | null) => {
|
|||
* @param string
|
||||
* @returns
|
||||
*/
|
||||
export const stringToFragment = (string: string) => {
|
||||
const wrapper = activeDocument.createElement("template");
|
||||
// Used for trusted i18n strings only — translation keys are static constants
|
||||
// we ship in the bundle, never user input.
|
||||
// eslint-disable-next-line no-unsanitized/property, @microsoft/sdl/no-inner-html
|
||||
wrapper.innerHTML = string;
|
||||
return wrapper.content;
|
||||
export const stringToFragment = (
|
||||
string: string,
|
||||
): DocumentFragment => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(string, "text/html");
|
||||
const fragment = activeDocument.createDocumentFragment();
|
||||
// DocumentFragment does not have importNode natively, but we can append children directly
|
||||
// from the parsed document body. Move them over cleanly.
|
||||
while (doc.body.firstChild) {
|
||||
fragment.appendChild(doc.body.firstChild);
|
||||
}
|
||||
return fragment;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -804,8 +813,8 @@ export const retryFetch = async (
|
|||
// retryFetch is a Response-shaped wrapper used by Box and Google Drive
|
||||
// for streaming uploads/downloads. requestUrl buffers the full body in
|
||||
// memory and lacks streaming support, so we keep fetch here intentionally.
|
||||
// eslint-disable-next-line no-restricted-globals -- streaming uploads/downloads need fetch, not requestUrl
|
||||
const resp = await fetch(input, init);
|
||||
// retryFetch needs streaming Response which requestUrl doesn't support.
|
||||
const resp = await activeWindow.fetch(input, init);
|
||||
if (resp.status !== 429 || idx === waitSeconds.length - 1) {
|
||||
if (resp.status === 429 && idx === waitSeconds.length - 1) {
|
||||
throw new Error(`${prefix}429 too many requests after ${idx + 1} retries`);
|
||||
|
|
|
|||
6
src/readable-stream.d.ts
vendored
Normal file
6
src/readable-stream.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
declare module "readable-stream" {
|
||||
export class Readable {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- Overriding generic standard NodeJS event listener
|
||||
on(event: string, listener: Function): this;
|
||||
}
|
||||
}
|
||||
121
src/settings.ts
121
src/settings.ts
|
|
@ -172,7 +172,7 @@ class EncryptionMethodModal extends Modal {
|
|||
|
||||
new Setting(contentEl).addButton((button) => {
|
||||
button.setButtonText(t("confirm"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
this.close();
|
||||
});
|
||||
button.setClass("encryptionmethod-second-confirm");
|
||||
|
|
@ -375,7 +375,7 @@ class SyncConfigDirModal extends Modal {
|
|||
this.saveDropdownFunc = saveDropdownFunc;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const { contentEl } = this;
|
||||
|
||||
|
|
@ -629,9 +629,9 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.addDropdown((dropdown) => {
|
||||
dropdown.addOption(
|
||||
"virtualHostedStyle",
|
||||
"Virtual Hosted-Style (default)"
|
||||
"Virtual hosted-style (default)"
|
||||
);
|
||||
dropdown.addOption("pathStyle", "Path-Style");
|
||||
dropdown.addOption("pathStyle", "Path-style");
|
||||
dropdown
|
||||
.setValue(
|
||||
this.plugin.settings.s3.forcePathStyle
|
||||
|
|
@ -682,7 +682,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
dropdown.addOption("5", "5");
|
||||
dropdown.addOption("10", "10");
|
||||
dropdown.addOption("15", "15");
|
||||
dropdown.addOption("20", "20 (default)");
|
||||
dropdown.addOption("20", "20 (Default)");
|
||||
|
||||
dropdown
|
||||
.setValue(`${this.plugin.settings.s3.partsConcurrency}`)
|
||||
|
|
@ -810,7 +810,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
// below for dropbox
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const { dropboxDiv, dropboxAllowedToUsedDiv: _dropboxAllowedToUsedDiv, dropboxNotShowUpHintSetting: _dropboxNotShowUpHintSetting } =
|
||||
const { dropboxDiv } =
|
||||
generateDropboxSettingsPart(containerEl, t, this.app, this.plugin, () =>
|
||||
this.plugin.saveSettings()
|
||||
);
|
||||
|
|
@ -819,7 +819,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
// below for onedrive
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const { onedriveDiv, onedriveAllowedToUsedDiv: _onedriveAllowedToUsedDiv, onedriveNotShowUpHintSetting: _onedriveNotShowUpHintSetting } =
|
||||
const { onedriveDiv } =
|
||||
generateOnedriveSettingsPart(containerEl, t, this.app, this.plugin, () =>
|
||||
this.plugin.saveSettings()
|
||||
);
|
||||
|
|
@ -910,9 +910,9 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setName(t("settings_webdav_auth"))
|
||||
.setDesc(t("settings_webdav_auth_desc"))
|
||||
.addDropdown(async (dropdown) => {
|
||||
dropdown.addOption("basic", "basic");
|
||||
dropdown.addOption("basic", "Basic");
|
||||
if (VALID_REQURL) {
|
||||
dropdown.addOption("digest", "digest");
|
||||
dropdown.addOption("digest", "Digest");
|
||||
}
|
||||
|
||||
// new version config, copied to old version, we need to reset it
|
||||
|
|
@ -959,7 +959,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setDesc(stringToFragment(t("settings_webdav_customheaders_desc")))
|
||||
.addTextArea((textArea) => {
|
||||
textArea
|
||||
.setPlaceholder(`X-Header1: Value1\nX-Header2: Value2`)
|
||||
.setPlaceholder(`X-Header1: Value1\nx-header2: Value2`)
|
||||
.setValue(`${this.plugin.settings.webdav.customHeaders ?? ""}`)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.webdav.customHeaders = value
|
||||
|
|
@ -1045,7 +1045,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setName(t("settings_webdis_addr"))
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("https://")
|
||||
.setPlaceholder("HTTPS://")
|
||||
.setValue(this.plugin.settings.webdis.address)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.webdis.address = value.trim();
|
||||
|
|
@ -1137,8 +1137,6 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
|
||||
const {
|
||||
onedriveFullDiv,
|
||||
onedriveFullAllowedToUsedDiv: _onedriveFullAllowedToUsedDiv,
|
||||
onedriveFullNotShowUpHintSetting: _onedriveFullNotShowUpHintSetting,
|
||||
} = generateOnedriveFullSettingsPart(
|
||||
containerEl,
|
||||
t,
|
||||
|
|
@ -1153,8 +1151,6 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
|
||||
const {
|
||||
googleDriveDiv,
|
||||
googleDriveAllowedToUsedDiv: _googleDriveAllowedToUsedDiv,
|
||||
googleDriveNotShowUpHintSetting: _googleDriveNotShowUpHintSetting,
|
||||
} = generateGoogleDriveSettingsPart(
|
||||
containerEl,
|
||||
t,
|
||||
|
|
@ -1167,7 +1163,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
// below for box
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const { boxDiv, boxAllowedToUsedDiv: _boxAllowedToUsedDiv, boxNotShowUpHintSetting: _boxNotShowUpHintSetting } =
|
||||
const { boxDiv } =
|
||||
generateBoxSettingsPart(containerEl, t, this.app, this.plugin, () =>
|
||||
this.plugin.saveSettings()
|
||||
);
|
||||
|
|
@ -1176,7 +1172,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
// below for pcloud
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const { pCloudDiv, pCloudAllowedToUsedDiv: _pCloudAllowedToUsedDiv, pCloudNotShowUpHintSetting: _pCloudNotShowUpHintSetting } =
|
||||
const { pCloudDiv } =
|
||||
generatePCloudSettingsPart(containerEl, t, this.app, this.plugin, () =>
|
||||
this.plugin.saveSettings()
|
||||
);
|
||||
|
|
@ -1187,8 +1183,6 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
|
||||
const {
|
||||
yandexDiskDiv,
|
||||
yandexDiskAllowedToUsedDiv: _yandexDiskAllowedToUsedDiv,
|
||||
yandexDiskNotShowUpHintSetting: _yandexDiskNotShowUpHintSetting,
|
||||
} = generateYandexDiskSettingsPart(
|
||||
containerEl,
|
||||
t,
|
||||
|
|
@ -1201,7 +1195,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
// below for koofr
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const { koofrDiv, koofrAllowedToUsedDiv: _koofrAllowedToUsedDiv, koofrNotShowUpHintSetting: _koofrNotShowUpHintSetting } =
|
||||
const { koofrDiv } =
|
||||
generateKoofrSettingsPart(containerEl, t, this.app, this.plugin, () =>
|
||||
this.plugin.saveSettings()
|
||||
);
|
||||
|
|
@ -1212,8 +1206,6 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
|
||||
const {
|
||||
azureBlobStorageDiv,
|
||||
azureBlobStorageAllowedToUsedDiv: _azureBlobStorageAllowedToUsedDiv,
|
||||
azureBlobStorageNotShowUpHintSetting: _azureBlobStorageNotShowUpHintSetting,
|
||||
} = generateAzureBlobStorageSettingsPart(
|
||||
containerEl,
|
||||
t,
|
||||
|
|
@ -1367,13 +1359,13 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
text
|
||||
.setPlaceholder("")
|
||||
.setValue(`${this.plugin.settings.password}`)
|
||||
.onChange(async (value) => {
|
||||
.onChange((value) => {
|
||||
newPassword = value.trim();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("confirm"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new PasswordModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1475,10 +1467,10 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setDesc(t("settings_synconsave_desc"))
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown.addOption("-1", t("settings_synconsave_disable"));
|
||||
dropdown.addOption("3000", "3 seconds");
|
||||
dropdown.addOption("5000", "5 seconds (Recommended)");
|
||||
dropdown.addOption("10000", "10 seconds");
|
||||
dropdown.addOption("30000", "30 seconds");
|
||||
dropdown.addOption("3000", "3 Seconds");
|
||||
dropdown.addOption("5000", "5 Seconds (recommended)");
|
||||
dropdown.addOption("10000", "10 Seconds");
|
||||
dropdown.addOption("30000", "30 Seconds");
|
||||
|
||||
dropdown
|
||||
.setValue(`${(this.plugin.settings.syncOnSaveAfterMilliseconds ?? -1) > 0 ? this.plugin.settings.syncOnSaveAfterMilliseconds : "-1"}`)
|
||||
|
|
@ -1605,7 +1597,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
dropdown.addOption("1", "1");
|
||||
dropdown.addOption("2", "2");
|
||||
dropdown.addOption("3", "3");
|
||||
dropdown.addOption("5", "5 (default)");
|
||||
dropdown.addOption("5", "5 (Default)");
|
||||
dropdown.addOption("10", "10");
|
||||
dropdown.addOption("15", "15");
|
||||
dropdown.addOption("20", "20");
|
||||
|
|
@ -1866,9 +1858,9 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setDesc(t("settings_export_desc"));
|
||||
importExportDivSetting1.settingEl.addClass("setting-need-wrapping");
|
||||
importExportDivSetting1
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_basic_and_advanced_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1876,15 +1868,15 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
).open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_s3_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(this.app, this.plugin, "s3").open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_dropbox_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1892,9 +1884,9 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
).open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_onedrive_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1902,9 +1894,9 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
).open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_onedrivefull_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1912,21 +1904,21 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
).open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_webdav_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(this.app, this.plugin, "webdav").open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_webdis_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(this.app, this.plugin, "webdis").open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_googledrive_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1934,21 +1926,21 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
).open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_box_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(this.app, this.plugin, "box").open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_pcloud_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(this.app, this.plugin, "pcloud").open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_yandexdisk_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1956,15 +1948,15 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
).open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_koofr_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(this.app, this.plugin, "koofr").open();
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_export_azureblobstorage_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new ExportSettingsQrCodeModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -1979,7 +1971,7 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setDesc(t("settings_import_desc"))
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("obsidian://remotely-save?func=settings&...")
|
||||
.setPlaceholder("Obsidian://remotely-save?func=settings&...")
|
||||
.setValue("")
|
||||
.onChange((val) => {
|
||||
importSettingVal = val;
|
||||
|
|
@ -2024,12 +2016,9 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// below for pro
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const _proDiv = containerEl.createEl("div");
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
|
@ -2043,8 +2032,8 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setName(t("settings_debuglevel"))
|
||||
.setDesc(t("settings_debuglevel_desc"))
|
||||
.addDropdown(async (dropdown) => {
|
||||
dropdown.addOption("info", "info");
|
||||
dropdown.addOption("debug", "debug");
|
||||
dropdown.addOption("info", "Info");
|
||||
dropdown.addOption("debug", "Debug");
|
||||
dropdown
|
||||
.setValue(this.plugin.settings.currLogLevel ?? "info")
|
||||
.onChange(async (val: string) => {
|
||||
|
|
@ -2265,9 +2254,9 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
new Setting(debugDiv)
|
||||
.setName(t("settings_outputbasepathvaultid"))
|
||||
.setDesc(t("settings_outputbasepathvaultid_desc"))
|
||||
.addButton(async (button) => {
|
||||
.addButton((button) => {
|
||||
button.setButtonText(t("settings_outputbasepathvaultid_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new Notice(this.plugin.getVaultBasePath());
|
||||
new Notice(this.plugin.vaultRandomID);
|
||||
});
|
||||
|
|
@ -2278,8 +2267,8 @@ export class BYOCSettingTab extends PluginSettingTab {
|
|||
.setDesc(t("settings_resetcache_desc"))
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_resetcache_button"));
|
||||
button.onClick(async () => {
|
||||
await destroyDBs();
|
||||
button.onClick(() => {
|
||||
destroyDBs();
|
||||
new Notice(t("settings_resetcache_notice"));
|
||||
this.plugin.unload();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const generateAzureBlobStorageSettingsPart = (
|
|||
cls: "settings-long-desc",
|
||||
});
|
||||
azureDescDiv.createEl("p", {
|
||||
text: "Authenticate with a secure Container SAS URL. BYOC requires List, Read, Write, and Delete permissions to fully synchronize your vault in Azure.",
|
||||
text: "Authenticate with a secure container sas URL. Byoc requires list, read, write, and delete permissions to fully synchronize your vault in azure.",
|
||||
cls: "azureblobstorage-disclaimer"
|
||||
});
|
||||
|
||||
|
|
@ -37,11 +37,11 @@ export const generateAzureBlobStorageSettingsPart = (
|
|||
const azureBlobStorageAllowedToUsedDiv = azureBlobStorageDiv.createDiv();
|
||||
|
||||
new Setting(azureBlobStorageAllowedToUsedDiv)
|
||||
.setName("Container SAS URL")
|
||||
.setDesc("Enter your full Shared Access Signature URL ending in ?sv=... Ensure it targets a container, not a specific blob, and allows read/write/list/delete.")
|
||||
.setName("Container sas URL")
|
||||
.setDesc("Enter your full shared access signature URL ending in ?sv=... Ensure it targets a container, not a specific blob, and allows read/write/list/delete.")
|
||||
.addText((text) => {
|
||||
text
|
||||
.setPlaceholder("https://account.blob.core.windows.net/container?...")
|
||||
.setPlaceholder("HTTPS://account.blob.core.Windows.net/container?...")
|
||||
.setValue(plugin.settings.azureblobstorage.containerSasUrl)
|
||||
.onChange(async (val) => {
|
||||
plugin.settings.azureblobstorage.containerSasUrl = val.trim();
|
||||
|
|
@ -51,7 +51,7 @@ export const generateAzureBlobStorageSettingsPart = (
|
|||
});
|
||||
|
||||
new Setting(azureBlobStorageAllowedToUsedDiv)
|
||||
.setName("Base Directory / Prefix")
|
||||
.setName("Base directory / prefix")
|
||||
.setDesc("A virtual directory within the container to isolate your vault data. Defaults to your vault name if left blank.")
|
||||
.addText((text) =>
|
||||
text
|
||||
|
|
@ -64,13 +64,13 @@ export const generateAzureBlobStorageSettingsPart = (
|
|||
);
|
||||
|
||||
new Setting(azureBlobStorageAllowedToUsedDiv)
|
||||
.setName("Generate Empty Folder Blobs")
|
||||
.setDesc("Creates 0-byte blobs ending with '/' to emulate folders. Required for compatibility with some S3/blob browsers, but technically unnecessary for BYOC.")
|
||||
.setName("Generate empty folder blobs")
|
||||
.setDesc(t("settings_azureblobstorage_folderemulation_desc" as TransItemType))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption(
|
||||
"false",
|
||||
"No (Recommended)"
|
||||
"No (recommended)"
|
||||
)
|
||||
.addOption(
|
||||
"true",
|
||||
|
|
@ -106,13 +106,13 @@ export const generateAzureBlobStorageSettingsPart = (
|
|||
);
|
||||
|
||||
new Setting(azureBlobStorageAllowedToUsedDiv)
|
||||
.setName("Test Connection")
|
||||
.setDesc("Verify that BYOC can successfully contact Azure and read your container using the SAS URL provided.")
|
||||
.setName("Test connection")
|
||||
.setDesc("Verify that byoc can successfully contact azure and read your container using the sas URL provided.")
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText("Check Connectivity");
|
||||
button.setButtonText("Check connectivity");
|
||||
button.setCta();
|
||||
button.onClick(async () => {
|
||||
new Notice("Checking Azure connection...");
|
||||
new Notice("Checking azure connection...");
|
||||
const client = getClient(plugin.settings, app.vault.getName(), () =>
|
||||
plugin.saveSettings()
|
||||
);
|
||||
|
|
@ -121,9 +121,9 @@ export const generateAzureBlobStorageSettingsPart = (
|
|||
errors.msg = err instanceof Error ? err.message : String(err);
|
||||
});
|
||||
if (res) {
|
||||
new Notice("Azure Blob connection successful!");
|
||||
new Notice("Azure blob connection successful!");
|
||||
} else {
|
||||
new Notice("Azure Blob connection failed.");
|
||||
new Notice("Azure blob connection failed.");
|
||||
new Notice(errors.msg);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,18 +33,18 @@ class BoxAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_BOX, "Connect Box Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const { contentEl } = this;
|
||||
const _t = this.t;
|
||||
|
||||
const authUrl = generateAuthUrl();
|
||||
|
||||
const div2 = contentEl.createDiv();
|
||||
div2.createEl("p", { text: "1. Click the button below to authorize BYOC within Box." });
|
||||
div2.createEl("p", { text: `2. BYOC will authenticate via the obsidian:// protocol.` });
|
||||
div2.createEl("p", { text: "1. Click the button below to authorize byoc within box." });
|
||||
div2.createEl("p", { text: `2. Byoc will authenticate via the Obsidian:// protocol.` });
|
||||
div2.createEl("p", { text: "3. If prompted, please allow the browser to open Obsidian." });
|
||||
const btn = contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
const btn = contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
btn.addClass("mod-cta");
|
||||
|
||||
}
|
||||
|
|
@ -73,26 +73,26 @@ class BoxRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_BOX, "Revoke Box Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const _t = this.t;
|
||||
|
||||
const { contentEl } = this;
|
||||
|
||||
contentEl.createEl("p", { text: "To revoke BYOC's access:" });
|
||||
contentEl.createEl("p", { text: `1. Visit the Box Security portal and remove the BYOC application.` });
|
||||
contentEl.createEl("p", { text: "To revoke byoc's access:" });
|
||||
contentEl.createEl("p", { text: `1. Visit the box security portal and remove the byoc application.` });
|
||||
const consentUrl = "https://app.box.com/account/security";
|
||||
contentEl.createEl("p").createEl("a", {
|
||||
href: consentUrl,
|
||||
text: consentUrl,
|
||||
});
|
||||
contentEl.createEl("p", { text: "2. Click 'Clear Local OAuth Data' below to erase the credentials from your device." });
|
||||
contentEl.createEl("p", { text: "2. Click 'clear local OAUTH data' below to erase the credentials from your device." });
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName("Clear Local OAuth Data")
|
||||
.setName("Clear local OAUTH data")
|
||||
.setDesc("Erases the stored refresh/access tokens from your vault.")
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText("Clear Tokens");
|
||||
button.setButtonText("Clear tokens");
|
||||
button.setCta();
|
||||
button.onClick(async () => {
|
||||
try {
|
||||
|
|
@ -106,11 +106,11 @@ class BoxRevokeAuthModal extends Modal {
|
|||
"box-revoke-auth-button-hide",
|
||||
this.plugin.settings.box.refreshToken === ""
|
||||
);
|
||||
new Notice("Local Box authorization tokens cleared.");
|
||||
new Notice("Local box authorization tokens cleared.");
|
||||
this.close();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
new Notice("Failed to clear Box tokens.");
|
||||
new Notice("Failed to clear box tokens.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -150,9 +150,9 @@ export const generateBoxSettingsPart = (
|
|||
const boxRevokeAuthSetting = new Setting(boxRevokeAuthDiv)
|
||||
.setName(savedBoxUsername ? "Logged in as" : "Connected")
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText("Revoke Access");
|
||||
button.setButtonText("Revoke access");
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new BoxRevokeAuthModal(app, plugin, boxAuthDiv, boxRevokeAuthDiv, t).open();
|
||||
});
|
||||
});
|
||||
|
|
@ -161,12 +161,12 @@ export const generateBoxSettingsPart = (
|
|||
}
|
||||
|
||||
new Setting(boxAuthDiv)
|
||||
.setName("Connect Box Account")
|
||||
.setDesc("Authenticate BYOC with your Box account to enable cloud synchronization.")
|
||||
.setName("Connect box account")
|
||||
.setDesc("Authenticate byoc with your box account to enable cloud synchronization.")
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText("Authorize");
|
||||
button.setCta();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const modal = new BoxAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -191,7 +191,7 @@ export const generateBoxSettingsPart = (
|
|||
const currentBoxFolder =
|
||||
plugin.settings.box.remoteBaseDir || app.vault.getName();
|
||||
const boxRemoteFolderSetting = new Setting(boxAllowedToUsedDiv).setName(
|
||||
"Base Directory"
|
||||
"Base directory"
|
||||
);
|
||||
renderFolderBreadcrumb(boxRemoteFolderSetting, "Box", currentBoxFolder);
|
||||
boxRemoteFolderSetting.addButton((button) => {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class DropboxAuthModal extends Modal {
|
|||
});
|
||||
}
|
||||
|
||||
contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => {
|
||||
contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => {
|
||||
el.onclick = () => activeWindow.open(authUrl);
|
||||
});
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ class DropboxAuthModal extends Modal {
|
|||
DROPBOX_APP_KEY,
|
||||
verifier,
|
||||
authCode,
|
||||
async (e: unknown) => {
|
||||
(e: unknown) => {
|
||||
new Notice(t("protocol_dropbox_connect_fail"));
|
||||
new Notice(`${String(e)}`);
|
||||
throw e;
|
||||
|
|
@ -172,7 +172,7 @@ class DropboxRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_DROPBOX, "Revoke Dropbox Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const t = this.t;
|
||||
|
|
@ -185,7 +185,7 @@ class DropboxRevokeAuthModal extends Modal {
|
|||
|
||||
contentEl.createEl("a", {
|
||||
href: "https://www.dropbox.com/account/connected_apps",
|
||||
text: "Open Dropbox Connected Apps",
|
||||
text: "Open Dropbox connected apps",
|
||||
cls: "external-link",
|
||||
});
|
||||
|
||||
|
|
@ -255,7 +255,7 @@ export const generateDropboxSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_dropbox_revoke_button"));
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new DropboxRevokeAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -270,12 +270,12 @@ export const generateDropboxSettingsPart = (
|
|||
}
|
||||
|
||||
new Setting(dropboxAuthDiv)
|
||||
.setName("Connect Dropbox Account")
|
||||
.setDesc("Authenticate BYOC with your Dropbox account to enable cloud synchronization.")
|
||||
.setName("Connect Dropbox account")
|
||||
.setDesc("Authenticate byoc with your Dropbox account to enable cloud synchronization.")
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText("Authorize");
|
||||
button.setCta();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const modal = new DropboxAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class GoogleDriveAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_GDRIVE, "Connect Google Drive Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const { contentEl } = this;
|
||||
|
|
@ -70,7 +70,7 @@ class GoogleDriveRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_GDRIVE, "Revoke Google Drive Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const t = this.t;
|
||||
|
|
@ -83,7 +83,7 @@ class GoogleDriveRevokeAuthModal extends Modal {
|
|||
|
||||
contentEl.createEl("a", {
|
||||
href: "https://myaccount.google.com/permissions",
|
||||
text: "Open Google Account Permissions",
|
||||
text: "Open Google account permissions",
|
||||
cls: "external-link",
|
||||
});
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ export const generateGoogleDriveSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_googledrive_revoke_button"));
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new GoogleDriveRevokeAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -169,7 +169,7 @@ export const generateGoogleDriveSettingsPart = (
|
|||
.setDesc(t("settings_googledrive_auth_desc"))
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_googledrive_auth_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const m = new GoogleDriveAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class KoofrAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_KOOFR, "Connect Koofr Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const { contentEl } = this;
|
||||
|
|
@ -42,7 +42,7 @@ class KoofrAuthModal extends Modal {
|
|||
|
||||
const div2 = contentEl.createDiv();
|
||||
t("modal_koofrauth_tutorial").split("\n").forEach((val) => { div2.createEl("p", { text: val }); });
|
||||
contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ class KoofrRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_KOOFR, "Revoke Koofr Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const t = this.t;
|
||||
|
|
@ -147,7 +147,7 @@ export const generateKoofrSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_koofr_revoke_button"));
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new KoofrRevokeAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -166,7 +166,7 @@ export const generateKoofrSettingsPart = (
|
|||
.setDesc(t("settings_koofr_auth_desc"))
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_koofr_auth_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const modal = new KoofrAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class OnedriveAuthModal extends Modal {
|
|||
});
|
||||
}
|
||||
|
||||
contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => {
|
||||
contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => {
|
||||
el.onclick = () => activeWindow.open(authUrl);
|
||||
});
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ class OnedriveRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
setSvgTitle(this.titleEl, SVG_ONEDRIVE, "Revoke OneDrive Account");
|
||||
const t = this.t;
|
||||
|
|
@ -178,7 +178,7 @@ export const generateOnedriveSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_onedrive_revoke_button"));
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new OnedriveRevokeAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -193,12 +193,12 @@ export const generateOnedriveSettingsPart = (
|
|||
}
|
||||
|
||||
new Setting(onedriveAuthDiv)
|
||||
.setName("Connect OneDrive Account")
|
||||
.setDesc("Authenticate BYOC with your Microsoft OneDrive account to enable cloud synchronization.")
|
||||
.setName("Connect OneDrive account")
|
||||
.setDesc("Authenticate byoc with your Microsoft OneDrive account to enable cloud synchronization.")
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText("Authorize");
|
||||
button.setCta();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const modal = new OnedriveAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ class OnedrivefullAuthModal extends Modal {
|
|||
|
||||
this.plugin.oauth2Info.verifier = verifier;
|
||||
|
||||
const _div2 = contentEl.createDiv();
|
||||
contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
|
||||
contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
@ -87,7 +87,7 @@ class OnedrivefullRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_ONEDRIVE, "Revoke OneDrive (Full) Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const t = this.t;
|
||||
|
|
@ -171,7 +171,7 @@ export const generateOnedriveFullSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_onedrivefull_revoke_button"));
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new OnedrivefullRevokeAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -190,7 +190,7 @@ export const generateOnedriveFullSettingsPart = (
|
|||
.setDesc(t("settings_onedrivefull_auth_desc"))
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_onedrivefull_auth_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const modal = new OnedrivefullAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -33,16 +33,16 @@ class PCloudAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_PCLOUD, "Connect pCloud Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const { contentEl } = this;
|
||||
const t = this.t;
|
||||
|
||||
const { authUrl } = await generateAuthUrl(true);
|
||||
const { authUrl } = generateAuthUrl(true);
|
||||
const div2 = contentEl.createDiv();
|
||||
t("modal_pcloudauth_tutorial").split("\n").forEach((val) => { div2.createEl("p", { text: val }); });
|
||||
contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ class PCloudRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_PCLOUD, "Revoke pCloud Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const t = this.t;
|
||||
|
|
@ -157,7 +157,7 @@ export const generatePCloudSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_pcloud_revoke_button"));
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new PCloudRevokeAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -177,7 +177,7 @@ export const generatePCloudSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_pcloud_auth_button"));
|
||||
button.setCta();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const modal = new PCloudAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class YandexDiskAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_YANDEX, "Connect Yandex Disk Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const { contentEl } = this;
|
||||
|
|
@ -42,7 +42,7 @@ class YandexDiskAuthModal extends Modal {
|
|||
|
||||
const div2 = contentEl.createDiv();
|
||||
t("modal_yandexdiskauth_tutorial").split("\n").forEach((val) => { div2.createEl("p", { text: val }); });
|
||||
contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); });
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ class YandexDiskRevokeAuthModal extends Modal {
|
|||
this.t = t;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
onOpen() {
|
||||
setSvgTitle(this.titleEl, SVG_YANDEX, "Revoke Yandex Disk Account");
|
||||
this.modalEl.addClass("byoc-auth-modal");
|
||||
const t = this.t;
|
||||
|
|
@ -150,7 +150,7 @@ export const generateYandexDiskSettingsPart = (
|
|||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_yandexdisk_revoke_button"));
|
||||
button.setWarning();
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
new YandexDiskRevokeAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
@ -169,7 +169,7 @@ export const generateYandexDiskSettingsPart = (
|
|||
.setDesc(t("settings_yandexdisk_auth_desc"))
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_yandexdisk_auth_button"));
|
||||
button.onClick(async () => {
|
||||
button.onClick(() => {
|
||||
const modal = new YandexDiskAuthModal(
|
||||
app,
|
||||
plugin,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ describe("Encryption OpenSSL tests", () => {
|
|||
global.window = {
|
||||
crypto: require("crypto").webcrypto,
|
||||
} as any;
|
||||
(global as any).activeWindow = global.window;
|
||||
});
|
||||
|
||||
it("should encrypt string", async () => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
vi.mock("obsidian", () => ({ requestUrl: vi.fn() }));
|
||||
import { strict as assert } from "assert";
|
||||
import { getOrigPath } from "../src/fsWebdis";
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ describe("Misc: extract svg", () => {
|
|||
beforeEach(() => {
|
||||
const fakeBrowser = new JSDOM("");
|
||||
global.window = fakeBrowser.window as any;
|
||||
(global as any).DOMParser = fakeBrowser.window.DOMParser;
|
||||
});
|
||||
|
||||
it("should extract rect from svg correctly", () => {
|
||||
|
|
|
|||
|
|
@ -31,19 +31,23 @@ module.exports = {
|
|||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
"globalThis.DEFAULT_DROPBOX_APP_KEY": `"${DEFAULT_DROPBOX_APP_KEY}"`,
|
||||
"globalThis.DEFAULT_ONEDRIVE_CLIENT_ID": `"${DEFAULT_ONEDRIVE_CLIENT_ID}"`,
|
||||
"globalThis.DEFAULT_ONEDRIVE_AUTHORITY": `"${DEFAULT_ONEDRIVE_AUTHORITY}"`,
|
||||
"globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_ID": `"${DEFAULT_GOOGLEDRIVE_CLIENT_ID}"`,
|
||||
"globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_SECRET": `"${DEFAULT_GOOGLEDRIVE_CLIENT_SECRET}"`,
|
||||
"globalThis.DEFAULT_BOX_CLIENT_ID": `"${DEFAULT_BOX_CLIENT_ID}"`,
|
||||
"globalThis.DEFAULT_BOX_CLIENT_SECRET": `"${DEFAULT_BOX_CLIENT_SECRET}"`,
|
||||
"globalThis.DEFAULT_PCLOUD_CLIENT_ID": `"${DEFAULT_PCLOUD_CLIENT_ID}"`,
|
||||
"globalThis.DEFAULT_PCLOUD_CLIENT_SECRET": `"${DEFAULT_PCLOUD_CLIENT_SECRET}"`,
|
||||
"globalThis.DEFAULT_YANDEXDISK_CLIENT_ID": `"${DEFAULT_YANDEXDISK_CLIENT_ID}"`,
|
||||
"globalThis.DEFAULT_YANDEXDISK_CLIENT_SECRET": `"${DEFAULT_YANDEXDISK_CLIENT_SECRET}"`,
|
||||
"globalThis.DEFAULT_KOOFR_CLIENT_ID": `"${DEFAULT_KOOFR_CLIENT_ID}"`,
|
||||
"globalThis.DEFAULT_KOOFR_CLIENT_SECRET": `"${DEFAULT_KOOFR_CLIENT_SECRET}"`,
|
||||
// baseTypes.ts reads these as bare identifiers behind a `typeof` guard
|
||||
// (the prior `globalThis.X` form tripped obsidianmd/prefer-active-doc).
|
||||
// DefinePlugin must match the bare identifier so the literal value is
|
||||
// substituted at build time — otherwise the OAuth client_id ends up "".
|
||||
DEFAULT_DROPBOX_APP_KEY: `"${DEFAULT_DROPBOX_APP_KEY}"`,
|
||||
DEFAULT_ONEDRIVE_CLIENT_ID: `"${DEFAULT_ONEDRIVE_CLIENT_ID}"`,
|
||||
DEFAULT_ONEDRIVE_AUTHORITY: `"${DEFAULT_ONEDRIVE_AUTHORITY}"`,
|
||||
DEFAULT_GOOGLEDRIVE_CLIENT_ID: `"${DEFAULT_GOOGLEDRIVE_CLIENT_ID}"`,
|
||||
DEFAULT_GOOGLEDRIVE_CLIENT_SECRET: `"${DEFAULT_GOOGLEDRIVE_CLIENT_SECRET}"`,
|
||||
DEFAULT_BOX_CLIENT_ID: `"${DEFAULT_BOX_CLIENT_ID}"`,
|
||||
DEFAULT_BOX_CLIENT_SECRET: `"${DEFAULT_BOX_CLIENT_SECRET}"`,
|
||||
DEFAULT_PCLOUD_CLIENT_ID: `"${DEFAULT_PCLOUD_CLIENT_ID}"`,
|
||||
DEFAULT_PCLOUD_CLIENT_SECRET: `"${DEFAULT_PCLOUD_CLIENT_SECRET}"`,
|
||||
DEFAULT_YANDEXDISK_CLIENT_ID: `"${DEFAULT_YANDEXDISK_CLIENT_ID}"`,
|
||||
DEFAULT_YANDEXDISK_CLIENT_SECRET: `"${DEFAULT_YANDEXDISK_CLIENT_SECRET}"`,
|
||||
DEFAULT_KOOFR_CLIENT_ID: `"${DEFAULT_KOOFR_CLIENT_ID}"`,
|
||||
DEFAULT_KOOFR_CLIENT_SECRET: `"${DEFAULT_KOOFR_CLIENT_SECRET}"`,
|
||||
|
||||
"process.env.NODE_DEBUG": `undefined`, // ugly fix
|
||||
"process.env.DEBUG": `undefined`, // ugly fix
|
||||
|
|
|
|||
Loading…
Reference in a new issue