Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6790bb01d3 | ||
|
|
131f3f70e1 | ||
|
|
b551e0c1b5 | ||
|
|
c65c0aa258 | ||
|
|
1f0030b514 | ||
|
|
07896f92a3 | ||
|
|
9eddfb02d6 | ||
|
|
f0dd3bb5ef | ||
|
|
b91c0e694c | ||
|
|
84b03d2c2b | ||
|
|
313eff6a14 | ||
|
|
8671ba4660 | ||
|
|
3b195f33c8 | ||
|
|
65e18aa4b7 | ||
|
|
2cedef8168 | ||
|
|
22bc74f4d7 | ||
|
|
277793ccf4 |
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
This is yet another unofficial sync plugin for Obsidian. If you like it or find it useful, please consider give it a [star ](https://github.com/fyears/remotely-save) on Github.
|
This is yet another unofficial sync plugin for Obsidian. If you like it or find it useful, please consider give it a [star ](https://github.com/fyears/remotely-save) on Github.
|
||||||
|
|
||||||
[](https://github.com/fyears/remotely-save/actions/workflows/auto-build.yml) [](https://github.com/fyears/remotely-save/releases) [](https://github.com/fyears/remotely-save/releases)
|
[](https://github.com/fyears/remotely-save/actions/workflows/auto-build.yml)
|
||||||
|
|
||||||
|
[](https://github.com/fyears/remotely-save/releases)
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ As of Jan 2022, the plugin is considered in BETA stage. **DO NOT USE IT for any
|
|||||||
- Supports:
|
- Supports:
|
||||||
- Amazon S3 or S3-compatible
|
- Amazon S3 or S3-compatible
|
||||||
- Dropbox
|
- Dropbox
|
||||||
- OneDrive
|
- OneDrive for personal
|
||||||
- Webdav
|
- Webdav
|
||||||
- **Obsidiain Mobile supported.** Vaults can be synced across mobile and desktop devices with the cloud service as the "broker".
|
- **Obsidiain Mobile supported.** Vaults can be synced across mobile and desktop devices with the cloud service as the "broker".
|
||||||
- **[End-to-end encryption](./docs/encryption.md) supported.** Files would be encrypted using openssl format before being sent to the cloud **if** user specify a password.
|
- **[End-to-end encryption](./docs/encryption.md) supported.** Files would be encrypted using openssl format before being sent to the cloud **if** user specify a password.
|
||||||
@@ -70,10 +72,11 @@ Additionally, the plugin author may occasionally visit Obsidian official forum a
|
|||||||
- 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. But please be aware that **the vault name itself is not encrypted**.
|
- Password-based end-to-end encryption is also supported. But please be aware that **the vault name itself is not encrypted**.
|
||||||
|
|
||||||
### OneDrive
|
### OneDrive for personal
|
||||||
|
|
||||||
- **This plugin's function for OneDrive is not as mature as functions for S3.**
|
- **This plugin's function for OneDrive is not as mature as functions for S3.**
|
||||||
- **This plugin is NOT an official Microsoft / OneDrive product.** The plugin just uses Microsoft's [OneDrive's public API](https://docs.microsoft.com/en-us/onedrive/developer/rest-api).
|
- **This plugin is NOT an official Microsoft / OneDrive product.** The plugin just uses Microsoft's [OneDrive's public API](https://docs.microsoft.com/en-us/onedrive/developer/rest-api).
|
||||||
|
- This plugin only works for "OneDrive for personal", and not works for "OneDrive for Business" (yet). See [#11](https://github.com/fyears/remotely-save/issues/11) to further details.
|
||||||
- After the authorization, the plugin can read your name and email, and read and write files in your OneDrive's `/Apps/remotely-save` folder.
|
- After the authorization, the plugin can read your name and email, and read and write files in your OneDrive's `/Apps/remotely-save` folder.
|
||||||
- If you decide to authorize this plugin to connect to OneDrive, please go to plugin's settings, and choose OneDrive then follow the instructions.
|
- If you decide to authorize this plugin to connect to OneDrive, please go to plugin's settings, and choose OneDrive then follow the instructions.
|
||||||
- Password-based end-to-end encryption is also supported. But please be aware that **the vault name itself is not encrypted**.
|
- Password-based end-to-end encryption is also supported. But please be aware that **the vault name itself is not encrypted**.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:a9bf1cc2403c3a32e7a6f5d5829bb065573922dc050ae2fdb8b3d34b4c87ecf5
|
||||||
|
size 8296
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6990a598379c457442345e50c3f056c4598a121523bc10980835c08fc7447a54
|
||||||
|
size 10119
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:432198e63fa9676541a41436917e025d1ea6af3772139c1dea450fcc0155cfd0
|
||||||
|
size 13054
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fe3b5b8bf9c32e57c0af366c455b11ae68eb9fb8a32f371c9c445b3e7e89cc02
|
||||||
|
size 1120
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f68df7bae94eb4c4cddfa6af47d7819d01a6482d4f6afe6ef3236f72f9adf15f
|
||||||
|
size 1523
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# the logo
|
||||||
|
|
||||||
|
Some different sizes' logos are needed.
|
||||||
|
|
||||||
|
## license
|
||||||
|
|
||||||
|
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.
|
||||||
|
|
||||||
|
## the process of creation
|
||||||
|
|
||||||
|
No professional designers are here. Thus the following steps involve many programer styles. :-)
|
||||||
|
|
||||||
|
1. use excalidraw and export png and svg.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# results
|
||||||
|
logo.excalidraw
|
||||||
|
logo.png
|
||||||
|
logo.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
2. manually edit the `logo.svg` and make background transparent.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# results
|
||||||
|
logo-transparent.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
3. use python library [`svgutils`](https://github.com/btel/svg_utils) to make a strictly square figure. The [doc](https://svgutils.readthedocs.io/en/latest/tutorials/composing_multipanel_figures.html) is very useful.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from svgutils.compose import *
|
||||||
|
def get_standard_300x300(file_name):
|
||||||
|
fig = Figure(300, 300,
|
||||||
|
Panel(
|
||||||
|
SVG(file_name),
|
||||||
|
).move(-3, 12),
|
||||||
|
)
|
||||||
|
return fig
|
||||||
|
|
||||||
|
get_standard_300x300('logo-transparent.svg').save('300x300.svg')
|
||||||
|
|
||||||
|
# def get_other_size_from_standard(file_name, px):
|
||||||
|
# fig = Figure(px, px,
|
||||||
|
# Panel(
|
||||||
|
# SVG(file_name).scale(px/300.0),
|
||||||
|
# ).move(-3*px/300.0, 12*px/300.0),
|
||||||
|
# )
|
||||||
|
# return fig
|
||||||
|
|
||||||
|
# get_other_size_from_standard('logo.svg',256).save('256x256.svg')
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# results
|
||||||
|
300x300.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
4. use `inkscape` command line to get different sizes' `.png` files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
inkscape 300x300.svg -o 300x300.png
|
||||||
|
|
||||||
|
inkscape 300x300.svg -o 50x50.png -w 50 -h 50
|
||||||
|
|
||||||
|
inkscape 300x300.svg -o 64x64.png -w 64 -h 64
|
||||||
|
inkscape 300x300.svg -o 256x256.png -w 256 -h 256
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# results
|
||||||
|
50x50.png
|
||||||
|
64x64.png
|
||||||
|
256x256.png
|
||||||
|
```
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:daedd262170200cb5e8cc288898b40892771b3962cf2bc17dc443f1c82a4aa94
|
||||||
|
size 12886
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "remotely-save",
|
"id": "remotely-save",
|
||||||
"name": "Remotely Save",
|
"name": "Remotely Save",
|
||||||
"version": "0.2.7",
|
"version": "0.2.10",
|
||||||
"minAppVersion": "0.12.15",
|
"minAppVersion": "0.12.15",
|
||||||
"description": "Yet another unofficial plugin allowing users to synchronize 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",
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "remotely-save",
|
"name": "remotely-save",
|
||||||
"version": "0.2.7",
|
"version": "0.2.10",
|
||||||
"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",
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
"dropbox": "^10.22.0",
|
"dropbox": "^10.22.0",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"loglevel": "^1.8.0",
|
||||||
"mime-types": "^2.1.33",
|
"mime-types": "^2.1.33",
|
||||||
"obsidian": "^0.12.0",
|
"obsidian": "^0.12.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export interface RemotelySavePluginSettings {
|
|||||||
onedrive: OnedriveConfig;
|
onedrive: OnedriveConfig;
|
||||||
password: string;
|
password: string;
|
||||||
serviceType: SUPPORTED_SERVICES_TYPE;
|
serviceType: SUPPORTED_SERVICES_TYPE;
|
||||||
|
currLogLevel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteItem {
|
export interface RemoteItem {
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { base64, base64url } from "rfc4648";
|
||||||
|
import { reverseString } from "./misc";
|
||||||
|
|
||||||
|
import type { RemotelySavePluginSettings } from "./baseTypes";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
|
const DEFAULT_README: string =
|
||||||
|
"Do NOT modify this manually. It's generated automatically.";
|
||||||
|
|
||||||
|
interface MessyConfigType {
|
||||||
|
readme: string;
|
||||||
|
d: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this should accept the result after loadData();
|
||||||
|
*/
|
||||||
|
export const messyConfigToNormal = (
|
||||||
|
x: MessyConfigType | RemotelySavePluginSettings | null | undefined
|
||||||
|
): RemotelySavePluginSettings | null | undefined => {
|
||||||
|
log.debug("loading, original config on disk:");
|
||||||
|
log.debug(x);
|
||||||
|
if (x === null || x === undefined) {
|
||||||
|
log.debug("the messy config is null or undefined, skip");
|
||||||
|
return x as any;
|
||||||
|
}
|
||||||
|
if ("readme" in x && "d" in x) {
|
||||||
|
// we should decode
|
||||||
|
const y = JSON.parse(
|
||||||
|
(
|
||||||
|
base64url.parse(reverseString(x["d"]), {
|
||||||
|
out: Buffer.allocUnsafe as any,
|
||||||
|
loose: true,
|
||||||
|
}) as Buffer
|
||||||
|
).toString("utf-8")
|
||||||
|
);
|
||||||
|
log.debug("loading, parsed config is:");
|
||||||
|
log.debug(y);
|
||||||
|
return y;
|
||||||
|
} else {
|
||||||
|
// return as is
|
||||||
|
log.debug("loading, parsed config is the same");
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this should accept the result of original config
|
||||||
|
*/
|
||||||
|
export const normalConfigToMessy = (
|
||||||
|
x: RemotelySavePluginSettings | null | undefined
|
||||||
|
) => {
|
||||||
|
if (x === null || x === undefined) {
|
||||||
|
log.debug("the normal config is null or undefined, skip");
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
const y = {
|
||||||
|
readme: DEFAULT_README,
|
||||||
|
d: reverseString(
|
||||||
|
base64url.stringify(Buffer.from(JSON.stringify(x), "utf-8"), {
|
||||||
|
pad: false,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
log.debug("encoding, encoded config is:");
|
||||||
|
log.debug(y);
|
||||||
|
return y;
|
||||||
|
};
|
||||||
+5
-2
@@ -5,11 +5,14 @@ import { readAllSyncPlanRecordTexts } from "./localdb";
|
|||||||
import type { InternalDBs } from "./localdb";
|
import type { InternalDBs } from "./localdb";
|
||||||
import { mkdirpInVault } from "./misc";
|
import { mkdirpInVault } from "./misc";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
const DEFAULT_DEBUG_FOLDER = "_debug_remotely_save/";
|
const DEFAULT_DEBUG_FOLDER = "_debug_remotely_save/";
|
||||||
const DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX = "sync_plans_hist_exported_on_";
|
const DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX = "sync_plans_hist_exported_on_";
|
||||||
|
|
||||||
export const exportSyncPlansToFiles = async (db: InternalDBs, vault: Vault) => {
|
export const exportSyncPlansToFiles = async (db: InternalDBs, vault: Vault) => {
|
||||||
console.log("exporting");
|
log.info("exporting");
|
||||||
await mkdirpInVault(DEFAULT_DEBUG_FOLDER, vault);
|
await mkdirpInVault(DEFAULT_DEBUG_FOLDER, vault);
|
||||||
const records = await readAllSyncPlanRecordTexts(db);
|
const records = await readAllSyncPlanRecordTexts(db);
|
||||||
let md = "";
|
let md = "";
|
||||||
@@ -25,5 +28,5 @@ export const exportSyncPlansToFiles = async (db: InternalDBs, vault: Vault) => {
|
|||||||
await vault.create(filePath, md, {
|
await vault.create(filePath, md, {
|
||||||
mtime: ts,
|
mtime: ts,
|
||||||
});
|
});
|
||||||
console.log("finish exporting");
|
log.info("finish exporting");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { base32, base64url } from "rfc4648";
|
import { base32, base64url } from "rfc4648";
|
||||||
import { bufferToArrayBuffer, hexStringToTypedArray } from "./misc";
|
import { bufferToArrayBuffer, hexStringToTypedArray } from "./misc";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
const DEFAULT_ITER = 20000;
|
const DEFAULT_ITER = 20000;
|
||||||
|
|
||||||
// base32.stringify(Buffer.from('Salted__'))
|
// base32.stringify(Buffer.from('Salted__'))
|
||||||
|
|||||||
+4
-1
@@ -7,6 +7,9 @@ import {
|
|||||||
RemotelySavePluginSettings,
|
RemotelySavePluginSettings,
|
||||||
} from "./baseTypes";
|
} from "./baseTypes";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export const exportQrCodeUri = async (
|
export const exportQrCodeUri = async (
|
||||||
settings: RemotelySavePluginSettings,
|
settings: RemotelySavePluginSettings,
|
||||||
currentVaultName: string,
|
currentVaultName: string,
|
||||||
@@ -19,7 +22,7 @@ export const exportQrCodeUri = async (
|
|||||||
const vault = encodeURIComponent(currentVaultName);
|
const vault = encodeURIComponent(currentVaultName);
|
||||||
const version = encodeURIComponent(pluginVersion);
|
const version = encodeURIComponent(pluginVersion);
|
||||||
const rawUri = `obsidian://${COMMAND_URI}?func=settings&version=${version}&vault=${vault}&data=${data}`;
|
const rawUri = `obsidian://${COMMAND_URI}?func=settings&version=${version}&vault=${vault}&data=${data}`;
|
||||||
// console.log(uri)
|
// log.info(uri)
|
||||||
const imgUri = await QRCode.toDataURL(rawUri);
|
const imgUri = await QRCode.toDataURL(rawUri);
|
||||||
return {
|
return {
|
||||||
rawUri,
|
rawUri,
|
||||||
|
|||||||
+8
-5
@@ -6,6 +6,9 @@ import type { SyncPlanType } from "./sync";
|
|||||||
|
|
||||||
export type LocalForage = typeof localforage;
|
export type LocalForage = typeof localforage;
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export const DEFAULT_DB_VERSION_NUMBER: number = 20211114;
|
export const DEFAULT_DB_VERSION_NUMBER: number = 20211114;
|
||||||
export const DEFAULT_DB_NAME = "remotelysavedb";
|
export const DEFAULT_DB_NAME = "remotelysavedb";
|
||||||
export const DEFAULT_TBL_VERSION = "schemaversion";
|
export const DEFAULT_TBL_VERSION = "schemaversion";
|
||||||
@@ -78,7 +81,7 @@ export const prepareDBs = async () => {
|
|||||||
await migrateDBs(db, originalVersion, DEFAULT_DB_VERSION_NUMBER);
|
await migrateDBs(db, originalVersion, DEFAULT_DB_VERSION_NUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("db connected");
|
log.info("db connected");
|
||||||
return db;
|
return db;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,10 +89,10 @@ export const destroyDBs = async () => {
|
|||||||
// await localforage.dropInstance({
|
// await localforage.dropInstance({
|
||||||
// name: DEFAULT_DB_NAME,
|
// name: DEFAULT_DB_NAME,
|
||||||
// });
|
// });
|
||||||
// console.log("db deleted");
|
// log.info("db deleted");
|
||||||
const req = indexedDB.deleteDatabase(DEFAULT_DB_NAME);
|
const req = indexedDB.deleteDatabase(DEFAULT_DB_NAME);
|
||||||
req.onsuccess = (event) => {
|
req.onsuccess = (event) => {
|
||||||
console.log("db deleted");
|
log.info("db deleted");
|
||||||
};
|
};
|
||||||
req.onblocked = (event) => {
|
req.onblocked = (event) => {
|
||||||
console.warn("trying to delete db but it was blocked");
|
console.warn("trying to delete db but it was blocked");
|
||||||
@@ -128,7 +131,7 @@ export const insertDeleteRecord = async (
|
|||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
fileOrFolder: TAbstractFile
|
fileOrFolder: TAbstractFile
|
||||||
) => {
|
) => {
|
||||||
// console.log(fileOrFolder);
|
// log.info(fileOrFolder);
|
||||||
let k: FileFolderHistoryRecord;
|
let k: FileFolderHistoryRecord;
|
||||||
if (fileOrFolder instanceof TFile) {
|
if (fileOrFolder instanceof TFile) {
|
||||||
k = {
|
k = {
|
||||||
@@ -165,7 +168,7 @@ export const insertRenameRecord = async (
|
|||||||
fileOrFolder: TAbstractFile,
|
fileOrFolder: TAbstractFile,
|
||||||
oldPath: string
|
oldPath: string
|
||||||
) => {
|
) => {
|
||||||
// console.log(fileOrFolder);
|
// log.info(fileOrFolder);
|
||||||
let k: FileFolderHistoryRecord;
|
let k: FileFolderHistoryRecord;
|
||||||
if (fileOrFolder instanceof TFile) {
|
if (fileOrFolder instanceof TFile) {
|
||||||
k = {
|
k = {
|
||||||
|
|||||||
+24
-13
@@ -34,6 +34,10 @@ import { DEFAULT_WEBDAV_CONFIG } from "./remoteForWebdav";
|
|||||||
import { RemotelySaveSettingTab } from "./settings";
|
import { RemotelySaveSettingTab } from "./settings";
|
||||||
import type { SyncStatusType } from "./sync";
|
import type { SyncStatusType } from "./sync";
|
||||||
import { doActualSync, getSyncPlan, isPasswordOk } from "./sync";
|
import { doActualSync, getSyncPlan, isPasswordOk } from "./sync";
|
||||||
|
import { messyConfigToNormal, normalConfigToMessy } from "./configPersist";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
||||||
s3: DEFAULT_S3_CONFIG,
|
s3: DEFAULT_S3_CONFIG,
|
||||||
@@ -42,6 +46,7 @@ const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
|||||||
onedrive: DEFAULT_ONEDRIVE_CONFIG,
|
onedrive: DEFAULT_ONEDRIVE_CONFIG,
|
||||||
password: "",
|
password: "",
|
||||||
serviceType: "s3",
|
serviceType: "s3",
|
||||||
|
currLogLevel: "info",
|
||||||
};
|
};
|
||||||
|
|
||||||
interface OAuth2Info {
|
interface OAuth2Info {
|
||||||
@@ -58,9 +63,10 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
db: InternalDBs;
|
db: InternalDBs;
|
||||||
syncStatus: SyncStatusType;
|
syncStatus: SyncStatusType;
|
||||||
oauth2Info: OAuth2Info;
|
oauth2Info: OAuth2Info;
|
||||||
|
currLogLevel: string;
|
||||||
|
|
||||||
async onload() {
|
async onload() {
|
||||||
console.log(`loading plugin ${this.manifest.id}`);
|
log.info(`loading plugin ${this.manifest.id}`);
|
||||||
|
|
||||||
this.oauth2Info = {
|
this.oauth2Info = {
|
||||||
verifier: "",
|
verifier: "",
|
||||||
@@ -71,6 +77,11 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}; // init
|
}; // init
|
||||||
|
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
|
||||||
|
if (this.settings.currLogLevel !== undefined) {
|
||||||
|
log.setLevel(this.settings.currLogLevel as any);
|
||||||
|
}
|
||||||
|
|
||||||
await this.checkIfOauthExpires();
|
await this.checkIfOauthExpires();
|
||||||
|
|
||||||
await this.prepareDB();
|
await this.prepareDB();
|
||||||
@@ -275,7 +286,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//console.log(`huh ${this.settings.password}`)
|
//log.info(`huh ${this.settings.password}`)
|
||||||
new Notice(
|
new Notice(
|
||||||
`1/7 Remotely Save Sync Preparing (${this.settings.serviceType})`
|
`1/7 Remotely Save Sync Preparing (${this.settings.serviceType})`
|
||||||
);
|
);
|
||||||
@@ -294,14 +305,14 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
() => self.saveSettings()
|
() => self.saveSettings()
|
||||||
);
|
);
|
||||||
const remoteRsp = await client.listFromRemote();
|
const remoteRsp = await client.listFromRemote();
|
||||||
// console.log(remoteRsp);
|
// log.info(remoteRsp);
|
||||||
|
|
||||||
new Notice("3/7 Starting to fetch local meta data.");
|
new Notice("3/7 Starting to fetch local meta data.");
|
||||||
this.syncStatus = "getting_local_meta";
|
this.syncStatus = "getting_local_meta";
|
||||||
const local = this.app.vault.getAllLoadedFiles();
|
const local = this.app.vault.getAllLoadedFiles();
|
||||||
const localHistory = await loadDeleteRenameHistoryTable(this.db);
|
const localHistory = await loadDeleteRenameHistoryTable(this.db);
|
||||||
// console.log(local);
|
// log.info(local);
|
||||||
// console.log(localHistory);
|
// log.info(localHistory);
|
||||||
|
|
||||||
new Notice("4/7 Checking password correct or not.");
|
new Notice("4/7 Checking password correct or not.");
|
||||||
this.syncStatus = "checking_password";
|
this.syncStatus = "checking_password";
|
||||||
@@ -324,7 +335,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
client.serviceType,
|
client.serviceType,
|
||||||
this.settings.password
|
this.settings.password
|
||||||
);
|
);
|
||||||
console.log(syncPlan.mixedStates); // for debugging
|
log.info(syncPlan.mixedStates); // for debugging
|
||||||
await insertSyncPlanRecord(this.db, syncPlan);
|
await insertSyncPlanRecord(this.db, syncPlan);
|
||||||
|
|
||||||
// The operations above are read only and kind of safe.
|
// The operations above are read only and kind of safe.
|
||||||
@@ -346,8 +357,8 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
this.syncStatus = "idle";
|
this.syncStatus = "idle";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `Remotely Save error while ${this.syncStatus}`;
|
const msg = `Remotely Save error while ${this.syncStatus}`;
|
||||||
console.log(msg);
|
log.info(msg);
|
||||||
console.log(error);
|
log.info(error);
|
||||||
new Notice(msg);
|
new Notice(msg);
|
||||||
new Notice(error.message);
|
new Notice(error.message);
|
||||||
this.syncStatus = "idle";
|
this.syncStatus = "idle";
|
||||||
@@ -357,16 +368,16 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
this.addSettingTab(new RemotelySaveSettingTab(this.app, this));
|
this.addSettingTab(new RemotelySaveSettingTab(this.app, this));
|
||||||
|
|
||||||
// this.registerDomEvent(document, "click", (evt: MouseEvent) => {
|
// this.registerDomEvent(document, "click", (evt: MouseEvent) => {
|
||||||
// console.log("click", evt);
|
// log.info("click", evt);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// this.registerInterval(
|
// this.registerInterval(
|
||||||
// window.setInterval(() => console.log("setInterval"), 5 * 60 * 1000)
|
// window.setInterval(() => log.info("setInterval"), 5 * 60 * 1000)
|
||||||
// );
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
onunload() {
|
||||||
console.log(`unloading plugin ${this.manifest.id}`);
|
log.info(`unloading plugin ${this.manifest.id}`);
|
||||||
this.destroyDBs();
|
this.destroyDBs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +385,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
this.settings = Object.assign(
|
this.settings = Object.assign(
|
||||||
{},
|
{},
|
||||||
cloneDeep(DEFAULT_SETTINGS),
|
cloneDeep(DEFAULT_SETTINGS),
|
||||||
await this.loadData()
|
messyConfigToNormal(await this.loadData())
|
||||||
);
|
);
|
||||||
if (this.settings.dropbox.clientID === "") {
|
if (this.settings.dropbox.clientID === "") {
|
||||||
this.settings.dropbox.clientID = DEFAULT_SETTINGS.dropbox.clientID;
|
this.settings.dropbox.clientID = DEFAULT_SETTINGS.dropbox.clientID;
|
||||||
@@ -388,7 +399,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
await this.saveData(this.settings);
|
await this.saveData(normalConfigToMessy(this.settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkIfOauthExpires() {
|
async checkIfOauthExpires() {
|
||||||
|
|||||||
+17
-5
@@ -4,6 +4,9 @@ import * as path from "path";
|
|||||||
import { base32, base64url } from "rfc4648";
|
import { base32, base64url } from "rfc4648";
|
||||||
import XRegExp from "xregexp";
|
import XRegExp from "xregexp";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If any part of the file starts with '.' or '_' then it's a hidden file.
|
* If any part of the file starts with '.' or '_' then it's a hidden file.
|
||||||
* @param item
|
* @param item
|
||||||
@@ -13,7 +16,7 @@ import XRegExp from "xregexp";
|
|||||||
export const isHiddenPath = (item: string, loose: boolean = true) => {
|
export const isHiddenPath = (item: string, loose: boolean = true) => {
|
||||||
const k = path.posix.normalize(item); // TODO: only unix path now
|
const k = path.posix.normalize(item); // TODO: only unix path now
|
||||||
const k2 = k.split("/"); // TODO: only unix path now
|
const k2 = k.split("/"); // TODO: only unix path now
|
||||||
// console.log(k2)
|
// log.info(k2)
|
||||||
for (const singlePart of k2) {
|
for (const singlePart of k2) {
|
||||||
if (singlePart === "." || singlePart === ".." || singlePart === "") {
|
if (singlePart === "." || singlePart === ".." || singlePart === "") {
|
||||||
continue;
|
continue;
|
||||||
@@ -54,14 +57,14 @@ export const getFolderLevels = (x: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mkdirpInVault = async (thePath: string, vault: Vault) => {
|
export const mkdirpInVault = async (thePath: string, vault: Vault) => {
|
||||||
// console.log(thePath);
|
// log.info(thePath);
|
||||||
const foldersToBuild = getFolderLevels(thePath);
|
const foldersToBuild = getFolderLevels(thePath);
|
||||||
// console.log(foldersToBuild);
|
// log.info(foldersToBuild);
|
||||||
for (const folder of foldersToBuild) {
|
for (const folder of foldersToBuild) {
|
||||||
const r = await vault.adapter.exists(folder);
|
const r = await vault.adapter.exists(folder);
|
||||||
// console.log(r);
|
// log.info(r);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
console.log(`mkdir ${folder}`);
|
log.info(`mkdir ${folder}`);
|
||||||
await vault.adapter.mkdir(folder);
|
await vault.adapter.mkdir(folder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,3 +199,12 @@ export const getRandomArrayBuffer = (byteLength: number) => {
|
|||||||
const k = window.crypto.getRandomValues(new Uint8Array(byteLength));
|
const k = window.crypto.getRandomValues(new Uint8Array(byteLength));
|
||||||
return bufferToArrayBuffer(k);
|
return bufferToArrayBuffer(k);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://stackoverflow.com/questions/958908
|
||||||
|
* @param x
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const reverseString = (x: string) => {
|
||||||
|
return [...x].reverse().join("");
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import * as onedrive from "./remoteForOnedrive";
|
|||||||
import * as s3 from "./remoteForS3";
|
import * as s3 from "./remoteForS3";
|
||||||
import * as webdav from "./remoteForWebdav";
|
import * as webdav from "./remoteForWebdav";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export class RemoteClient {
|
export class RemoteClient {
|
||||||
readonly serviceType: SUPPORTED_SERVICES_TYPE;
|
readonly serviceType: SUPPORTED_SERVICES_TYPE;
|
||||||
readonly s3Client?: s3.S3Client;
|
readonly s3Client?: s3.S3Client;
|
||||||
|
|||||||
+13
-10
@@ -12,6 +12,9 @@ import { bufferToArrayBuffer, getFolderLevels, mkdirpInVault } from "./misc";
|
|||||||
|
|
||||||
export { Dropbox } from "dropbox";
|
export { Dropbox } from "dropbox";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export const DEFAULT_DROPBOX_CONFIG: DropboxConfig = {
|
export const DEFAULT_DROPBOX_CONFIG: DropboxConfig = {
|
||||||
accessToken: "",
|
accessToken: "",
|
||||||
clientID: process.env.DEFAULT_DROPBOX_APP_KEY,
|
clientID: process.env.DEFAULT_DROPBOX_APP_KEY,
|
||||||
@@ -208,7 +211,7 @@ export const sendRefreshTokenReq = async (
|
|||||||
appKey: string,
|
appKey: string,
|
||||||
refreshToken: string
|
refreshToken: string
|
||||||
) => {
|
) => {
|
||||||
console.log("start auto getting refreshed Dropbox access token.");
|
log.info("start auto getting refreshed Dropbox access token.");
|
||||||
const resp1 = await fetch("https://api.dropboxapi.com/oauth2/token", {
|
const resp1 = await fetch("https://api.dropboxapi.com/oauth2/token", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
@@ -218,7 +221,7 @@ export const sendRefreshTokenReq = async (
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const resp2 = (await resp1.json()) as DropboxSuccessAuthRes;
|
const resp2 = (await resp1.json()) as DropboxSuccessAuthRes;
|
||||||
console.log("finish auto getting refreshed Dropbox access token.");
|
log.info("finish auto getting refreshed Dropbox access token.");
|
||||||
return resp2;
|
return resp2;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -227,7 +230,7 @@ export const setConfigBySuccessfullAuthInplace = async (
|
|||||||
authRes: DropboxSuccessAuthRes,
|
authRes: DropboxSuccessAuthRes,
|
||||||
saveUpdatedConfigFunc: () => Promise<any> | undefined
|
saveUpdatedConfigFunc: () => Promise<any> | undefined
|
||||||
) => {
|
) => {
|
||||||
console.log("start updating local info of Dropbox token");
|
log.info("start updating local info of Dropbox token");
|
||||||
|
|
||||||
config.accessToken = authRes.access_token;
|
config.accessToken = authRes.access_token;
|
||||||
config.accessTokenExpiresInSeconds = parseInt(authRes.expires_in);
|
config.accessTokenExpiresInSeconds = parseInt(authRes.expires_in);
|
||||||
@@ -249,7 +252,7 @@ export const setConfigBySuccessfullAuthInplace = async (
|
|||||||
await saveUpdatedConfigFunc();
|
await saveUpdatedConfigFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("finish updating local info of Dropbox token");
|
log.info("finish updating local info of Dropbox token");
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -308,9 +311,9 @@ export class WrappedDropboxClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check vault folder
|
// check vault folder
|
||||||
// console.log(`checking remote has folder /${this.vaultName}`);
|
// log.info(`checking remote has folder /${this.vaultName}`);
|
||||||
if (this.vaultFolderExists) {
|
if (this.vaultFolderExists) {
|
||||||
// console.log(`already checked, /${this.vaultName} exist before`)
|
// log.info(`already checked, /${this.vaultName} exist before`)
|
||||||
} else {
|
} else {
|
||||||
const res = await this.dropbox.filesListFolder({
|
const res = await this.dropbox.filesListFolder({
|
||||||
path: "",
|
path: "",
|
||||||
@@ -323,14 +326,14 @@ export class WrappedDropboxClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.vaultFolderExists) {
|
if (!this.vaultFolderExists) {
|
||||||
console.log(`remote does not have folder /${this.vaultName}`);
|
log.info(`remote does not have folder /${this.vaultName}`);
|
||||||
await this.dropbox.filesCreateFolderV2({
|
await this.dropbox.filesCreateFolderV2({
|
||||||
path: `/${this.vaultName}`,
|
path: `/${this.vaultName}`,
|
||||||
});
|
});
|
||||||
console.log(`remote folder /${this.vaultName} created`);
|
log.info(`remote folder /${this.vaultName} created`);
|
||||||
this.vaultFolderExists = true;
|
this.vaultFolderExists = true;
|
||||||
} else {
|
} else {
|
||||||
// console.log(`remote folder /${this.vaultName} exists`);
|
// log.info(`remote folder /${this.vaultName} exists`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +492,7 @@ export const listFromRemote = async (
|
|||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
throw Error(JSON.stringify(res));
|
throw Error(JSON.stringify(res));
|
||||||
}
|
}
|
||||||
// console.log(res);
|
// log.info(res);
|
||||||
|
|
||||||
const contents = res.result.entries;
|
const contents = res.result.entries;
|
||||||
const unifiedContents = contents
|
const unifiedContents = contents
|
||||||
|
|||||||
+24
-21
@@ -28,6 +28,9 @@ import {
|
|||||||
mkdirpInVault,
|
mkdirpInVault,
|
||||||
} from "./misc";
|
} from "./misc";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
const SCOPES = ["User.Read", "Files.ReadWrite.AppFolder", "offline_access"];
|
const SCOPES = ["User.Read", "Files.ReadWrite.AppFolder", "offline_access"];
|
||||||
const REDIRECT_URI = `obsidian://${COMMAND_CALLBACK_ONEDRIVE}`;
|
const REDIRECT_URI = `obsidian://${COMMAND_CALLBACK_ONEDRIVE}`;
|
||||||
|
|
||||||
@@ -117,8 +120,8 @@ export const sendAuthReq = async (
|
|||||||
// code: authCode,
|
// code: authCode,
|
||||||
// codeVerifier: verifier, // PKCE Code Verifier
|
// codeVerifier: verifier, // PKCE Code Verifier
|
||||||
// });
|
// });
|
||||||
// console.log('authResponse')
|
// log.info('authResponse')
|
||||||
// console.log(authResponse)
|
// log.info(authResponse)
|
||||||
// return authResponse;
|
// return authResponse;
|
||||||
|
|
||||||
// Because of the CORS problem,
|
// Because of the CORS problem,
|
||||||
@@ -142,7 +145,7 @@ export const sendAuthReq = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const rsp2 = JSON.parse(rsp1);
|
const rsp2 = JSON.parse(rsp1);
|
||||||
// console.log(rsp2);
|
// log.info(rsp2);
|
||||||
|
|
||||||
if (rsp2.error !== undefined) {
|
if (rsp2.error !== undefined) {
|
||||||
return rsp2 as AccessCodeResponseFailedType;
|
return rsp2 as AccessCodeResponseFailedType;
|
||||||
@@ -171,7 +174,7 @@ export const sendRefreshTokenReq = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const rsp2 = JSON.parse(rsp1);
|
const rsp2 = JSON.parse(rsp1);
|
||||||
// console.log(rsp2);
|
// log.info(rsp2);
|
||||||
|
|
||||||
if (rsp2.error !== undefined) {
|
if (rsp2.error !== undefined) {
|
||||||
return rsp2 as AccessCodeResponseFailedType;
|
return rsp2 as AccessCodeResponseFailedType;
|
||||||
@@ -185,7 +188,7 @@ export const setConfigBySuccessfullAuthInplace = async (
|
|||||||
authRes: AccessCodeResponseSuccessfulType,
|
authRes: AccessCodeResponseSuccessfulType,
|
||||||
saveUpdatedConfigFunc: () => Promise<any> | undefined
|
saveUpdatedConfigFunc: () => Promise<any> | undefined
|
||||||
) => {
|
) => {
|
||||||
console.log("start updating local info of OneDrive token");
|
log.info("start updating local info of OneDrive token");
|
||||||
config.accessToken = authRes.access_token;
|
config.accessToken = authRes.access_token;
|
||||||
config.accessTokenExpiresAtTime =
|
config.accessTokenExpiresAtTime =
|
||||||
Date.now() + authRes.expires_in - 5 * 60 * 1000;
|
Date.now() + authRes.expires_in - 5 * 60 * 1000;
|
||||||
@@ -200,7 +203,7 @@ export const setConfigBySuccessfullAuthInplace = async (
|
|||||||
await saveUpdatedConfigFunc();
|
await saveUpdatedConfigFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("finish updating local info of Onedrive token");
|
log.info("finish updating local info of Onedrive token");
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -352,7 +355,7 @@ class MyAuthProvider implements AuthenticationProvider {
|
|||||||
this.onedriveConfig.accessTokenExpiresAtTime =
|
this.onedriveConfig.accessTokenExpiresAtTime =
|
||||||
currentTs + r2.expires_in * 1000 - 60 * 2 * 1000;
|
currentTs + r2.expires_in * 1000 - 60 * 2 * 1000;
|
||||||
await this.saveUpdatedConfigFunc();
|
await this.saveUpdatedConfigFunc();
|
||||||
console.log("Onedrive accessToken updated");
|
log.info("Onedrive accessToken updated");
|
||||||
return this.onedriveConfig.accessToken;
|
return this.onedriveConfig.accessToken;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -388,26 +391,26 @@ export class WrappedOnedriveClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check vault folder
|
// check vault folder
|
||||||
// console.log(`checking remote has folder /${this.vaultName}`);
|
// log.info(`checking remote has folder /${this.vaultName}`);
|
||||||
if (this.vaultFolderExists) {
|
if (this.vaultFolderExists) {
|
||||||
// console.log(`already checked, /${this.vaultName} exist before`)
|
// log.info(`already checked, /${this.vaultName} exist before`)
|
||||||
} else {
|
} else {
|
||||||
const k = await this.client.api("/drive/special/approot/children").get();
|
const k = await this.client.api("/drive/special/approot/children").get();
|
||||||
// console.log(k);
|
// log.info(k);
|
||||||
this.vaultFolderExists =
|
this.vaultFolderExists =
|
||||||
(k.value as DriveItem[]).filter((x) => x.name === this.vaultName)
|
(k.value as DriveItem[]).filter((x) => x.name === this.vaultName)
|
||||||
.length > 0;
|
.length > 0;
|
||||||
if (!this.vaultFolderExists) {
|
if (!this.vaultFolderExists) {
|
||||||
console.log(`remote does not have folder /${this.vaultName}`);
|
log.info(`remote does not have folder /${this.vaultName}`);
|
||||||
await this.client.api("/drive/special/approot/children").post({
|
await this.client.api("/drive/special/approot/children").post({
|
||||||
name: `${this.vaultName}`,
|
name: `${this.vaultName}`,
|
||||||
folder: {},
|
folder: {},
|
||||||
"@microsoft.graph.conflictBehavior": "replace",
|
"@microsoft.graph.conflictBehavior": "replace",
|
||||||
});
|
});
|
||||||
console.log(`remote folder /${this.vaultName} created`);
|
log.info(`remote folder /${this.vaultName} created`);
|
||||||
this.vaultFolderExists = true;
|
this.vaultFolderExists = true;
|
||||||
} else {
|
} else {
|
||||||
// console.log(`remote folder /${this.vaultName} exists`);
|
// log.info(`remote folder /${this.vaultName} exists`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -479,15 +482,15 @@ export const getRemoteMeta = async (
|
|||||||
) => {
|
) => {
|
||||||
await client.init();
|
await client.init();
|
||||||
const remotePath = getOnedrivePath(fileOrFolderPath, client.vaultName);
|
const remotePath = getOnedrivePath(fileOrFolderPath, client.vaultName);
|
||||||
// console.log(`remotePath=${remotePath}`);
|
// log.info(`remotePath=${remotePath}`);
|
||||||
const rsp = await client.client
|
const rsp = await client.client
|
||||||
.api(remotePath)
|
.api(remotePath)
|
||||||
.select("cTag,eTag,fileSystemInfo,folder,file,name,parentReference,size")
|
.select("cTag,eTag,fileSystemInfo,folder,file,name,parentReference,size")
|
||||||
.get();
|
.get();
|
||||||
// console.log(rsp);
|
// log.info(rsp);
|
||||||
const driveItem = rsp as DriveItem;
|
const driveItem = rsp as DriveItem;
|
||||||
const res = fromDriveItemToRemoteItem(driveItem, client.vaultName);
|
const res = fromDriveItemToRemoteItem(driveItem, client.vaultName);
|
||||||
// console.log(res);
|
// log.info(res);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -507,7 +510,7 @@ export const uploadToRemote = async (
|
|||||||
uploadFile = remoteEncryptedKey;
|
uploadFile = remoteEncryptedKey;
|
||||||
}
|
}
|
||||||
uploadFile = getOnedrivePath(uploadFile, client.vaultName);
|
uploadFile = getOnedrivePath(uploadFile, client.vaultName);
|
||||||
// console.log(`uploadFile=${uploadFile}`);
|
// log.info(`uploadFile=${uploadFile}`);
|
||||||
|
|
||||||
const isFolder = fileOrFolderPath.endsWith("/");
|
const isFolder = fileOrFolderPath.endsWith("/");
|
||||||
|
|
||||||
@@ -567,7 +570,7 @@ export const uploadToRemote = async (
|
|||||||
uploadEventHandlers: {
|
uploadEventHandlers: {
|
||||||
progress: (range?: Range) => {
|
progress: (range?: Range) => {
|
||||||
// Handle progress event
|
// Handle progress event
|
||||||
// console.log(
|
// log.info(
|
||||||
// `uploading ${range.minValue}-${range.maxValue} of ${fileOrFolderPath}`
|
// `uploading ${range.minValue}-${range.maxValue} of ${fileOrFolderPath}`
|
||||||
// );
|
// );
|
||||||
},
|
},
|
||||||
@@ -575,7 +578,7 @@ export const uploadToRemote = async (
|
|||||||
} as LargeFileUploadTaskOptions
|
} as LargeFileUploadTaskOptions
|
||||||
);
|
);
|
||||||
const uploadResult: UploadResult = await task.upload();
|
const uploadResult: UploadResult = await task.upload();
|
||||||
// console.log(uploadResult)
|
// log.info(uploadResult)
|
||||||
const res = await getRemoteMeta(client, uploadFile);
|
const res = await getRemoteMeta(client, uploadFile);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -594,7 +597,7 @@ export const uploadToRemote = async (
|
|||||||
// so use LargeFileUploadTask instead of OneDriveLargeFileUploadTask
|
// so use LargeFileUploadTask instead of OneDriveLargeFileUploadTask
|
||||||
const progress = (range?: Range) => {
|
const progress = (range?: Range) => {
|
||||||
// Handle progress event
|
// Handle progress event
|
||||||
// console.log(
|
// log.info(
|
||||||
// `uploading ${range.minValue}-${range.maxValue} of ${fileOrFolderPath}`
|
// `uploading ${range.minValue}-${range.maxValue} of ${fileOrFolderPath}`
|
||||||
// );
|
// );
|
||||||
};
|
};
|
||||||
@@ -631,7 +634,7 @@ export const uploadToRemote = async (
|
|||||||
options
|
options
|
||||||
);
|
);
|
||||||
const uploadResult: UploadResult = await task.upload();
|
const uploadResult: UploadResult = await task.upload();
|
||||||
// console.log(uploadResult)
|
// log.info(uploadResult)
|
||||||
const res = await getRemoteMeta(client, uploadFile);
|
const res = await getRemoteMeta(client, uploadFile);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -25,6 +25,9 @@ import {
|
|||||||
|
|
||||||
export { S3Client } from "@aws-sdk/client-s3";
|
export { S3Client } from "@aws-sdk/client-s3";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export const DEFAULT_S3_CONFIG = {
|
export const DEFAULT_S3_CONFIG = {
|
||||||
s3Endpoint: "",
|
s3Endpoint: "",
|
||||||
s3Region: "",
|
s3Region: "",
|
||||||
@@ -150,7 +153,7 @@ export const uploadToRemote = async (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
upload.on("httpUploadProgress", (progress) => {
|
upload.on("httpUploadProgress", (progress) => {
|
||||||
// console.log(progress);
|
// log.info(progress);
|
||||||
});
|
});
|
||||||
await upload.done();
|
await upload.done();
|
||||||
|
|
||||||
|
|||||||
+12
-9
@@ -7,6 +7,9 @@ import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt";
|
|||||||
import { bufferToArrayBuffer, getPathFolder, mkdirpInVault } from "./misc";
|
import { bufferToArrayBuffer, getPathFolder, mkdirpInVault } from "./misc";
|
||||||
export type { WebDAVClient } from "webdav/web";
|
export type { WebDAVClient } from "webdav/web";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export const DEFAULT_WEBDAV_CONFIG = {
|
export const DEFAULT_WEBDAV_CONFIG = {
|
||||||
address: "",
|
address: "",
|
||||||
username: "",
|
username: "",
|
||||||
@@ -82,7 +85,7 @@ export class WrappedWebdavClient {
|
|||||||
: AuthType.Password,
|
: AuthType.Password,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("no password");
|
log.info("no password");
|
||||||
this.client = createClient(this.webdavConfig.address);
|
this.client = createClient(this.webdavConfig.address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,12 +96,12 @@ export class WrappedWebdavClient {
|
|||||||
} else {
|
} else {
|
||||||
const res = await this.client.exists(`/${this.vaultName}`);
|
const res = await this.client.exists(`/${this.vaultName}`);
|
||||||
if (res) {
|
if (res) {
|
||||||
// console.log("remote vault folder exits!");
|
// log.info("remote vault folder exits!");
|
||||||
this.vaultFolderExists = true;
|
this.vaultFolderExists = true;
|
||||||
} else {
|
} else {
|
||||||
console.log("remote vault folder not exists, creating");
|
log.info("remote vault folder not exists, creating");
|
||||||
await this.client.createDirectory(`/${this.vaultName}`);
|
await this.client.createDirectory(`/${this.vaultName}`);
|
||||||
console.log("remote vault folder created!");
|
log.info("remote vault folder created!");
|
||||||
this.vaultFolderExists = true;
|
this.vaultFolderExists = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +121,7 @@ export const getRemoteMeta = async (
|
|||||||
) => {
|
) => {
|
||||||
await client.init();
|
await client.init();
|
||||||
const remotePath = getWebdavPath(fileOrFolderPath, client.vaultName);
|
const remotePath = getWebdavPath(fileOrFolderPath, client.vaultName);
|
||||||
// console.log(`remotePath = ${remotePath}`);
|
// log.info(`remotePath = ${remotePath}`);
|
||||||
const res = (await client.client.stat(remotePath, {
|
const res = (await client.client.stat(remotePath, {
|
||||||
details: false,
|
details: false,
|
||||||
})) as FileStat;
|
})) as FileStat;
|
||||||
@@ -158,7 +161,7 @@ export const uploadToRemote = async (
|
|||||||
await client.client.putFileContents(uploadFile, "", {
|
await client.client.putFileContents(uploadFile, "", {
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
onUploadProgress: (progress) => {
|
onUploadProgress: (progress) => {
|
||||||
// console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`);
|
// log.info(`Uploaded ${progress.loaded} bytes of ${progress.total}`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -180,7 +183,7 @@ export const uploadToRemote = async (
|
|||||||
await client.client.putFileContents(uploadFile, remoteContent, {
|
await client.client.putFileContents(uploadFile, remoteContent, {
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
onUploadProgress: (progress) => {
|
onUploadProgress: (progress) => {
|
||||||
console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`);
|
log.info(`Uploaded ${progress.loaded} bytes of ${progress.total}`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -282,10 +285,10 @@ export const deleteFromRemote = async (
|
|||||||
await client.init();
|
await client.init();
|
||||||
try {
|
try {
|
||||||
await client.client.deleteFile(remoteFileName);
|
await client.client.deleteFile(remoteFileName);
|
||||||
// console.log(`delete ${remoteFileName} succeeded`);
|
// log.info(`delete ${remoteFileName} succeeded`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("some error while deleting");
|
console.error("some error while deleting");
|
||||||
console.log(err);
|
log.info(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+66
-6
@@ -20,6 +20,9 @@ import {
|
|||||||
getAuthUrlAndVerifier as getAuthUrlAndVerifierOnedrive,
|
getAuthUrlAndVerifier as getAuthUrlAndVerifierOnedrive,
|
||||||
} from "./remoteForOnedrive";
|
} from "./remoteForOnedrive";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
class PasswordModal extends Modal {
|
class PasswordModal extends Modal {
|
||||||
plugin: RemotelySavePlugin;
|
plugin: RemotelySavePlugin;
|
||||||
newPassword: string;
|
newPassword: string;
|
||||||
@@ -117,6 +120,21 @@ class DropboxAuthModal extends Modal {
|
|||||||
contentEl.createEl("p", {
|
contentEl.createEl("p", {
|
||||||
text: "Finally you should be redirected to Obsidian.",
|
text: "Finally you should be redirected to Obsidian.",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const div2 = contentEl.createDiv();
|
||||||
|
div2.createEl(
|
||||||
|
"button",
|
||||||
|
{
|
||||||
|
text: "Click to copy the auth url",
|
||||||
|
},
|
||||||
|
(el) => {
|
||||||
|
el.onclick = async () => {
|
||||||
|
await navigator.clipboard.writeText(authUrl);
|
||||||
|
new Notice("the auth url copied to clipboard!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
contentEl.createEl("p").createEl("a", {
|
contentEl.createEl("p").createEl("a", {
|
||||||
href: authUrl,
|
href: authUrl,
|
||||||
text: authUrl,
|
text: authUrl,
|
||||||
@@ -157,12 +175,31 @@ export class OnedriveAuthModal extends Modal {
|
|||||||
);
|
);
|
||||||
this.plugin.oauth2Info.verifier = verifier;
|
this.plugin.oauth2Info.verifier = verifier;
|
||||||
|
|
||||||
|
contentEl.createEl("p", {
|
||||||
|
text: "Currently only OneDrive for personal is supported. OneDrive for Business is NOT supported (yet).",
|
||||||
|
});
|
||||||
|
|
||||||
contentEl.createEl("p", {
|
contentEl.createEl("p", {
|
||||||
text: "Visit the address in a browser, and follow the steps.",
|
text: "Visit the address in a browser, and follow the steps.",
|
||||||
});
|
});
|
||||||
contentEl.createEl("p", {
|
contentEl.createEl("p", {
|
||||||
text: "Finally you should be redirected to Obsidian.",
|
text: "Finally you should be redirected to Obsidian.",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const div2 = contentEl.createDiv();
|
||||||
|
div2.createEl(
|
||||||
|
"button",
|
||||||
|
{
|
||||||
|
text: "Click to copy the auth url",
|
||||||
|
},
|
||||||
|
(el) => {
|
||||||
|
el.onclick = async () => {
|
||||||
|
await navigator.clipboard.writeText(authUrl);
|
||||||
|
new Notice("the auth url copied to clipboard!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
contentEl.createEl("p").createEl("a", {
|
contentEl.createEl("p").createEl("a", {
|
||||||
href: authUrl,
|
href: authUrl,
|
||||||
text: authUrl,
|
text: authUrl,
|
||||||
@@ -365,7 +402,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
});
|
});
|
||||||
|
|
||||||
s3Div.createEl("p", {
|
s3Div.createEl("p", {
|
||||||
text: "Disclaimer: The information is stored in PLAIN TEXT locally. Other malicious/harmful/faulty plugins could read the info. If you see any unintentional access to your bucket, please immediately delete the access key on your AWS (or other S3-service provider) settings.",
|
text: "Disclaimer: The information is stored in locally. Other malicious/harmful/faulty plugins could read the info. If you see any unintentional access to your bucket, please immediately delete the access key on your AWS (or other S3-service provider) settings.",
|
||||||
cls: "s3-disclaimer",
|
cls: "s3-disclaimer",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -491,7 +528,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
cls: "dropbox-disclaimer",
|
cls: "dropbox-disclaimer",
|
||||||
});
|
});
|
||||||
dropboxDiv.createEl("p", {
|
dropboxDiv.createEl("p", {
|
||||||
text: "Disclaimer: The information is stored in PLAIN TEXT locally. Other malicious/harmful/faulty plugins could read the info. If you see any unintentional access to your Dropbox, please immediately disconnect this app on https://www.dropbox.com/account/connected_apps .",
|
text: "Disclaimer: The information is stored in locally. Other malicious/harmful/faulty plugins could read the info. If you see any unintentional access to your Dropbox, please immediately disconnect this app on https://www.dropbox.com/account/connected_apps .",
|
||||||
cls: "dropbox-disclaimer",
|
cls: "dropbox-disclaimer",
|
||||||
});
|
});
|
||||||
dropboxDiv.createEl("p", {
|
dropboxDiv.createEl("p", {
|
||||||
@@ -614,13 +651,13 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
"onedrive-hide",
|
"onedrive-hide",
|
||||||
this.plugin.settings.serviceType !== "onedrive"
|
this.plugin.settings.serviceType !== "onedrive"
|
||||||
);
|
);
|
||||||
onedriveDiv.createEl("h2", { text: "Remote For Onedrive" });
|
onedriveDiv.createEl("h2", { text: "Remote For Onedrive (for personal)" });
|
||||||
onedriveDiv.createEl("p", {
|
onedriveDiv.createEl("p", {
|
||||||
text: "Disclaimer: This app is NOT an official Microsoft / Onedrive product.",
|
text: "Disclaimer: This app is NOT an official Microsoft / Onedrive product.",
|
||||||
cls: "onedrive-disclaimer",
|
cls: "onedrive-disclaimer",
|
||||||
});
|
});
|
||||||
onedriveDiv.createEl("p", {
|
onedriveDiv.createEl("p", {
|
||||||
text: "Disclaimer: The information is stored in PLAIN TEXT locally. Other malicious/harmful/faulty plugins could read the info. If you see any unintentional access to your Onedrive, please immediately disconnect this app on https://microsoft.com/consent .",
|
text: "Disclaimer: The information is stored locally. Other malicious/harmful/faulty plugins could read the info. If you see any unintentional access to your Onedrive, please immediately disconnect this app on https://microsoft.com/consent .",
|
||||||
cls: "onedrive-disclaimer",
|
cls: "onedrive-disclaimer",
|
||||||
});
|
});
|
||||||
onedriveDiv.createEl("p", {
|
onedriveDiv.createEl("p", {
|
||||||
@@ -629,6 +666,10 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
}/${this.app.vault.getName()} on your Onedrive.`,
|
}/${this.app.vault.getName()} on your Onedrive.`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onedriveDiv.createEl("p", {
|
||||||
|
text: "Currently only OneDrive for personal is supported. OneDrive for Business is NOT supported (yet).",
|
||||||
|
});
|
||||||
|
|
||||||
const onedriveSelectAuthDiv = onedriveDiv.createDiv();
|
const onedriveSelectAuthDiv = onedriveDiv.createDiv();
|
||||||
const onedriveAuthDiv = onedriveSelectAuthDiv.createDiv({
|
const onedriveAuthDiv = onedriveSelectAuthDiv.createDiv({
|
||||||
cls: "onedrive-auth-button-hide",
|
cls: "onedrive-auth-button-hide",
|
||||||
@@ -724,7 +765,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
webdavDiv.createEl("h2", { text: "Remote For Webdav" });
|
webdavDiv.createEl("h2", { text: "Remote For Webdav" });
|
||||||
|
|
||||||
webdavDiv.createEl("p", {
|
webdavDiv.createEl("p", {
|
||||||
text: "Disclaimer: The information is stored in PLAIN TEXT locally. Other malicious/harmful/faulty plugins may read the info. If you see any unintentional access to your webdav server, please immediately change the username and password.",
|
text: "Disclaimer: The information is stored in locally. Other malicious/harmful/faulty plugins may read the info. If you see any unintentional access to your webdav server, please immediately change the username and password.",
|
||||||
cls: "webdav-disclaimer",
|
cls: "webdav-disclaimer",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -829,7 +870,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
dropdown.addOption("s3", "S3 or compatible");
|
dropdown.addOption("s3", "S3 or compatible");
|
||||||
dropdown.addOption("dropbox", "Dropbox");
|
dropdown.addOption("dropbox", "Dropbox");
|
||||||
dropdown.addOption("webdav", "Webdav (beta)");
|
dropdown.addOption("webdav", "Webdav (beta)");
|
||||||
dropdown.addOption("onedrive", "OneDrive (alpha)");
|
dropdown.addOption("onedrive", "OneDrive for personal (alpha)");
|
||||||
dropdown
|
dropdown
|
||||||
.setValue(this.plugin.settings.serviceType)
|
.setValue(this.plugin.settings.serviceType)
|
||||||
.onChange(async (val: SUPPORTED_SERVICES_TYPE) => {
|
.onChange(async (val: SUPPORTED_SERVICES_TYPE) => {
|
||||||
@@ -886,6 +927,25 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
const debugDiv = containerEl.createEl("div");
|
const debugDiv = containerEl.createEl("div");
|
||||||
debugDiv.createEl("h2", { text: "Debug" });
|
debugDiv.createEl("h2", { text: "Debug" });
|
||||||
|
|
||||||
|
const setConsoleLogLevelDiv = debugDiv.createDiv("div");
|
||||||
|
new Setting(setConsoleLogLevelDiv)
|
||||||
|
.setName("alter console log level")
|
||||||
|
.setDesc(
|
||||||
|
'By default the log level is "info". You can change to "debug" to get verbose infomation in console.'
|
||||||
|
)
|
||||||
|
.addDropdown(async (dropdown) => {
|
||||||
|
dropdown.addOption("info", "info");
|
||||||
|
dropdown.addOption("debug", "debug");
|
||||||
|
dropdown
|
||||||
|
.setValue(this.plugin.settings.currLogLevel)
|
||||||
|
.onChange(async (val: string) => {
|
||||||
|
this.plugin.settings.currLogLevel = val;
|
||||||
|
log.setLevel(val as any);
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
log.info(`the log level is changed to ${val}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
const syncPlanDiv = debugDiv.createEl("div");
|
const syncPlanDiv = debugDiv.createEl("div");
|
||||||
new Setting(syncPlanDiv)
|
new Setting(syncPlanDiv)
|
||||||
.setName("export sync plans")
|
.setName("export sync plans")
|
||||||
|
|||||||
+7
-3
@@ -16,6 +16,9 @@ import {
|
|||||||
import { isHiddenPath, isVaildText, mkdirpInVault } from "./misc";
|
import { isHiddenPath, isVaildText, mkdirpInVault } from "./misc";
|
||||||
import { RemoteClient } from "./remote";
|
import { RemoteClient } from "./remote";
|
||||||
|
|
||||||
|
import * as origLog from "loglevel";
|
||||||
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export type SyncStatusType =
|
export type SyncStatusType =
|
||||||
| "idle"
|
| "idle"
|
||||||
| "preparing"
|
| "preparing"
|
||||||
@@ -573,16 +576,17 @@ export const doActualSync = async (
|
|||||||
)) {
|
)) {
|
||||||
const k2 = k as string;
|
const k2 = k as string;
|
||||||
const v2 = v as FileOrFolderMixedState;
|
const v2 = v as FileOrFolderMixedState;
|
||||||
|
log.debug(`start syncing "${k2}" with plan ${JSON.stringify(v2)}`);
|
||||||
await dispatchOperationToActual(
|
await dispatchOperationToActual(
|
||||||
k as string,
|
k2,
|
||||||
v as FileOrFolderMixedState,
|
v2,
|
||||||
client,
|
client,
|
||||||
db,
|
db,
|
||||||
vault,
|
vault,
|
||||||
password,
|
password,
|
||||||
foldersCreatedBefore
|
foldersCreatedBefore
|
||||||
);
|
);
|
||||||
// console.log(`finished ${k}, with ${setToString(foldersCreatedBefore)}`);
|
// log.info(`finished ${k}, with ${setToString(foldersCreatedBefore)}`);
|
||||||
}
|
}
|
||||||
// await Promise.all(
|
// await Promise.all(
|
||||||
// Object.entries(keyStates)
|
// Object.entries(keyStates)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import * as chai from "chai";
|
||||||
|
import chaiAsPromised from "chai-as-promised";
|
||||||
|
|
||||||
|
import { RemotelySavePluginSettings } from "../src/baseTypes";
|
||||||
|
import { messyConfigToNormal, normalConfigToMessy } from "../src/configPersist";
|
||||||
|
|
||||||
|
chai.use(chaiAsPromised);
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
||||||
|
s3: {
|
||||||
|
s3AccessKeyID: "acc",
|
||||||
|
} as any,
|
||||||
|
webdav: {
|
||||||
|
address: "addr",
|
||||||
|
} as any,
|
||||||
|
dropbox: {
|
||||||
|
username: "测试中文",
|
||||||
|
} as any,
|
||||||
|
onedrive: {
|
||||||
|
username: "test 🍎 emoji",
|
||||||
|
} as any,
|
||||||
|
password: "password",
|
||||||
|
serviceType: "s3",
|
||||||
|
currLogLevel: "info",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Config Persist tests", () => {
|
||||||
|
it("should encrypt go back and forth conrrectly", async () => {
|
||||||
|
const k = DEFAULT_SETTINGS;
|
||||||
|
const k2 = normalConfigToMessy(k);
|
||||||
|
const k3 = messyConfigToNormal(k2);
|
||||||
|
expect(k3).to.deep.equal(k);
|
||||||
|
});
|
||||||
|
});
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"0.2.7": "0.12.15"
|
"0.2.10": "0.12.15"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user