add qrcode export import
This commit is contained in:
parent
563c2c3ff6
commit
8624141780
@ -25,6 +25,7 @@
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/qrcode": "^1.4.1",
|
||||
"builtin-modules": "^3.2.0",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
@ -57,6 +58,7 @@
|
||||
"obsidian": "^0.12.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"process": "^0.11.10",
|
||||
"qrcode": "^1.5.0",
|
||||
"rfc4648": "^1.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"stream-browserify": "^3.0.0",
|
||||
|
||||
@ -1,9 +1,45 @@
|
||||
/**
|
||||
* Only type defs here.
|
||||
* To avoid circular dependency.
|
||||
*/
|
||||
|
||||
export type SUPPORTED_SERVICES_TYPE = "s3" | "webdav" | "dropbox";
|
||||
|
||||
export interface S3Config {
|
||||
s3Endpoint: string;
|
||||
s3Region: string;
|
||||
s3AccessKeyID: string;
|
||||
s3SecretAccessKey: string;
|
||||
s3BucketName: string;
|
||||
}
|
||||
|
||||
export interface DropboxConfig {
|
||||
accessToken: string;
|
||||
clientID: string;
|
||||
refreshToken: string;
|
||||
accessTokenExpiresInSeconds: number;
|
||||
accessTokenExpiresAtTime: number;
|
||||
accountID: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export type WebdavAuthType = "digest" | "basic";
|
||||
|
||||
export interface WebdavConfig {
|
||||
address: string;
|
||||
username: string;
|
||||
password: string;
|
||||
authType: WebdavAuthType;
|
||||
}
|
||||
|
||||
export interface RemotelySavePluginSettings {
|
||||
s3: S3Config;
|
||||
webdav: WebdavConfig;
|
||||
dropbox: DropboxConfig;
|
||||
password: string;
|
||||
serviceType: SUPPORTED_SERVICES_TYPE;
|
||||
}
|
||||
|
||||
export interface RemoteItem {
|
||||
key: string;
|
||||
lastModified: number;
|
||||
@ -11,3 +47,13 @@ export interface RemoteItem {
|
||||
remoteType: SUPPORTED_SERVICES_TYPE;
|
||||
etag?: string;
|
||||
}
|
||||
|
||||
export const COMMAND_URI = "remotely-save";
|
||||
export const COMMAND_CALLBACK = "remotely-save-cb";
|
||||
|
||||
export interface UriParams {
|
||||
func?: string;
|
||||
vault?: string;
|
||||
ver?: string;
|
||||
data?: string;
|
||||
}
|
||||
|
||||
76
src/importExport.ts
Normal file
76
src/importExport.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import QRCode from "qrcode";
|
||||
|
||||
import {
|
||||
COMMAND_URI,
|
||||
UriParams,
|
||||
RemotelySavePluginSettings,
|
||||
} from "./baseTypes";
|
||||
|
||||
export const exportQrCodeUri = async (
|
||||
settings: RemotelySavePluginSettings,
|
||||
currentVaultName: string,
|
||||
pluginVersion: string
|
||||
) => {
|
||||
const data = encodeURIComponent(JSON.stringify(settings));
|
||||
const vault = encodeURIComponent(currentVaultName);
|
||||
const version = encodeURIComponent(pluginVersion);
|
||||
const rawUri = `obsidian://${COMMAND_URI}?func=settings&version=${version}&vault=${vault}&data=${data}`;
|
||||
// console.log(uri)
|
||||
const imgUri = await QRCode.toDataURL(rawUri);
|
||||
return {
|
||||
rawUri,
|
||||
imgUri,
|
||||
};
|
||||
};
|
||||
|
||||
export interface ProcessQrCodeResultType {
|
||||
status: "error" | "ok";
|
||||
message: string;
|
||||
result?: RemotelySavePluginSettings;
|
||||
}
|
||||
|
||||
export const importQrCodeUri = (
|
||||
inputParams: any,
|
||||
currentVaultName: string
|
||||
): ProcessQrCodeResultType => {
|
||||
let params = inputParams as UriParams;
|
||||
if (
|
||||
params.func === undefined ||
|
||||
params.func !== "settings" ||
|
||||
params.vault === undefined ||
|
||||
params.data === undefined
|
||||
) {
|
||||
return {
|
||||
status: "error",
|
||||
message: `the uri is not for exporting/importing settings: ${JSON.stringify(
|
||||
inputParams
|
||||
)}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (params.vault !== currentVaultName) {
|
||||
return {
|
||||
status: "error",
|
||||
message: `the target vault is ${
|
||||
params.vault
|
||||
} but you are currently in ${currentVaultName}: ${JSON.stringify(
|
||||
inputParams
|
||||
)}`,
|
||||
};
|
||||
}
|
||||
|
||||
let settings = {} as RemotelySavePluginSettings;
|
||||
try {
|
||||
settings = JSON.parse(params.data);
|
||||
} catch (e) {
|
||||
return {
|
||||
status: "error",
|
||||
message: `errors while parsing settings: ${JSON.stringify(inputParams)}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: "ok",
|
||||
message: "ok",
|
||||
result: settings,
|
||||
};
|
||||
};
|
||||
127
src/main.ts
127
src/main.ts
@ -27,14 +27,9 @@ import type { InternalDBs } from "./localdb";
|
||||
import type { SyncStatusType, PasswordCheckType } from "./sync";
|
||||
import { isPasswordOk, getSyncPlan, doActualSync } from "./sync";
|
||||
|
||||
import { S3Config, DEFAULT_S3_CONFIG } from "./remoteForS3";
|
||||
import { DEFAULT_S3_CONFIG } from "./remoteForS3";
|
||||
import { DEFAULT_WEBDAV_CONFIG } from "./remoteForWebdav";
|
||||
import {
|
||||
WebdavConfig,
|
||||
DEFAULT_WEBDAV_CONFIG,
|
||||
WebdavAuthType,
|
||||
} from "./remoteForWebdav";
|
||||
import {
|
||||
DropboxConfig,
|
||||
DEFAULT_DROPBOX_CONFIG,
|
||||
getAuthUrlAndVerifier,
|
||||
sendAuthReq,
|
||||
@ -43,15 +38,17 @@ import {
|
||||
|
||||
import { RemoteClient } from "./remote";
|
||||
import { exportSyncPlansToFiles } from "./debugMode";
|
||||
import { SUPPORTED_SERVICES_TYPE } from "./baseTypes";
|
||||
|
||||
interface RemotelySavePluginSettings {
|
||||
s3: S3Config;
|
||||
webdav: WebdavConfig;
|
||||
dropbox: DropboxConfig;
|
||||
password: string;
|
||||
serviceType: SUPPORTED_SERVICES_TYPE;
|
||||
}
|
||||
import { COMMAND_URI, COMMAND_CALLBACK } from "./baseTypes";
|
||||
import type {
|
||||
SUPPORTED_SERVICES_TYPE,
|
||||
S3Config,
|
||||
DropboxConfig,
|
||||
WebdavAuthType,
|
||||
WebdavConfig,
|
||||
RemotelySavePluginSettings,
|
||||
} from "./baseTypes";
|
||||
import type { ProcessQrCodeResultType } from "./importExport";
|
||||
import { exportQrCodeUri, importQrCodeUri } from "./importExport";
|
||||
|
||||
const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
||||
s3: DEFAULT_S3_CONFIG,
|
||||
@ -88,6 +85,32 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
})
|
||||
);
|
||||
|
||||
this.registerObsidianProtocolHandler(COMMAND_URI, async (inputParams) => {
|
||||
const parsed = importQrCodeUri(inputParams, this.app.vault.getName());
|
||||
if (parsed.status === "error") {
|
||||
new Notice(parsed.message);
|
||||
} else {
|
||||
const copied = JSON.parse(JSON.stringify(parsed.result));
|
||||
// new Notice(JSON.stringify(copied))
|
||||
this.settings = copied;
|
||||
this.saveSettings();
|
||||
new Notice(
|
||||
`New settings for ${this.manifest.name} saved. Reopen the plugin Settings to the effect.`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
this.registerObsidianProtocolHandler(
|
||||
COMMAND_CALLBACK,
|
||||
async (inputParams) => {
|
||||
new Notice(
|
||||
`Your uri call a callback that's not supported yet: ${JSON.stringify(
|
||||
inputParams
|
||||
)}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
this.addRibbonIcon("switch", "Remotely Save", async () => {
|
||||
if (this.syncStatus !== "idle") {
|
||||
new Notice(
|
||||
@ -382,6 +405,60 @@ export class DropboxAuthModal extends Modal {
|
||||
}
|
||||
}
|
||||
|
||||
export class ExportSettingsQrCodeModal extends Modal {
|
||||
plugin: RemotelySavePlugin;
|
||||
constructor(app: App, plugin: RemotelySavePlugin) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
let { contentEl } = this;
|
||||
|
||||
const { rawUri, imgUri } = await exportQrCodeUri(
|
||||
this.plugin.settings,
|
||||
this.app.vault.getName(),
|
||||
this.plugin.manifest.version
|
||||
);
|
||||
|
||||
contentEl.createEl("p", {
|
||||
text: "You can use another device to scan this qrcode.",
|
||||
});
|
||||
|
||||
contentEl.createEl("p", {
|
||||
text: "Or, you can click the button to copy the special url.",
|
||||
});
|
||||
|
||||
contentEl.createEl(
|
||||
"button",
|
||||
{
|
||||
text: "Click to copy the special URI",
|
||||
},
|
||||
(el) => {
|
||||
el.onclick = async () => {
|
||||
await navigator.clipboard.writeText(rawUri);
|
||||
new Notice("special uri copied to clipboard!");
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
contentEl.createEl(
|
||||
"img",
|
||||
{
|
||||
cls: "qrcode-img",
|
||||
},
|
||||
async (el) => {
|
||||
el.src = imgUri;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
let { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
plugin: RemotelySavePlugin;
|
||||
|
||||
@ -799,6 +876,24 @@ class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
});
|
||||
});
|
||||
|
||||
// import and export
|
||||
const importExportDiv = containerEl.createEl("div");
|
||||
importExportDiv.createEl("h2", { text: "Import and Export Settings" });
|
||||
|
||||
new Setting(importExportDiv)
|
||||
.setName("export")
|
||||
.setDesc("export all settings by generating qrcode")
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText("Get QR Code");
|
||||
button.onClick(async () => {
|
||||
new ExportSettingsQrCodeModal(this.app, this.plugin).open();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(importExportDiv)
|
||||
.setName("import")
|
||||
.setDesc("You should manually open a camera app to scan the QR code");
|
||||
|
||||
const debugDiv = containerEl.createEl("div");
|
||||
debugDiv.createEl("h2", { text: "Debug" });
|
||||
const syncPlanDiv = debugDiv.createEl("div");
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { Vault } from "obsidian";
|
||||
|
||||
import type { SUPPORTED_SERVICES_TYPE } from "./baseTypes";
|
||||
import type {
|
||||
SUPPORTED_SERVICES_TYPE,
|
||||
S3Config,
|
||||
DropboxConfig,
|
||||
WebdavConfig,
|
||||
} from "./baseTypes";
|
||||
import * as s3 from "./remoteForS3";
|
||||
import * as webdav from "./remoteForWebdav";
|
||||
import * as dropbox from "./remoteForDropbox";
|
||||
@ -8,17 +13,17 @@ import * as dropbox from "./remoteForDropbox";
|
||||
export class RemoteClient {
|
||||
readonly serviceType: SUPPORTED_SERVICES_TYPE;
|
||||
readonly s3Client?: s3.S3Client;
|
||||
readonly s3Config?: s3.S3Config;
|
||||
readonly s3Config?: S3Config;
|
||||
readonly webdavClient?: webdav.WebDAVClient;
|
||||
readonly webdavConfig?: webdav.WebdavConfig;
|
||||
readonly webdavConfig?: WebdavConfig;
|
||||
readonly dropboxClient?: dropbox.WrappedDropboxClient;
|
||||
readonly dropboxConfig?: dropbox.DropboxConfig;
|
||||
readonly dropboxConfig?: DropboxConfig;
|
||||
|
||||
constructor(
|
||||
serviceType: SUPPORTED_SERVICES_TYPE,
|
||||
s3Config?: s3.S3Config,
|
||||
webdavConfig?: webdav.WebdavConfig,
|
||||
dropboxConfig?: dropbox.DropboxConfig,
|
||||
s3Config?: S3Config,
|
||||
webdavConfig?: WebdavConfig,
|
||||
dropboxConfig?: DropboxConfig,
|
||||
vaultName?: string,
|
||||
saveUpdatedConfigFunc?: () => Promise<any>
|
||||
) {
|
||||
|
||||
@ -3,7 +3,7 @@ import { FileStats, Vault } from "obsidian";
|
||||
|
||||
import { Dropbox, DropboxAuth, DropboxResponse, files } from "dropbox";
|
||||
export { Dropbox } from "dropbox";
|
||||
import { RemoteItem } from "./baseTypes";
|
||||
import { RemoteItem, DropboxConfig } from "./baseTypes";
|
||||
import {
|
||||
arrayBufferToBuffer,
|
||||
bufferToArrayBuffer,
|
||||
@ -14,16 +14,6 @@ import {
|
||||
} from "./misc";
|
||||
import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt";
|
||||
|
||||
export interface DropboxConfig {
|
||||
accessToken: string;
|
||||
clientID: string;
|
||||
refreshToken: string;
|
||||
accessTokenExpiresInSeconds: number;
|
||||
accessTokenExpiresAtTime: number;
|
||||
accountID: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_DROPBOX_CONFIG = {
|
||||
accessToken: "",
|
||||
clientID: process.env.DEFAULT_DROPBOX_APP_KEY,
|
||||
|
||||
@ -27,17 +27,9 @@ import {
|
||||
} from "./misc";
|
||||
import * as mime from "mime-types";
|
||||
|
||||
import { RemoteItem } from "./baseTypes";
|
||||
import { RemoteItem, S3Config } from "./baseTypes";
|
||||
import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt";
|
||||
|
||||
export interface S3Config {
|
||||
s3Endpoint: string;
|
||||
s3Region: string;
|
||||
s3AccessKeyID: string;
|
||||
s3SecretAccessKey: string;
|
||||
s3BucketName: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_S3_CONFIG = {
|
||||
s3Endpoint: "",
|
||||
s3Region: "",
|
||||
|
||||
@ -4,7 +4,7 @@ import { AuthType, BufferLike, createClient } from "webdav/web";
|
||||
import type { WebDAVClient, ResponseDataDetailed, FileStat } from "webdav/web";
|
||||
export type { WebDAVClient } from "webdav/web";
|
||||
|
||||
import type { RemoteItem } from "./baseTypes";
|
||||
import type { RemoteItem, WebdavAuthType, WebdavConfig } from "./baseTypes";
|
||||
|
||||
import {
|
||||
arrayBufferToBuffer,
|
||||
@ -14,15 +14,6 @@ import {
|
||||
} from "./misc";
|
||||
import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt";
|
||||
|
||||
export type WebdavAuthType = "digest" | "basic";
|
||||
|
||||
export interface WebdavConfig {
|
||||
address: string;
|
||||
username: string;
|
||||
password: string;
|
||||
authType: WebdavAuthType;
|
||||
}
|
||||
|
||||
export const DEFAULT_WEBDAV_CONFIG = {
|
||||
address: "",
|
||||
username: "",
|
||||
|
||||
@ -32,3 +32,8 @@
|
||||
.webdav-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.qrcode-img {
|
||||
width: 350px;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user