Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
474216b9ab | ||
|
|
99f143115b | ||
|
|
39940deb3f | ||
|
|
df53ea957f | ||
|
|
0cf8023278 | ||
|
|
be1386b345 | ||
|
|
9a189d63c9 | ||
|
|
00d0c557df | ||
|
|
a70a53eeec | ||
|
|
8cec2b00eb | ||
|
|
6efe52b26e |
@@ -20,7 +20,7 @@ The list is for information purposes only.
|
||||
| [Nginx `ngx_http_dav_module`](http://nginx.org/en/docs/http/ngx_http_dav_module.html) | Yes? | | Yes? | | Yes? | ? |
|
||||
| NextCloud | Yes? | | Yes? | | Yes? | No CORS config by default. |
|
||||
| OwnCloud | Yes? | | Yes? | | Yes? | No CORS config by default. |
|
||||
| Seafile | Yes? | | Yes? | | Yes? | |
|
||||
| Seafile | Yes | | Yes | | Yes? | No CORS config by default. |
|
||||
| `rclone serve webdav` | Yes | | Yes | | Yes | No CORS support. |
|
||||
| [Nutstore 坚果云](https://www.jianguoyun.com/) | Yes | | Yes | | Yes | No CORS support. |
|
||||
| [TeraCLOUD](https://teracloud.jp/en/) | Yes | | Yes | | Yes | No CORS support. |
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "remotely-save",
|
||||
"name": "Remotely Save",
|
||||
"version": "0.3.17",
|
||||
"version": "0.3.18",
|
||||
"minAppVersion": "0.13.21",
|
||||
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
|
||||
"author": "fyears",
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "remotely-save",
|
||||
"version": "0.3.17",
|
||||
"version": "0.3.18",
|
||||
"description": "This is yet another sync plugin for Obsidian app.",
|
||||
"scripts": {
|
||||
"dev2": "node esbuild.config.mjs",
|
||||
@@ -51,13 +51,13 @@
|
||||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.54.0",
|
||||
"@aws-sdk/fetch-http-handler": "^3.54.0",
|
||||
"@aws-sdk/lib-storage": "^3.54.0",
|
||||
"@aws-sdk/protocol-http": "^3.54.0",
|
||||
"@aws-sdk/querystring-builder": "^3.54.0",
|
||||
"@aws-sdk/signature-v4-crt": "^3.54.0",
|
||||
"@aws-sdk/types": "^3.54.0",
|
||||
"@aws-sdk/client-s3": "^3.67.0",
|
||||
"@aws-sdk/fetch-http-handler": "^3.58.0",
|
||||
"@aws-sdk/lib-storage": "^3.67.0",
|
||||
"@aws-sdk/protocol-http": "^3.58.0",
|
||||
"@aws-sdk/querystring-builder": "^3.55.0",
|
||||
"@aws-sdk/signature-v4-crt": "^3.66.0",
|
||||
"@aws-sdk/types": "^3.55.0",
|
||||
"@azure/msal-node": "^1.6.0",
|
||||
"@fyears/tsqueue": "^1.0.1",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.1",
|
||||
|
||||
+70
-4
@@ -11,14 +11,73 @@ import {
|
||||
DEFAULT_DEBUG_FOLDER,
|
||||
DEFAULT_LOG_HISTORY_FILE_PREFIX,
|
||||
DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX,
|
||||
FileOrFolderMixedState,
|
||||
} from "./baseTypes";
|
||||
|
||||
import { log } from "./moreOnLog";
|
||||
|
||||
const turnSyncPlanToTable = (record: string) => {
|
||||
const syncPlan: SyncPlanType = JSON.parse(record);
|
||||
const { ts, tsFmt, remoteType, mixedStates } = syncPlan;
|
||||
|
||||
type allowedHeadersType = keyof FileOrFolderMixedState;
|
||||
const headers: allowedHeadersType[] = [
|
||||
"key",
|
||||
"remoteEncryptedKey",
|
||||
"existLocal",
|
||||
"sizeLocal",
|
||||
"mtimeLocal",
|
||||
"deltimeLocal",
|
||||
"changeLocalMtimeUsingMapping",
|
||||
"existRemote",
|
||||
"sizeRemote",
|
||||
"mtimeRemote",
|
||||
"deltimeRemote",
|
||||
"changeRemoteMtimeUsingMapping",
|
||||
"decision",
|
||||
"decisionBranch",
|
||||
];
|
||||
|
||||
const lines = [
|
||||
`ts: ${ts}${tsFmt !== undefined ? " / " + tsFmt : ""}`,
|
||||
`remoteType: ${remoteType}`,
|
||||
`| ${headers.join(" | ")} |`,
|
||||
`| ${headers.map((x) => "---").join(" | ")} |`,
|
||||
];
|
||||
for (const [k1, v1] of Object.entries(syncPlan.mixedStates)) {
|
||||
const k = k1 as string;
|
||||
const v = v1 as FileOrFolderMixedState;
|
||||
const singleLine = [];
|
||||
for (const h of headers) {
|
||||
const field = v[h];
|
||||
if (field === undefined) {
|
||||
singleLine.push("");
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
h === "mtimeLocal" ||
|
||||
h === "deltimeLocal" ||
|
||||
h === "mtimeRemote" ||
|
||||
h === "deltimeRemote"
|
||||
) {
|
||||
const fmt = v[(h + "Fmt") as allowedHeadersType] as string;
|
||||
const s = `${field}${fmt !== undefined ? " / " + fmt : ""}`;
|
||||
singleLine.push(s);
|
||||
} else {
|
||||
singleLine.push(field);
|
||||
}
|
||||
}
|
||||
lines.push(`| ${singleLine.join(" | ")} |`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
};
|
||||
|
||||
export const exportVaultSyncPlansToFiles = async (
|
||||
db: InternalDBs,
|
||||
vault: Vault,
|
||||
vaultRandomID: string
|
||||
vaultRandomID: string,
|
||||
toFormat: "table" | "json" = "json"
|
||||
) => {
|
||||
log.info("exporting");
|
||||
await mkdirpInVault(DEFAULT_DEBUG_FOLDER, vault);
|
||||
@@ -27,9 +86,16 @@ export const exportVaultSyncPlansToFiles = async (
|
||||
if (records.length === 0) {
|
||||
md = "No sync plans history found";
|
||||
} else {
|
||||
md =
|
||||
"Sync plans found:\n\n" +
|
||||
records.map((x) => "```json\n" + x + "\n```\n").join("\n");
|
||||
if (toFormat === "json") {
|
||||
md =
|
||||
"Sync plans found:\n\n" +
|
||||
records.map((x) => "```json\n" + x + "\n```\n").join("\n");
|
||||
} else if (toFormat === "table") {
|
||||
md =
|
||||
"Sync plans found:\n\n" + records.map(turnSyncPlanToTable).join("\n\n");
|
||||
} else {
|
||||
const _: never = toFormat;
|
||||
}
|
||||
}
|
||||
const ts = Date.now();
|
||||
const filePath = `${DEFAULT_DEBUG_FOLDER}${DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX}${ts}.md`;
|
||||
|
||||
+1
-1
Submodule src/langs updated: c46d302441...470fe141ef
+58
-3
@@ -8,7 +8,7 @@ import {
|
||||
FileSystemAdapter,
|
||||
} from "obsidian";
|
||||
import cloneDeep from "lodash/cloneDeep";
|
||||
import { createElement, RotateCcw, RefreshCcw } from "lucide";
|
||||
import { createElement, RotateCcw, RefreshCcw, FileText } from "lucide";
|
||||
import type { RemotelySavePluginSettings } from "./baseTypes";
|
||||
import {
|
||||
COMMAND_CALLBACK,
|
||||
@@ -57,6 +57,10 @@ import { applyPresetRulesInplace } from "./presetRules";
|
||||
|
||||
import { applyLogWriterInplace, log } from "./moreOnLog";
|
||||
import AggregateError from "aggregate-error";
|
||||
import {
|
||||
exportVaultLoggerOutputToFiles,
|
||||
exportVaultSyncPlansToFiles,
|
||||
} from "./debugMode";
|
||||
|
||||
const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
||||
s3: DEFAULT_S3_CONFIG,
|
||||
@@ -89,6 +93,7 @@ type SyncTriggerSourceType = "manual" | "auto" | "dry" | "autoOnceInit";
|
||||
|
||||
const iconNameSyncWait = `remotely-save-sync-wait`;
|
||||
const iconNameSyncRunning = `remotely-save-sync-running`;
|
||||
const iconNameLogs = `remotely-save-logs`;
|
||||
|
||||
const getIconSvg = () => {
|
||||
const iconSvgSyncWait = createElement(RotateCcw);
|
||||
@@ -97,13 +102,18 @@ const getIconSvg = () => {
|
||||
const iconSvgSyncRunning = createElement(RefreshCcw);
|
||||
iconSvgSyncRunning.setAttribute("width", "100");
|
||||
iconSvgSyncRunning.setAttribute("height", "100");
|
||||
const iconSvgLogs = createElement(FileText);
|
||||
iconSvgLogs.setAttribute("width", "100");
|
||||
iconSvgLogs.setAttribute("height", "100");
|
||||
const res = {
|
||||
iconSvgSyncWait: iconSvgSyncWait.outerHTML,
|
||||
iconSvgSyncRunning: iconSvgSyncRunning.outerHTML,
|
||||
iconSvgLogs: iconSvgLogs.outerHTML,
|
||||
};
|
||||
|
||||
iconSvgSyncWait.empty();
|
||||
iconSvgSyncRunning.empty();
|
||||
iconSvgLogs.empty();
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -204,7 +214,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
() => self.saveSettings()
|
||||
);
|
||||
const remoteRsp = await client.listFromRemote();
|
||||
log.debug(remoteRsp);
|
||||
// log.debug(remoteRsp);
|
||||
|
||||
getNotice(
|
||||
t("syncrun_step3", {
|
||||
@@ -369,10 +379,11 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
async onload() {
|
||||
log.info(`loading plugin ${this.manifest.id}`);
|
||||
|
||||
const { iconSvgSyncWait, iconSvgSyncRunning } = getIconSvg();
|
||||
const { iconSvgSyncWait, iconSvgSyncRunning, iconSvgLogs } = getIconSvg();
|
||||
|
||||
addIcon(iconNameSyncWait, iconSvgSyncWait);
|
||||
addIcon(iconNameSyncRunning, iconSvgSyncRunning);
|
||||
addIcon(iconNameLogs, iconSvgLogs);
|
||||
|
||||
this.oauth2Info = {
|
||||
verifier: "",
|
||||
@@ -664,6 +675,50 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "export-sync-plans-json",
|
||||
name: t("command_exportsyncplans_json"),
|
||||
icon: iconNameLogs,
|
||||
callback: async () => {
|
||||
await exportVaultSyncPlansToFiles(
|
||||
this.db,
|
||||
this.app.vault,
|
||||
this.vaultRandomID,
|
||||
"json"
|
||||
);
|
||||
new Notice(t("settings_syncplans_notice"));
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "export-sync-plans-table",
|
||||
name: t("command_exportsyncplans_table"),
|
||||
icon: iconNameLogs,
|
||||
callback: async () => {
|
||||
await exportVaultSyncPlansToFiles(
|
||||
this.db,
|
||||
this.app.vault,
|
||||
this.vaultRandomID,
|
||||
"table"
|
||||
);
|
||||
new Notice(t("settings_syncplans_notice"));
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "export-logs-in-db",
|
||||
name: t("command_exportlogsindb"),
|
||||
icon: iconNameLogs,
|
||||
callback: async () => {
|
||||
await exportVaultLoggerOutputToFiles(
|
||||
this.db,
|
||||
this.app.vault,
|
||||
this.vaultRandomID
|
||||
);
|
||||
new Notice(t("settings_logtodbexport_notice"));
|
||||
},
|
||||
});
|
||||
|
||||
this.addSettingTab(new RemotelySaveSettingTab(this.app, this));
|
||||
|
||||
// this.registerDomEvent(document, "click", (evt: MouseEvent) => {
|
||||
|
||||
@@ -400,7 +400,7 @@ export class WrappedOnedriveClient {
|
||||
// log.info(`already checked, /${this.remoteBaseDir} exist before`)
|
||||
} else {
|
||||
const k = await this.getJson("/drive/special/approot/children");
|
||||
log.debug(k);
|
||||
// log.debug(k);
|
||||
this.vaultFolderExists =
|
||||
(k.value as DriveItem[]).filter((x) => x.name === this.remoteBaseDir)
|
||||
.length > 0;
|
||||
@@ -617,6 +617,7 @@ export const listFromRemote = async (
|
||||
`/drive/special/approot:/${client.remoteBaseDir}:/delta`
|
||||
);
|
||||
let driveItems = res.value as DriveItem[];
|
||||
// log.debug(driveItems);
|
||||
|
||||
while (NEXT_LINK_KEY in res) {
|
||||
res = await client.getJson(res[NEXT_LINK_KEY]);
|
||||
|
||||
+6
-1
@@ -109,6 +109,11 @@ class ObsHttpHandler extends FetchHttpHandler {
|
||||
|
||||
const raceOfPromises = [
|
||||
requestUrl(param).then((rsp) => {
|
||||
const headers = rsp.headers;
|
||||
const headersLower: Record<string, string> = {};
|
||||
for (const key of Object.keys(headers)) {
|
||||
headersLower[key.toLowerCase()] = headers[key];
|
||||
}
|
||||
const stream = new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(new Uint8Array(rsp.arrayBuffer));
|
||||
@@ -117,7 +122,7 @@ class ObsHttpHandler extends FetchHttpHandler {
|
||||
});
|
||||
return {
|
||||
response: new HttpResponse({
|
||||
headers: rsp.headers,
|
||||
headers: headersLower,
|
||||
statusCode: rsp.status,
|
||||
body: stream,
|
||||
}),
|
||||
|
||||
+16
-7
@@ -1678,11 +1678,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
button.setButtonText(t("settings_outputsettingsconsole_button"));
|
||||
button.onClick(async () => {
|
||||
const c = messyConfigToNormal(await this.plugin.loadData());
|
||||
if (c.currLogLevel === "debug") {
|
||||
// no need to ouput it again because debug mode already output it
|
||||
} else {
|
||||
log.info(c);
|
||||
}
|
||||
log.info(c);
|
||||
new Notice(t("settings_outputsettingsconsole_notice"));
|
||||
});
|
||||
});
|
||||
@@ -1691,12 +1687,25 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
.setName(t("settings_syncplans"))
|
||||
.setDesc(t("settings_syncplans_desc"))
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_syncplans_button"));
|
||||
button.setButtonText(t("settings_syncplans_button_json"));
|
||||
button.onClick(async () => {
|
||||
await exportVaultSyncPlansToFiles(
|
||||
this.plugin.db,
|
||||
this.app.vault,
|
||||
this.plugin.vaultRandomID
|
||||
this.plugin.vaultRandomID,
|
||||
"json"
|
||||
);
|
||||
new Notice(t("settings_syncplans_notice"));
|
||||
});
|
||||
})
|
||||
.addButton(async (button) => {
|
||||
button.setButtonText(t("settings_syncplans_button_table"));
|
||||
button.onClick(async () => {
|
||||
await exportVaultSyncPlansToFiles(
|
||||
this.plugin.db,
|
||||
this.app.vault,
|
||||
this.plugin.vaultRandomID,
|
||||
"table"
|
||||
);
|
||||
new Notice(t("settings_syncplans_notice"));
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"0.3.2": "0.12.15",
|
||||
"0.3.17": "0.13.21"
|
||||
"0.3.18": "0.13.21"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user