Compare commits

...
7 Commits
Author SHA1 Message Date
fyears 581c6237cc bump to 0.4.7
Release A New Version / build (16.x) (push) Failing after 32s
2024-03-20 03:44:34 +08:00
fyears 257c995090 fix a mysterious bug for webdav 2024-03-20 03:10:57 +08:00
fyears fd095a9093 more debug for webdav and bump to 0.4.6
Release A New Version / build (16.x) (push) Failing after 39s
2024-03-20 02:01:51 +08:00
fyears acc9233611 bump to beta 0.4.5
Release A New Version / build (16.x) (push) Failing after 41s
2024-03-20 01:42:33 +08:00
fyears 4456db11cf try to make webdav header happy 2024-03-20 01:42:02 +08:00
fyears 54449dd207 add doc for onedrive 2024-03-19 01:29:10 +08:00
fyears 822941f1c3 fix statusbar 2024-03-18 23:59:52 +08:00
6 changed files with 85 additions and 83 deletions
+1
View File
@@ -92,6 +92,7 @@ Additionally, the plugin author may occasionally visit Obsidian official forum a
- 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**.
- If you want to sync the files across multiple devices, **your vault name should be the same** while using default settings. - If you want to sync the files across multiple devices, **your vault name should be the same** while using default settings.
- You might also want to checkout [faq for OneDrive](./docs/remote_services/onedrive/README.md).
### webdav ### webdav
+23
View File
@@ -0,0 +1,23 @@
# OneDrive
- **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).
- 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.
- Password-based end-to-end encryption is also supported. But please be aware that **the vault name itself is not encrypted**.
- If you want to sync the files across multiple devices, **your vault name should be the same** while using default settings.
## FAQ
### How about OneDrive for Business?
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.
### I cannot find `/Apps/remotely-save` folder
Mystically some users report that their OneDrive generate `/Application/Graph` instead of `/Apps/remotely-save`. See [#517](https://github.com/remotely-save/remotely-save/issues/517).
The solution is simple:
1. Backup your vault manually.
2. Go to onedrive website (<https://onedrive.live.com/>), and rename `/Application/Graph` to `/Application/remotely-save` (right click on the folder and you will see rename option)
3. Come back to Obsidian and try to sync!
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "remotely-save", "id": "remotely-save",
"name": "Remotely Save", "name": "Remotely Save",
"version": "0.4.4", "version": "0.4.7",
"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",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "remotely-save", "name": "remotely-save",
"version": "0.4.4", "version": "0.4.7",
"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 --watch", "dev2": "node esbuild.config.mjs --watch",
+7 -10
View File
@@ -509,17 +509,14 @@ export const stringToFragment = (string: string) => {
* @param op * @param op
*/ */
export const changeMobileStatusBar = (op: "enable" | "disable") => { export const changeMobileStatusBar = (op: "enable" | "disable") => {
const bar = document.querySelector(
".is-mobile .app-container .status-bar"
) as HTMLElement;
if (op === "enable") { if (op === "enable") {
( bar.style.setProperty("display", "flex");
document.querySelector( bar.style.setProperty("margin-bottom", "40px");
".is-mobile .app-container .status-bar"
) as HTMLElement
).style.setProperty("display", "flex");
} else { } else {
( bar.style.removeProperty("display");
document.querySelector( bar.style.removeProperty("margin-bottom");
".is-mobile .app-container .status-bar"
) as HTMLElement
).style.setProperty("display", "none");
} }
}; };
+52 -71
View File
@@ -1,5 +1,5 @@
import { Buffer } from "buffer"; import { Buffer } from "buffer";
import { Vault, requestUrl } from "obsidian"; import { Platform, Vault, requestUrl } from "obsidian";
import { Queue } from "@fyears/tsqueue"; import { Queue } from "@fyears/tsqueue";
import chunk from "lodash/chunk"; import chunk from "lodash/chunk";
@@ -26,43 +26,70 @@ function onlyAscii(str: string) {
return !/[^\u0000-\u00ff]/g.test(str); return !/[^\u0000-\u00ff]/g.test(str);
} }
/**
* https://stackoverflow.com/questions/12539574/
* @param obj
* @returns
*/
function objKeyToLower(obj: Record<string, string>) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])
);
}
// @ts-ignore // @ts-ignore
import { getPatcher } from "webdav/dist/web/index.js"; import { getPatcher } from "webdav/dist/web/index.js";
if (VALID_REQURL) { if (VALID_REQURL) {
getPatcher().patch( getPatcher().patch(
"request", "request",
async (options: RequestOptionsWithState): Promise<Response> => { async (options: RequestOptionsWithState): Promise<Response> => {
const transformedHeaders = { ...options.headers }; const transformedHeaders = objKeyToLower({ ...options.headers });
delete transformedHeaders["host"]; delete transformedHeaders["host"];
delete transformedHeaders["Host"];
delete transformedHeaders["content-length"]; delete transformedHeaders["content-length"];
delete transformedHeaders["Content-Length"];
const r = await requestUrl({ const reqContentType =
transformedHeaders["accept"] ?? transformedHeaders["content-type"];
console.debug(`before request:`);
console.debug(`url: ${options.url}`);
console.debug(`method: ${options.method}`);
console.debug(`headers: ${JSON.stringify(transformedHeaders, null, 2)}`);
console.debug(`reqContentType: ${reqContentType}`);
let r = await requestUrl({
url: options.url, url: options.url,
method: options.method, method: options.method,
body: options.data as string | ArrayBuffer, body: options.data as string | ArrayBuffer,
headers: transformedHeaders, headers: transformedHeaders,
contentType: reqContentType,
throw: false, throw: false,
}); });
let contentType: string | undefined = if (
r.headers["Content-Type"] || r.headers["content-type"]; r.status === 401 &&
if (options.headers !== undefined) { Platform.isIosApp &&
contentType = !options.url.endsWith("/") &&
contentType || !options.url.endsWith(".md") &&
options.headers["Content-Type"] || options.method.toUpperCase() === "PROPFIND"
options.headers["content-type"] || ) {
options.headers["Accept"] || // don't ask me why,
options.headers["accept"]; // some webdav servers have some mysterious behaviours,
// if a folder doesn't exist without slash, the servers return 401 instead of 404
// here is a dirty hack that works
console.debug(`so we have 401, try appending request url with slash`);
r = await requestUrl({
url: `${options.url}/`,
method: options.method,
body: options.data as string | ArrayBuffer,
headers: transformedHeaders,
contentType: reqContentType,
throw: false,
});
} }
if (contentType !== undefined) { console.debug(`after request:`);
contentType = contentType.toLowerCase(); const rspHeaders = objKeyToLower({ ...r.headers });
} console.debug(`rspHeaders: ${JSON.stringify(rspHeaders, null, 2)}`);
const rspHeaders = { ...r.headers };
console.log("rspHeaders");
console.log(rspHeaders);
for (let key in rspHeaders) { for (let key in rspHeaders) {
if (rspHeaders.hasOwnProperty(key)) { if (rspHeaders.hasOwnProperty(key)) {
// avoid the error: // avoid the error:
@@ -79,74 +106,28 @@ if (VALID_REQURL) {
// } // }
// } // }
if (!onlyAscii(rspHeaders[key])) { if (!onlyAscii(rspHeaders[key])) {
console.debug(`rspHeaders[key] needs encode: ${key}`);
rspHeaders[key] = encodeURIComponent(rspHeaders[key]); rspHeaders[key] = encodeURIComponent(rspHeaders[key]);
} }
} }
} }
// console.info(`requesting url=${options.url}`);
// console.info(`contentType=${contentType}`);
// console.info(`rspHeaders=${JSON.stringify(rspHeaders)}`)
// let r2: Response = undefined;
// if (contentType.includes("xml")) {
// r2 = new Response(r.text, {
// status: r.status,
// statusText: getReasonPhrase(r.status),
// headers: rspHeaders,
// });
// } else if (
// contentType.includes("json") ||
// contentType.includes("javascript")
// ) {
// console.info('inside json branch');
// // const j = r.json;
// // console.info(j);
// r2 = new Response(
// r.text, // yea, here is the text because Response constructor expects a text
// {
// status: r.status,
// statusText: getReasonPhrase(r.status),
// headers: rspHeaders,
// });
// } else if (contentType.includes("text")) {
// // avoid text/json,
// // so we split this out from the above xml or json branch
// r2 = new Response(r.text, {
// status: r.status,
// statusText: getReasonPhrase(r.status),
// headers: rspHeaders,
// });
// } else if (
// contentType.includes("octet-stream") ||
// contentType.includes("binary") ||
// contentType.includes("buffer")
// ) {
// // application/octet-stream
// r2 = new Response(r.arrayBuffer, {
// status: r.status,
// statusText: getReasonPhrase(r.status),
// headers: rspHeaders,
// });
// } else {
// throw Error(
// `do not know how to deal with requested content type = ${contentType}`
// );
// }
let r2: Response | undefined = undefined; let r2: Response | undefined = undefined;
const statusText = getReasonPhrase(r.status);
console.debug(`statusText: ${statusText}`);
if ([101, 103, 204, 205, 304].includes(r.status)) { if ([101, 103, 204, 205, 304].includes(r.status)) {
// A null body status is a status that is 101, 103, 204, 205, or 304. // A null body status is a status that is 101, 103, 204, 205, or 304.
// https://fetch.spec.whatwg.org/#statuses // https://fetch.spec.whatwg.org/#statuses
// fix this: Failed to construct 'Response': Response with null body status cannot have body // fix this: Failed to construct 'Response': Response with null body status cannot have body
r2 = new Response(null, { r2 = new Response(null, {
status: r.status, status: r.status,
statusText: getReasonPhrase(r.status), statusText: statusText,
headers: rspHeaders, headers: rspHeaders,
}); });
} else { } else {
r2 = new Response(r.arrayBuffer, { r2 = new Response(r.arrayBuffer, {
status: r.status, status: r.status,
statusText: getReasonPhrase(r.status), statusText: statusText,
headers: rspHeaders, headers: rspHeaders,
}); });
} }