Compare commits

..
12 Commits
Author SHA1 Message Date
fyears 63a66d69f9 bump to 0.3.9 2022-03-20 23:43:48 +08:00
fyears 79fb1d90d0 Merge branch 'Victrid-master' 2022-03-20 23:37:45 +08:00
fyears ac4eff376d Merge branch 'master' of https://github.com/Victrid/remotely-save into Victrid-master 2022-03-20 23:37:26 +08:00
fyears 5512978dee add enc size compute 2022-03-20 23:01:32 +08:00
fyears 2bac0c32dc update lang link 2022-03-20 20:53:04 +08:00
fyears 1cd02913bb reduce size by using lucide not feather-icons 2022-03-20 20:35:09 +08:00
fyears 51f6d600a6 bump to 0.3.8 2022-03-20 16:19:53 +08:00
fyears a4905445c7 link new lang 2022-03-20 16:19:02 +08:00
fyears 12d78444b3 auto build add sub modules support 2022-03-20 15:58:32 +08:00
fyears dffada894f Squashed commit add i18n:
commit 238db51e1fcd22e0b65d3176356ac328481f33ef
Author: fyears <1142836+fyears@users.noreply.github.com>
Date:   Sun Mar 20 15:54:10 2022 +0800

    add langs sub module

commit 30e8cad844821e62a1d6d1e36161c594ddf38111
Author: fyears <1142836+fyears@users.noreply.github.com>
Date:   Sun Mar 20 15:30:45 2022 +0800

    i18n
2022-03-20 15:54:57 +08:00
fyears 50ae224769 add more readme 2022-03-19 14:12:06 +08:00
Victrid 5add7ad645 Add path style configurations for S3. 2022-03-04 23:24:48 +08:00
16 changed files with 596 additions and 381 deletions
+1
View File
@@ -30,6 +30,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
lfs: true lfs: true
submodules: recursive
- name: Checkout LFS - name: Checkout LFS
run: git lfs checkout run: git lfs checkout
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
+3
View File
@@ -0,0 +1,3 @@
[submodule "src/langs"]
path = src/langs
url = https://github.com/remotely-save/langs.git
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "remotely-save", "id": "remotely-save",
"name": "Remotely Save", "name": "Remotely Save",
"version": "0.3.7", "version": "0.3.9",
"minAppVersion": "0.13.21", "minAppVersion": "0.13.21",
"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",
+4 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "remotely-save", "name": "remotely-save",
"version": "0.3.7", "version": "0.3.9",
"description": "This is yet another sync plugin for Obsidian app.", "description": "This is yet another sync plugin for Obsidian app.",
"scripts": { "scripts": {
"dev2": "node esbuild.config.mjs", "dev2": "node esbuild.config.mjs",
@@ -26,11 +26,11 @@
"@microsoft/microsoft-graph-types": "^2.11.0", "@microsoft/microsoft-graph-types": "^2.11.0",
"@types/chai": "^4.2.22", "@types/chai": "^4.2.22",
"@types/chai-as-promised": "^7.1.4", "@types/chai-as-promised": "^7.1.4",
"@types/feather-icons": "^4.7.0",
"@types/jsdom": "^16.2.13", "@types/jsdom": "^16.2.13",
"@types/lodash": "^4.14.178", "@types/lodash": "^4.14.178",
"@types/mime-types": "^2.1.1", "@types/mime-types": "^2.1.1",
"@types/mocha": "^9.0.0", "@types/mocha": "^9.0.0",
"@types/mustache": "^4.1.2",
"@types/node": "^14.14.37", "@types/node": "^14.14.37",
"@types/qrcode": "^1.4.1", "@types/qrcode": "^1.4.1",
"builtin-modules": "^3.2.0", "builtin-modules": "^3.2.0",
@@ -68,12 +68,13 @@
"codemirror": "^5.63.1", "codemirror": "^5.63.1",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"dropbox": "^10.22.0", "dropbox": "^10.22.0",
"feather-icons": "^4.28.0",
"http-status-codes": "^2.2.0", "http-status-codes": "^2.2.0",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"loglevel": "^1.8.0", "loglevel": "^1.8.0",
"lucide": "^0.17.12",
"mime-types": "^2.1.33", "mime-types": "^2.1.33",
"mustache": "^4.2.0",
"nanoid": "^3.1.30", "nanoid": "^3.1.30",
"obsidian": "^0.13.26", "obsidian": "^0.13.26",
"p-queue": "^7.2.0", "p-queue": "^7.2.0",
+4
View File
@@ -3,6 +3,8 @@
* To avoid circular dependency. * To avoid circular dependency.
*/ */
import type { LangType, LangTypeAndAuto } from "./i18n";
export type SUPPORTED_SERVICES_TYPE = "s3" | "webdav" | "dropbox" | "onedrive"; export type SUPPORTED_SERVICES_TYPE = "s3" | "webdav" | "dropbox" | "onedrive";
export interface S3Config { export interface S3Config {
@@ -13,6 +15,7 @@ export interface S3Config {
s3BucketName: string; s3BucketName: string;
bypassCorsLocally?: boolean; bypassCorsLocally?: boolean;
partsConcurrency?: number; partsConcurrency?: number;
forcePathStyle?: boolean;
} }
export interface DropboxConfig { export interface DropboxConfig {
@@ -70,6 +73,7 @@ export interface RemotelySavePluginSettings {
concurrency?: number; concurrency?: number;
syncConfigDir?: boolean; syncConfigDir?: boolean;
syncUnderscoreItems?: boolean; syncUnderscoreItems?: boolean;
lang?: LangTypeAndAuto;
} }
export interface RemoteItem { export interface RemoteItem {
+20
View File
@@ -165,3 +165,23 @@ export const decryptBase64urlToString = async (
) )
); );
}; };
export const getSizeFromOrigToEnc = (x: number) => {
if (x < 0 || !Number.isInteger(x)) {
throw Error(`x=${x} is not a valid size`);
}
return (Math.floor(x / 16) + 1) * 16 + 16;
};
export const getSizeFromEncToOrig = (x: number) => {
if (x < 32 || !Number.isInteger(x)) {
throw Error(`${x} is not a valid size`);
}
if (x % 16 !== 0) {
throw Error(`${x} is not a valid encrypted file size`);
}
return {
minSize: ((x - 16) / 16 - 1) * 16,
maxSize: ((x - 16) / 16 - 1) * 16 + 15,
};
};
+44
View File
@@ -0,0 +1,44 @@
import Mustache from "mustache";
import { moment } from "obsidian";
import { LANGS } from "./langs";
export type LangType = keyof typeof LANGS;
export type LangTypeAndAuto = LangType | "auto";
export type TransItemType = keyof typeof LANGS["en"];
export class I18n {
lang: LangTypeAndAuto;
readonly saveSettingFunc: (tolang: LangTypeAndAuto) => Promise<void>;
constructor(
lang: LangTypeAndAuto,
saveSettingFunc: (tolang: LangTypeAndAuto) => Promise<void>
) {
this.lang = lang;
this.saveSettingFunc = saveSettingFunc;
}
async changeTo(anotherLang: LangTypeAndAuto) {
this.lang = anotherLang;
await this.saveSettingFunc(anotherLang);
}
_get(key: TransItemType) {
let realLang = this.lang;
if (this.lang === "auto" && moment.locale().replace("-", "_") in LANGS) {
realLang = moment.locale().replace("-", "_") as LangType;
} else {
realLang = "en";
}
const res: string =
(LANGS[realLang] as typeof LANGS["en"])[key] || LANGS["en"][key] || key;
return res;
}
t(key: TransItemType, vars?: Record<string, string>) {
if (vars === undefined) {
return this._get(key);
}
return Mustache.render(this._get(key), vars);
}
}
Submodule
+1
Submodule src/langs added at 28730585f8
+129 -56
View File
@@ -1,7 +1,7 @@
import { Modal, Notice, Plugin, Setting, addIcon, setIcon } from "obsidian"; import { Modal, Notice, Plugin, Setting, addIcon, setIcon } from "obsidian";
import cloneDeep from "lodash/cloneDeep"; import cloneDeep from "lodash/cloneDeep";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import feather from "feather-icons"; import { createElement, RotateCcw, RefreshCcw } from "lucide";
import type { RemotelySavePluginSettings } from "./baseTypes"; import type { RemotelySavePluginSettings } from "./baseTypes";
import { import {
COMMAND_CALLBACK, COMMAND_CALLBACK,
@@ -38,6 +38,8 @@ import { fetchMetadataFile, parseRemoteItems, SyncStatusType } from "./sync";
import { doActualSync, getSyncPlan, isPasswordOk } from "./sync"; import { doActualSync, getSyncPlan, isPasswordOk } from "./sync";
import { messyConfigToNormal, normalConfigToMessy } from "./configPersist"; import { messyConfigToNormal, normalConfigToMessy } from "./configPersist";
import { ObsConfigDirFileType, listFilesInObsFolder } from "./obsFolderLister"; import { ObsConfigDirFileType, listFilesInObsFolder } from "./obsFolderLister";
import { I18n } from "./i18n";
import type { LangType, LangTypeAndAuto, TransItemType } from "./i18n";
import * as origLog from "loglevel"; import * as origLog from "loglevel";
import { DeletionOnRemote, MetadataOnRemote } from "./metadataOnRemote"; import { DeletionOnRemote, MetadataOnRemote } from "./metadataOnRemote";
@@ -59,6 +61,7 @@ const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
concurrency: 5, concurrency: 5,
syncConfigDir: false, syncConfigDir: false,
syncUnderscoreItems: false, syncUnderscoreItems: false,
lang: "auto",
}; };
interface OAuth2Info { interface OAuth2Info {
@@ -73,14 +76,13 @@ type SyncTriggerSourceType = "manual" | "auto" | "dry" | "autoOnceInit";
const iconNameSyncWait = `remotely-save-sync-wait`; const iconNameSyncWait = `remotely-save-sync-wait`;
const iconNameSyncRunning = `remotely-save-sync-running`; const iconNameSyncRunning = `remotely-save-sync-running`;
const iconSvgSyncWait = feather.icons["rotate-ccw"].toSvg({
width: 100, const iconSvgSyncWait = createElement(RotateCcw);
height: 100, iconSvgSyncWait.setAttribute("width", "100");
}); iconSvgSyncWait.setAttribute("height", "100");
const iconSvgSyncRunning = feather.icons["refresh-ccw"].toSvg({ const iconSvgSyncRunning = createElement(RefreshCcw);
width: 100, iconSvgSyncRunning.setAttribute("width", "100");
height: 100, iconSvgSyncRunning.setAttribute("height", "100");
});
export default class RemotelySavePlugin extends Plugin { export default class RemotelySavePlugin extends Plugin {
settings: RemotelySavePluginSettings; settings: RemotelySavePluginSettings;
@@ -92,8 +94,13 @@ export default class RemotelySavePlugin extends Plugin {
currSyncMsg?: string; currSyncMsg?: string;
syncRibbon?: HTMLElement; syncRibbon?: HTMLElement;
autoRunIntervalID?: number; autoRunIntervalID?: number;
i18n: I18n;
async syncRun(triggerSource: SyncTriggerSourceType = "manual") { async syncRun(triggerSource: SyncTriggerSourceType = "manual") {
const t = (x: TransItemType, vars?: any) => {
return this.i18n.t(x, vars);
};
const getNotice = (x: string) => { const getNotice = (x: string) => {
// only show notices in manual mode // only show notices in manual mode
// no notice in auto mode // no notice in auto mode
@@ -103,7 +110,12 @@ export default class RemotelySavePlugin extends Plugin {
}; };
if (this.syncStatus !== "idle") { if (this.syncStatus !== "idle") {
// here the notice is shown regardless of triggerSource // here the notice is shown regardless of triggerSource
new Notice(`Remotely Save already running in stage ${this.syncStatus}!`); new Notice(
t("syncrun_alreadyrunning", {
pluginName: this.manifest.name,
syncStatus: this.syncStatus,
})
);
if (this.currSyncMsg !== undefined && this.currSyncMsg !== "") { if (this.currSyncMsg !== undefined && this.currSyncMsg !== "") {
new Notice(this.currSyncMsg); new Notice(this.currSyncMsg);
} }
@@ -126,7 +138,10 @@ export default class RemotelySavePlugin extends Plugin {
setIcon(this.syncRibbon, iconNameSyncRunning); setIcon(this.syncRibbon, iconNameSyncRunning);
this.syncRibbon.setAttribute( this.syncRibbon.setAttribute(
"aria-label", "aria-label",
`${this.manifest.name}: ${triggerSource} syncing` t("syncrun_syncingribbon", {
pluginName: this.manifest.name,
triggerSource: triggerSource,
})
); );
} }
@@ -134,17 +149,26 @@ export default class RemotelySavePlugin extends Plugin {
if (triggerSource === "dry") { if (triggerSource === "dry") {
getNotice( getNotice(
`0/${MAX_STEPS} Remotely Save running in dry mode, not actual file changes would happen.` t("syncrun_step0", {
maxSteps: `${MAX_STEPS}`,
})
); );
} }
//log.info(`huh ${this.settings.password}`) //log.info(`huh ${this.settings.password}`)
getNotice( getNotice(
`1/${MAX_STEPS} Remotely Save Sync Preparing (${this.settings.serviceType})` t("syncrun_step1", {
maxSteps: `${MAX_STEPS}`,
serviceType: this.settings.serviceType,
})
); );
this.syncStatus = "preparing"; this.syncStatus = "preparing";
getNotice(`2/${MAX_STEPS} Starting to fetch remote meta data.`); getNotice(
t("syncrun_step2", {
maxSteps: `${MAX_STEPS}`,
})
);
this.syncStatus = "getting_remote_files_list"; this.syncStatus = "getting_remote_files_list";
const self = this; const self = this;
const client = new RemoteClient( const client = new RemoteClient(
@@ -159,18 +183,26 @@ export default class RemotelySavePlugin extends Plugin {
const remoteRsp = await client.listFromRemote(); const remoteRsp = await client.listFromRemote();
log.debug(remoteRsp); log.debug(remoteRsp);
getNotice(`3/${MAX_STEPS} Checking password correct or not.`); getNotice(
t("syncrun_step3", {
maxSteps: `${MAX_STEPS}`,
})
);
this.syncStatus = "checking_password"; this.syncStatus = "checking_password";
const passwordCheckResult = await isPasswordOk( const passwordCheckResult = await isPasswordOk(
remoteRsp.Contents, remoteRsp.Contents,
this.settings.password this.settings.password
); );
if (!passwordCheckResult.ok) { if (!passwordCheckResult.ok) {
getNotice("something goes wrong while checking password"); getNotice(t("syncrun_passworderr"));
throw Error(passwordCheckResult.reason); throw Error(passwordCheckResult.reason);
} }
getNotice(`4/${MAX_STEPS} Trying to fetch extra meta data from remote.`); getNotice(
t("syncrun_step4", {
maxSteps: `${MAX_STEPS}`,
})
);
this.syncStatus = "getting_remote_extra_meta"; this.syncStatus = "getting_remote_extra_meta";
const { remoteStates, metadataFile } = await parseRemoteItems( const { remoteStates, metadataFile } = await parseRemoteItems(
remoteRsp.Contents, remoteRsp.Contents,
@@ -186,7 +218,11 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.password this.settings.password
); );
getNotice(`5/${MAX_STEPS} Starting to fetch local meta data.`); getNotice(
t("syncrun_step5", {
maxSteps: `${MAX_STEPS}`,
})
);
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 loadDeleteRenameHistoryTableByVault( const localHistory = await loadDeleteRenameHistoryTableByVault(
@@ -204,7 +240,11 @@ export default class RemotelySavePlugin extends Plugin {
// log.info(local); // log.info(local);
// log.info(localHistory); // log.info(localHistory);
getNotice(`6/${MAX_STEPS} Starting to generate sync plan.`); getNotice(
t("syncrun_step6", {
maxSteps: `${MAX_STEPS}`,
})
);
this.syncStatus = "generating_plan"; this.syncStatus = "generating_plan";
const { plan, sortedKeys, deletions } = await getSyncPlan( const { plan, sortedKeys, deletions } = await getSyncPlan(
remoteStates, remoteStates,
@@ -232,7 +272,11 @@ export default class RemotelySavePlugin extends Plugin {
// The operations below begins to write or delete (!!!) something. // The operations below begins to write or delete (!!!) something.
if (triggerSource !== "dry") { if (triggerSource !== "dry") {
getNotice(`7/${MAX_STEPS} Remotely Save Sync data exchanging!`); getNotice(
t("syncrun_step7", {
maxSteps: `${MAX_STEPS}`,
})
);
this.syncStatus = "syncing"; this.syncStatus = "syncing";
await doActualSync( await doActualSync(
@@ -254,11 +298,17 @@ export default class RemotelySavePlugin extends Plugin {
} else { } else {
this.syncStatus = "syncing"; this.syncStatus = "syncing";
getNotice( getNotice(
`7/${MAX_STEPS} Remotely Save real sync is skipped in dry run mode.` t("syncrun_step7skip", {
maxSteps: `${MAX_STEPS}`,
})
); );
} }
getNotice(`8/${MAX_STEPS} Remotely Save finish!`); getNotice(
t("syncrun_step8", {
maxSteps: `${MAX_STEPS}`,
})
);
this.syncStatus = "finish"; this.syncStatus = "finish";
this.syncStatus = "idle"; this.syncStatus = "idle";
@@ -273,11 +323,12 @@ export default class RemotelySavePlugin extends Plugin {
}-${Date.now()}: finish sync, triggerSource=${triggerSource}` }-${Date.now()}: finish sync, triggerSource=${triggerSource}`
); );
} catch (error) { } catch (error) {
const msg = `${ const msg = t("syncrun_abort", {
this.manifest.id manifestID: this.manifest.id,
}-${Date.now()}: abort sync, triggerSource=${triggerSource}, error while ${ theDate: `${Date.now()}`,
this.syncStatus triggerSource: triggerSource,
}`; syncStatus: this.syncStatus,
});
log.info(msg); log.info(msg);
log.info(error); log.info(error);
getNotice(msg); getNotice(msg);
@@ -293,8 +344,8 @@ export default class RemotelySavePlugin extends Plugin {
async onload() { async onload() {
log.info(`loading plugin ${this.manifest.id}`); log.info(`loading plugin ${this.manifest.id}`);
addIcon(iconNameSyncWait, iconSvgSyncWait); addIcon(iconNameSyncWait, iconSvgSyncWait.outerHTML);
addIcon(iconNameSyncRunning, iconSvgSyncRunning); addIcon(iconNameSyncRunning, iconSvgSyncRunning.outerHTML);
this.oauth2Info = { this.oauth2Info = {
verifier: "", verifier: "",
@@ -308,6 +359,15 @@ export default class RemotelySavePlugin extends Plugin {
await this.loadSettings(); await this.loadSettings();
// lang should be load early, but after settings
this.i18n = new I18n(this.settings.lang, async (lang: LangTypeAndAuto) => {
this.settings.lang = lang;
await this.saveSettings();
});
const t = (x: TransItemType, vars?: any) => {
return this.i18n.t(x, vars);
};
if (this.settings.currLogLevel !== undefined) { if (this.settings.currLogLevel !== undefined) {
log.setLevel(this.settings.currLogLevel as any); log.setLevel(this.settings.currLogLevel as any);
} }
@@ -353,7 +413,9 @@ export default class RemotelySavePlugin extends Plugin {
this.settings = Object.assign({}, this.settings, copied); this.settings = Object.assign({}, this.settings, copied);
this.saveSettings(); this.saveSettings();
new Notice( new Notice(
`New not-oauth2 settings for ${this.manifest.name} saved. Reopen the plugin Settings to the effect.` t("protocol_saveqr", {
manifestName: this.manifest.name,
})
); );
} }
}); });
@@ -362,9 +424,9 @@ export default class RemotelySavePlugin extends Plugin {
COMMAND_CALLBACK, COMMAND_CALLBACK,
async (inputParams) => { async (inputParams) => {
new Notice( new Notice(
`Your uri call a callback that's not supported yet: ${JSON.stringify( t("protocol_callbacknotsupported", {
inputParams params: JSON.stringify(inputParams),
)}` })
); );
} }
); );
@@ -375,11 +437,13 @@ export default class RemotelySavePlugin extends Plugin {
if (inputParams.code !== undefined) { if (inputParams.code !== undefined) {
if (this.oauth2Info.helperModal !== undefined) { if (this.oauth2Info.helperModal !== undefined) {
this.oauth2Info.helperModal.contentEl.empty(); this.oauth2Info.helperModal.contentEl.empty();
t("protocol_dropbox_connecting")
.split("\n")
.forEach((val) => {
this.oauth2Info.helperModal.contentEl.createEl("p", { this.oauth2Info.helperModal.contentEl.createEl("p", {
text: "Connecting to Dropbox...", text: val,
}); });
this.oauth2Info.helperModal.contentEl.createEl("p", {
text: "Please DO NOT close this modal.",
}); });
} }
@@ -410,7 +474,11 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.dropbox.username = username; this.settings.dropbox.username = username;
await this.saveSettings(); await this.saveSettings();
new Notice(`Good! We've connected to Dropbox as user ${username}!`); new Notice(
t("protocol_dropbox_connect_succ", {
username: username,
})
);
this.oauth2Info.verifier = ""; // reset it this.oauth2Info.verifier = ""; // reset it
this.oauth2Info.helperModal?.close(); // close it this.oauth2Info.helperModal?.close(); // close it
@@ -423,7 +491,9 @@ export default class RemotelySavePlugin extends Plugin {
this.oauth2Info.authDiv = undefined; this.oauth2Info.authDiv = undefined;
this.oauth2Info.revokeAuthSetting?.setDesc( this.oauth2Info.revokeAuthSetting?.setDesc(
`You've connected as user ${this.settings.dropbox.username}. If you want to disconnect, click this button.` t("protocol_dropbox_connect_succ_revoke", {
username: this.settings.dropbox.username,
})
); );
this.oauth2Info.revokeAuthSetting = undefined; this.oauth2Info.revokeAuthSetting = undefined;
this.oauth2Info.revokeDiv?.toggleClass( this.oauth2Info.revokeDiv?.toggleClass(
@@ -432,13 +502,11 @@ export default class RemotelySavePlugin extends Plugin {
); );
this.oauth2Info.revokeDiv = undefined; this.oauth2Info.revokeDiv = undefined;
} else { } else {
new Notice( new Notice(t("protocol_dropbox_connect_fail"));
"Something went wrong from response from Dropbox. Maybe you rejected the auth?"
);
throw Error( throw Error(
`do not know how to deal with the callback: ${JSON.stringify( t("protocol_dropbox_connect_unknown", {
inputParams params: JSON.stringify(inputParams),
)}` })
); );
} }
} }
@@ -450,11 +518,13 @@ export default class RemotelySavePlugin extends Plugin {
if (inputParams.code !== undefined) { if (inputParams.code !== undefined) {
if (this.oauth2Info.helperModal !== undefined) { if (this.oauth2Info.helperModal !== undefined) {
this.oauth2Info.helperModal.contentEl.empty(); this.oauth2Info.helperModal.contentEl.empty();
t("protocol_onedrive_connecting")
.split("\n")
.forEach((val) => {
this.oauth2Info.helperModal.contentEl.createEl("p", { this.oauth2Info.helperModal.contentEl.createEl("p", {
text: "Connecting to Onedrive...", text: val,
}); });
this.oauth2Info.helperModal.contentEl.createEl("p", {
text: "Please DO NOT close this modal.",
}); });
} }
@@ -499,7 +569,9 @@ export default class RemotelySavePlugin extends Plugin {
this.oauth2Info.authDiv = undefined; this.oauth2Info.authDiv = undefined;
this.oauth2Info.revokeAuthSetting?.setDesc( this.oauth2Info.revokeAuthSetting?.setDesc(
`You've connected as user ${this.settings.onedrive.username}. If you want to disconnect, click this button.` t("protocol_onedrive_connect_succ_revoke", {
username: this.settings.onedrive.username,
})
); );
this.oauth2Info.revokeAuthSetting = undefined; this.oauth2Info.revokeAuthSetting = undefined;
this.oauth2Info.revokeDiv?.toggleClass( this.oauth2Info.revokeDiv?.toggleClass(
@@ -508,13 +580,11 @@ export default class RemotelySavePlugin extends Plugin {
); );
this.oauth2Info.revokeDiv = undefined; this.oauth2Info.revokeDiv = undefined;
} else { } else {
new Notice( new Notice(t("protocol_onedrive_connect_fail"));
"Something went wrong from response from OneDrive. Maybe you rejected the auth?"
);
throw Error( throw Error(
`do not know how to deal with the callback: ${JSON.stringify( t("protocol_onedrive_connect_unknown", {
inputParams params: JSON.stringify(inputParams),
)}` })
); );
} }
} }
@@ -528,7 +598,7 @@ export default class RemotelySavePlugin extends Plugin {
this.addCommand({ this.addCommand({
id: "start-sync", id: "start-sync",
name: "start sync", name: t("command_startsync"),
icon: iconNameSyncWait, icon: iconNameSyncWait,
callback: async () => { callback: async () => {
this.syncRun("manual"); this.syncRun("manual");
@@ -537,7 +607,7 @@ export default class RemotelySavePlugin extends Plugin {
this.addCommand({ this.addCommand({
id: "start-sync-dry-run", id: "start-sync-dry-run",
name: "start sync (dry run only)", name: t("command_drynrun"),
icon: iconNameSyncWait, icon: iconNameSyncWait,
callback: async () => { callback: async () => {
this.syncRun("dry"); this.syncRun("dry");
@@ -588,6 +658,9 @@ export default class RemotelySavePlugin extends Plugin {
if (this.settings.s3.partsConcurrency === undefined) { if (this.settings.s3.partsConcurrency === undefined) {
this.settings.s3.partsConcurrency = 20; this.settings.s3.partsConcurrency = 20;
} }
if (this.settings.s3.forcePathStyle === undefined) {
this.settings.s3.forcePathStyle = false;
}
} }
async saveSettings() { async saveSettings() {
+3
View File
@@ -155,6 +155,7 @@ export const DEFAULT_S3_CONFIG = {
s3BucketName: "", s3BucketName: "",
bypassCorsLocally: true, bypassCorsLocally: true,
partsConcurrency: 20, partsConcurrency: 20,
forcePathStyle: false,
}; };
export type S3ObjectType = _Object; export type S3ObjectType = _Object;
@@ -192,6 +193,7 @@ export const getS3Client = (s3Config: S3Config) => {
const s3Client = new S3Client({ const s3Client = new S3Client({
region: s3Config.s3Region, region: s3Config.s3Region,
endpoint: endpoint, endpoint: endpoint,
forcePathStyle: s3Config.forcePathStyle,
credentials: { credentials: {
accessKeyId: s3Config.s3AccessKeyID, accessKeyId: s3Config.s3AccessKeyID,
secretAccessKey: s3Config.s3SecretAccessKey, secretAccessKey: s3Config.s3SecretAccessKey,
@@ -203,6 +205,7 @@ export const getS3Client = (s3Config: S3Config) => {
const s3Client = new S3Client({ const s3Client = new S3Client({
region: s3Config.s3Region, region: s3Config.s3Region,
endpoint: endpoint, endpoint: endpoint,
forcePathStyle: s3Config.forcePathStyle,
credentials: { credentials: {
accessKeyId: s3Config.s3AccessKeyID, accessKeyId: s3Config.s3AccessKeyID,
secretAccessKey: s3Config.s3SecretAccessKey, secretAccessKey: s3Config.s3SecretAccessKey,
+299 -262
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -984,7 +984,8 @@ const splitThreeSteps = (syncPlan: SyncPlanType, sortedKeys: string[]) => {
} }
} }
// the deletionOps should be run from shadowest to deepest // the deletionOps should be run from max level to min level
// right now it is sorted by level from min to max (NOT length of key!)
// so we need to reverse it! // so we need to reverse it!
deletionOps.reverse(); // inplace reverse deletionOps.reverse(); // inplace reverse
+13 -19
View File
@@ -1,5 +1,6 @@
import { App, Modal, Notice, PluginSettingTab, Setting } from "obsidian"; import { App, Modal, Notice, PluginSettingTab, Setting } from "obsidian";
import type RemotelySavePlugin from "./main"; // unavoidable import type RemotelySavePlugin from "./main"; // unavoidable
import type { TransItemType } from "./i18n";
import * as origLog from "loglevel"; import * as origLog from "loglevel";
const log = origLog.getLogger("rs-default"); const log = origLog.getLogger("rs-default");
@@ -13,40 +14,33 @@ export class SyncAlgoV2Modal extends Modal {
} }
onOpen() { onOpen() {
let { contentEl } = this; let { contentEl } = this;
const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars);
};
contentEl.createEl("h2", { contentEl.createEl("h2", {
text: "Remotely Save has a better sync algorithm", text: t("syncalgov2_title"),
}); });
const texts = [
"Welcome to use Remotely Save!",
"From version 0.3.0, a new algorithm has been developed, but it needs uploading extra meta data files _remotely-save-metadata-on-remote.{json,bin} to YOUR configured cloud destinations, besides your notes.",
"So that, for example, the second device can know that what files/folders have been deleted on the first device by reading those files.",
'If you agree, plase click the button "agree", and enjoy the plugin! AND PLEASE REMEMBER TO BACKUP YOUR VAULT FIRSTLY!',
'If you do not agree, you should stop using the current and later versions of Remotely Save. You could consider manually install the old version 0.2.14 which uses old algorithm and does not upload any extra meta data files. By clicking the "Do not agree" button, the plugin will unload itself, and you need to manually disable it in Obsidian settings.',
];
const ul = contentEl.createEl("ul"); const ul = contentEl.createEl("ul");
t("syncalgov2_texts")
for (const t of texts) { .split("\n")
.forEach((val) => {
ul.createEl("li", { ul.createEl("li", {
text: t, text: val,
});
}); });
}
new Setting(contentEl) new Setting(contentEl)
.addButton((button) => { .addButton((button) => {
button.setButtonText("Agree"); button.setButtonText(t("syncalgov2_button_agree"));
button.onClick(async () => { button.onClick(async () => {
this.agree = true; this.agree = true;
this.close(); this.close();
}); });
}) })
.addButton((button) => { .addButton((button) => {
button.setButtonText("Do not agree"); button.setButtonText(t("syncalgov2_button_disagree"));
button.onClick(() => { button.onClick(() => {
this.close(); this.close();
}); });
+32
View File
@@ -8,6 +8,8 @@ import {
encryptArrayBuffer, encryptArrayBuffer,
encryptStringToBase32, encryptStringToBase32,
encryptStringToBase64url, encryptStringToBase64url,
getSizeFromEncToOrig,
getSizeFromOrigToEnc,
} from "../src/encrypt"; } from "../src/encrypt";
import { base64ToBase64url, bufferToArrayBuffer } from "../src/misc"; import { base64ToBase64url, bufferToArrayBuffer } from "../src/misc";
@@ -110,4 +112,34 @@ describe("Encryption tests", () => {
expect(Buffer.from(dec).equals(Buffer.from(opensslArrBuf))).to.be.true; expect(Buffer.from(dec).equals(Buffer.from(opensslArrBuf))).to.be.true;
}); });
it("should get size from origin to encrypted correctly", () => {
expect(() => getSizeFromOrigToEnc(-1)).to.throw();
expect(() => getSizeFromOrigToEnc(0.5)).to.throw();
expect(getSizeFromOrigToEnc(0)).equals(32);
expect(getSizeFromOrigToEnc(15)).equals(32);
expect(getSizeFromOrigToEnc(16)).equals(48);
expect(getSizeFromOrigToEnc(31)).equals(48);
expect(getSizeFromOrigToEnc(32)).equals(64);
expect(getSizeFromOrigToEnc(14787203)).equals(14787232);
});
it("should get size from encrypted to origin correctly", () => {
expect(() => getSizeFromEncToOrig(-1)).to.throw();
expect(() => getSizeFromEncToOrig(30)).to.throw();
expect(getSizeFromEncToOrig(32)).to.deep.equal({
minSize: 0,
maxSize: 15,
});
expect(getSizeFromEncToOrig(48)).to.deep.equal({
minSize: 16,
maxSize: 31,
});
expect(() => getSizeFromEncToOrig(14787231)).to.throw();
let { minSize, maxSize } = getSizeFromEncToOrig(14787232);
expect(minSize <= 14787203 && 14787203 <= maxSize).to.be.true;
});
}); });
+1
View File
@@ -9,6 +9,7 @@
"noImplicitAny": true, "noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "node",
// "allowSyntheticDefaultImports": true, // "allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"importHelpers": true, "importHelpers": true,
"isolatedModules": true, "isolatedModules": true,
+1 -1
View File
@@ -1,4 +1,4 @@
{ {
"0.3.2": "0.12.15", "0.3.2": "0.12.15",
"0.3.7": "0.13.21" "0.3.9": "0.13.21"
} }