Compare commits

..
12 Commits
Author SHA1 Message Date
fyears 7a13f4566c full word 2021-12-10 00:18:26 +08:00
fyears d355f72b7a update to 0.2.1 2021-12-10 00:17:31 +08:00
fyears 9b1eebdf90 standarized codes 2021-12-10 00:16:28 +08:00
fyears 2ce461ed6a allow multi pages in dropbox 2021-12-10 00:11:53 +08:00
fyears 3aa91a2a1e add env to build 2021-12-04 01:46:40 +08:00
fyears 2a3e0524c5 fix terrible typo 2021-12-04 01:21:37 +08:00
fyears 2f20ca61ce rebranding 2021-12-04 01:21:15 +08:00
fyears 68ec8b5454 bump version and update name 2021-12-04 01:18:20 +08:00
fyears 2bbbd569c5 fix typo 2021-12-04 01:11:17 +08:00
fyears 62936c4473 attempt to reduce size and use native crypto 2021-12-04 00:55:31 +08:00
fyears 789d07261e 0.1.10 2021-12-01 10:11:20 +08:00
fyears ccd3b49b4b inject env var in webpack 2021-12-01 10:10:39 +08:00
12 changed files with 78 additions and 55 deletions
+2
View File
@@ -13,6 +13,8 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: env-for-buildci
strategy: strategy:
matrix: matrix:
node-version: [16.x] node-version: [16.x]
+3 -3
View File
@@ -4,7 +4,7 @@ This is yet another unofficial sync plugin for Obsidian.
## Disclaimer ## Disclaimer
- **This is NOT the [official sync service](https://github.com/fyears/obsidian-remotely-save.git) provided by Obsidian.** - **This is NOT the [official sync service](https://obsidian.md/sync) provided by Obsidian.**
## !!!Caution!!! ## !!!Caution!!!
@@ -29,7 +29,7 @@ As of November 2021, the plugin is considered in BETA stage. **DO NOT USE IT for
## Download and Install ## Download and Install
- Option #1: [![BuildCI](https://github.com/fyears/obsidian-remotely-save/actions/workflows/auto-build.yml/badge.svg)](https://github.com/fyears/obsidian-remotely-save/actions/workflows/auto-build.yml) Every artifacts are placed in the "Summary" under every successful builds. - Option #1: [![BuildCI](https://github.com/fyears/remotely-save/actions/workflows/auto-build.yml/badge.svg)](https://github.com/fyears/remotely-save/actions/workflows/auto-build.yml) Every artifacts are placed in the "Summary" under every successful builds.
- Option #2: Besides manually downloading the files, you can also use [Obsidian42 - BRAT](https://github.com/TfTHacker/obsidian42-brat) to install this plugin. - Option #2: Besides manually downloading the files, you can also use [Obsidian42 - BRAT](https://github.com/TfTHacker/obsidian42-brat) to install this plugin.
- Option #3: The pluin would be submitted to the official "community plugin list" in near future. - Option #3: The pluin would be submitted to the official "community plugin list" in near future.
@@ -49,7 +49,7 @@ As of November 2021, the plugin is considered in BETA stage. **DO NOT USE IT for
- **This plugin's function for Dropbox is not as mature as functions for S3.** - **This plugin's function for Dropbox is not as mature as functions for S3.**
- **This plugin is NOT an official Dropbox product.** The plugin just uses Dropbox's public API. - **This plugin is NOT an official Dropbox product.** The plugin just uses Dropbox's public API.
- After the authorization, the plugin can read your name and email (which cannot be unselected on Dropbox api), and read and write files in your Dropbox's `/Apps/obsidian-remotely-save` folder. - After the authorization, the plugin can read your name and email (which cannot be unselected on Dropbox api), and read and write files in your Dropbox's `/Apps/remotely-save` folder.
- If you decide to authorize this plugin to connect to Dropbox, please go to plugin's settings, and choose Dropbox then follow the instructions. - If you decide to authorize this plugin to connect to Dropbox, please go to plugin's settings, and choose Dropbox then follow the instructions.
- Password-based end-to-end encryption is also supported. - Password-based end-to-end encryption is also supported.
+1
View File
@@ -24,6 +24,7 @@ esbuild
"obsidian", "obsidian",
"electron", "electron",
"fs", "fs",
"crypto",
// ...builtins // ...builtins
], ],
format: "cjs", format: "cjs",
+3 -3
View File
@@ -1,9 +1,9 @@
{ {
"id": "obsidian-remotely-save", "id": "remotely-save",
"name": "Remotely Save", "name": "Remotely Save",
"version": "0.1.9", "version": "0.2.1",
"minAppVersion": "0.12.15", "minAppVersion": "0.12.15",
"description": "Yet another unofficial plugin allowing users to sync notes between local device and the cloud service.", "description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
"author": "fyears", "author": "fyears",
"authorUrl": "https://github.com/fyears", "authorUrl": "https://github.com/fyears",
"isDesktopOnly": false "isDesktopOnly": false
+3 -6
View File
@@ -1,6 +1,6 @@
{ {
"name": "obsidian-remotely-save", "name": "remotely-save",
"version": "0.1.9", "version": "0.2.1",
"description": "This is yet another sync plugin for Obsidian app.", "description": "This is yet another sync plugin for Obsidian app.",
"scripts": { "scripts": {
"dev": "node esbuild.config.mjs", "dev": "node esbuild.config.mjs",
@@ -13,10 +13,7 @@
"browser": { "browser": {
"path": "path-browserify", "path": "path-browserify",
"process": "process/browser", "process": "process/browser",
"stream": "stream-browserify", "stream": "stream-browserify"
"crypto": "crypto-browserify",
"util": "util/",
"assert": "assert/"
}, },
"source": "main.ts", "source": "main.ts",
"keywords": [], "keywords": [],
+14 -13
View File
@@ -27,13 +27,16 @@ import type { InternalDBs } from "./localdb";
import type { SyncStatusType, PasswordCheckType } from "./sync"; import type { SyncStatusType, PasswordCheckType } from "./sync";
import { isPasswordOk, getSyncPlan, doActualSync } from "./sync"; import { isPasswordOk, getSyncPlan, doActualSync } from "./sync";
import { S3Config, DEFAULT_S3_CONFIG } from "./s3"; import { S3Config, DEFAULT_S3_CONFIG } from "./remoteForS3";
import { WebdavConfig, DEFAULT_WEBDAV_CONFIG, WebdavAuthType } from "./webdav"; import {
WebdavConfig,
DEFAULT_WEBDAV_CONFIG,
WebdavAuthType,
} from "./remoteForWebdav";
import { import {
DropboxConfig, DropboxConfig,
DEFAULT_DROPBOX_CONFIG, DEFAULT_DROPBOX_CONFIG,
getCodeVerifierAndChallenge, getAuthUrlAndVerifier,
getAuthUrl,
sendAuthReq, sendAuthReq,
setConfigBySuccessfullAuthInplace, setConfigBySuccessfullAuthInplace,
} from "./remoteForDropbox"; } from "./remoteForDropbox";
@@ -67,7 +70,7 @@ export default class RemotelySavePlugin extends Plugin {
syncStatus: SyncStatusType; syncStatus: SyncStatusType;
async onload() { async onload() {
console.log("loading plugin obsidian-remotely-save"); console.log("loading plugin remotely-save");
await this.loadSettings(); await this.loadSettings();
@@ -191,7 +194,7 @@ export default class RemotelySavePlugin extends Plugin {
} }
onunload() { onunload() {
console.log("unloading plugin obsidian-remotely-save"); console.log("unloading plugin remotely-save");
this.destroyDBs(); this.destroyDBs();
} }
@@ -296,13 +299,11 @@ export class DropboxAuthModal extends Modal {
this.revokeAuthSetting = revokeAuthSetting; this.revokeAuthSetting = revokeAuthSetting;
} }
onOpen() { async onOpen() {
let { contentEl } = this; let { contentEl } = this;
const k = getCodeVerifierAndChallenge(); const { authUrl, verifier } = await getAuthUrlAndVerifier(
const authUrl = getAuthUrl( this.plugin.settings.dropbox.clientID
this.plugin.settings.dropbox.clientID,
k.challenge
); );
contentEl.createEl("p", { contentEl.createEl("p", {
@@ -336,7 +337,7 @@ export class DropboxAuthModal extends Modal {
try { try {
const authRes = await sendAuthReq( const authRes = await sendAuthReq(
this.plugin.settings.dropbox.clientID, this.plugin.settings.dropbox.clientID,
k.verifier, verifier,
authCode authCode
); );
const self = this; const self = this;
@@ -602,7 +603,7 @@ class RemotelySaveSettingTab extends PluginSettingTab {
cls: "dropbox-disclaimer", cls: "dropbox-disclaimer",
}); });
dropboxDiv.createEl("p", { dropboxDiv.createEl("p", {
text: "We will create a folder App/obsidian-remotely-save on your Dropbox. All files/folders sync would happen inside this folder.", text: "We will create a folder Apps/remotely-save on your Dropbox. All files/folders sync would happen inside this folder.",
}); });
const dropboxSelectAuthDiv = dropboxDiv.createDiv(); const dropboxSelectAuthDiv = dropboxDiv.createDiv();
+2 -2
View File
@@ -1,8 +1,8 @@
import { Vault } from "obsidian"; import { Vault } from "obsidian";
import type { SUPPORTED_SERVICES_TYPE } from "./baseTypes"; import type { SUPPORTED_SERVICES_TYPE } from "./baseTypes";
import * as s3 from "./s3"; import * as s3 from "./remoteForS3";
import * as webdav from "./webdav"; import * as webdav from "./remoteForWebdav";
import * as dropbox from "./remoteForDropbox"; import * as dropbox from "./remoteForDropbox";
export class RemoteClient { export class RemoteClient {
+41 -26
View File
@@ -1,9 +1,7 @@
import * as path from "path"; import * as path from "path";
import { FileStats, Vault } from "obsidian"; import { FileStats, Vault } from "obsidian";
import { Buffer } from "buffer";
import * as crypto from "crypto";
import { Dropbox, DropboxResponse, files } from "dropbox"; import { Dropbox, DropboxAuth, DropboxResponse, files } from "dropbox";
export { Dropbox } from "dropbox"; export { Dropbox } from "dropbox";
import { RemoteItem } from "./baseTypes"; import { RemoteItem } from "./baseTypes";
import { import {
@@ -162,32 +160,28 @@ const fixLastModifiedTimeInplace = (allFilesFolders: RemoteItem[]) => {
// see https://dropbox.tech/developers/pkce--what-and-why- // see https://dropbox.tech/developers/pkce--what-and-why-
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const specialBase64Encode = (str: Buffer) => { export const getAuthUrlAndVerifier = async (appKey: string) => {
return str const auth = new DropboxAuth({
.toString("base64") clientId: appKey,
.replace(/\+/g, "-") });
.replace(/\//g, "_") const authUrl = (
.replace(/=/g, ""); await auth.getAuthenticationUrl(
}; undefined,
const sha256 = (buffer: string) => { undefined,
return crypto.createHash("sha256").update(buffer).digest(); "code",
}; "offline",
undefined,
export const getCodeVerifierAndChallenge = () => { "none",
const codeVerifier = specialBase64Encode(crypto.randomBytes(32)); true
// console.log(`Client generated code_verifier: ${codeVerifier}`); )
const codeChallenge = specialBase64Encode(sha256(codeVerifier)); ).toString();
// console.log(`Client generated code_challenge: ${codeChallenge}`); const verifier = auth.getCodeVerifier();
return { return {
verifier: codeVerifier, authUrl: authUrl,
challenge: codeChallenge, verifier: verifier,
}; };
}; };
export const getAuthUrl = (appKey: string, challenge: string) => {
return `https://www.dropbox.com/oauth2/authorize?client_id=${appKey}&response_type=code&code_challenge=${challenge}&code_challenge_method=S256&token_access_type=offline`;
};
export interface DropboxSuccessAuthRes { export interface DropboxSuccessAuthRes {
access_token: string; access_token: string;
token_type: "bearer"; token_type: "bearer";
@@ -487,20 +481,41 @@ export const listFromRemote = async (
throw Error("prefix not supported (yet)"); throw Error("prefix not supported (yet)");
} }
await client.init(); await client.init();
const res = await client.dropbox.filesListFolder({ let res = await client.dropbox.filesListFolder({
path: `/${client.vaultName}`, path: `/${client.vaultName}`,
recursive: true, recursive: true,
include_deleted: false,
limit: 1000,
}); });
if (res.status !== 200) { if (res.status !== 200) {
throw Error(JSON.stringify(res)); throw Error(JSON.stringify(res));
} }
// console.log(res); // console.log(res);
const contents = res.result.entries; const contents = res.result.entries;
const unifiedContents = contents const unifiedContents = contents
.filter((x) => x[".tag"] !== "deleted") .filter((x) => x[".tag"] !== "deleted")
.filter((x) => x.path_display !== `/${client.vaultName}`) .filter((x) => x.path_display !== `/${client.vaultName}`)
.map((x) => fromDropboxItemToRemoteItem(x, client.vaultName)); .map((x) => fromDropboxItemToRemoteItem(x, client.vaultName));
while (res.result.has_more) {
res = await client.dropbox.filesListFolderContinue({
cursor: res.result.cursor,
});
if (res.status !== 200) {
throw Error(JSON.stringify(res));
}
const contents2 = res.result.entries;
const unifiedContents2 = contents2
.filter((x) => x[".tag"] !== "deleted")
.filter((x) => x.path_display !== `/${client.vaultName}`)
.map((x) => fromDropboxItemToRemoteItem(x, client.vaultName));
unifiedContents.push(...unifiedContents2);
}
fixLastModifiedTimeInplace(unifiedContents); fixLastModifiedTimeInplace(unifiedContents);
return { return {
Contents: unifiedContents, Contents: unifiedContents,
}; };
View File
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"0.1.9": "0.12.15" "0.2.1": "0.12.15"
} }
+8 -1
View File
@@ -1,7 +1,10 @@
require("dotenv").config();
const path = require("path"); const path = require("path");
const webpack = require("webpack"); const webpack = require("webpack");
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const DEFAULT_DROPBOX_APP_KEY = process.env.DROPBOX_APP_KEY || "";
module.exports = { module.exports = {
entry: "./src/main.ts", entry: "./src/main.ts",
target: "web", target: "web",
@@ -11,6 +14,9 @@ module.exports = {
libraryTarget: "commonjs", libraryTarget: "commonjs",
}, },
plugins: [ plugins: [
new webpack.DefinePlugin({
"process.env.DEFAULT_DROPBOX_APP_KEY": `"${DEFAULT_DROPBOX_APP_KEY}"`,
}),
// Work around for Buffer is undefined: // Work around for Buffer is undefined:
// https://github.com/webpack/changelog-v5/issues/10 // https://github.com/webpack/changelog-v5/issues/10
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
@@ -37,7 +43,8 @@ module.exports = {
// buffer: require.resolve("buffer/"), // buffer: require.resolve("buffer/"),
// console: require.resolve("console-browserify"), // console: require.resolve("console-browserify"),
// constants: require.resolve("constants-browserify"), // constants: require.resolve("constants-browserify"),
crypto: require.resolve("crypto-browserify"), // crypto: require.resolve("crypto-browserify"),
crypto: false,
// domain: require.resolve("domain-browser"), // domain: require.resolve("domain-browser"),
// events: require.resolve("events"), // events: require.resolve("events"),
// http: require.resolve("stream-http"), // http: require.resolve("stream-http"),