Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4edd8caaea | ||
|
|
005b698862 | ||
|
|
e76a5e4c12 | ||
|
|
ea3dcc6533 | ||
|
|
c8bd10a855 | ||
|
|
996074e431 | ||
|
|
275224cf73 | ||
|
|
575a27db26 | ||
|
|
76127dd131 | ||
|
|
e2e6f6a12c | ||
|
|
4f3e8803da |
@@ -61,8 +61,8 @@ Additionally, the plugin author may occasionally visit Obsidian official forum a
|
|||||||
|
|
||||||
- Prepare your S3 (-compatible) service information: [endpoint, region](https://docs.aws.amazon.com/general/latest/gr/s3.html), [access key id, secret access key](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-your-credentials.html), bucket name. The bucket should be empty and solely for syncing a vault.
|
- Prepare your S3 (-compatible) service information: [endpoint, region](https://docs.aws.amazon.com/general/latest/gr/s3.html), [access key id, secret access key](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-your-credentials.html), bucket name. The bucket should be empty and solely for syncing a vault.
|
||||||
- About CORS:
|
- About CORS:
|
||||||
- If you are using Obsidian >= 0.13.25, you can skip this CORS part.
|
- If you are using Obsidian desktop >= 0.13.25 or mobile >= 1.1.1, you can skip this CORS part.
|
||||||
- If you are using Obsidian < 0.13.25, you need to configure (enable) [CORS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html) for requests from `app://obsidian.md` and `capacitor://localhost` and `http://localhost`, and add at least `ETag` into exposed headers. Full example is [here](./docs/s3_cors_configure.md). It's unfortunately required, because the plugin sends requests from a browser-like envirement. And those addresses are tested and found on desktop and ios and android.
|
- If you are using Obsidian desktop < 0.13.25 or mobile < 1.1.1, you need to configure (enable) [CORS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html) for requests from `app://obsidian.md` and `capacitor://localhost` and `http://localhost`, and add at least `ETag` into exposed headers. Full example is [here](./docs/s3_cors_configure.md). It's unfortunately required, because the plugin sends requests from a browser-like envirement. And those addresses are tested and found on desktop and ios and android.
|
||||||
- Download and enable this plugin.
|
- Download and enable this plugin.
|
||||||
- Enter your information to the settings of this plugin.
|
- Enter your information to the settings of this plugin.
|
||||||
- If you want to enable end-to-end encryption, also set a password in settings. If you do not specify a password, the files and folders are synced in plain, original content to the cloud.
|
- If you want to enable end-to-end encryption, also set a password in settings. If you do not specify a password, the files and folders are synced in plain, original content to the cloud.
|
||||||
@@ -87,8 +87,8 @@ Additionally, the plugin author may occasionally visit Obsidian official forum a
|
|||||||
### webdav
|
### webdav
|
||||||
|
|
||||||
- About CORS:
|
- About CORS:
|
||||||
- If you are using Obsidian >= 0.13.25, you can skip this CORS part.
|
- If you are using Obsidian desktop >= 0.13.25 or mobile >= 1.1.1, you can skip this CORS part.
|
||||||
- If you are using Obsidian < 0.13.25:
|
- If you are using Obsidian desktop < 0.13.25 or mobile < 1.1.1:
|
||||||
- The webdav server has to be enabled CORS for requests from `app://obsidian.md` and `capacitor://localhost` and `http://localhost`, **AND** all webdav HTTP methods, **AND** all webdav headers. These are required, because Obsidian mobile works like a browser and mobile plugins are limited by CORS policies unless under a upgraded Obsidian version.
|
- The webdav server has to be enabled CORS for requests from `app://obsidian.md` and `capacitor://localhost` and `http://localhost`, **AND** all webdav HTTP methods, **AND** all webdav headers. These are required, because Obsidian mobile works like a browser and mobile plugins are limited by CORS policies unless under a upgraded Obsidian version.
|
||||||
- Popular software NextCloud, OwnCloud, `rclone serve webdav` do **NOT** enable CORS by default. If you are using any of them, you should evaluate the risk, and find a way to enable CORS, before using this plugin, or use a upgraded Obsidian version.
|
- Popular software NextCloud, OwnCloud, `rclone serve webdav` do **NOT** enable CORS by default. If you are using any of them, you should evaluate the risk, and find a way to enable CORS, before using this plugin, or use a upgraded Obsidian version.
|
||||||
- The plugin is tested successfully under python package [`wsgidav` (version 4.0)](https://github.com/mar10/wsgidav). See [this issue](https://github.com/mar10/wsgidav/issues/239) for some details.
|
- The plugin is tested successfully under python package [`wsgidav` (version 4.0)](https://github.com/mar10/wsgidav). See [this issue](https://github.com/mar10/wsgidav/issues/239) for some details.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ The plugin is developed for the browser environment. The "fake" browser behind t
|
|||||||
|
|
||||||
[MDN has a doc about CORS.](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
|
[MDN has a doc about CORS.](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
|
||||||
|
|
||||||
From Obsidian version >= insider 0.13.25, Obsidian [provides a new API `requiestUrl`](https://forum.obsidian.md/t/obsidian-release-v0-13-25-insider-build/32701), that allows the plugin to fully bypass the CORS issue. As of Mar 2022, the latest public-released Obsidian desktop has supported this API, but the Obsidian mobile still stays in insider.
|
From Obsidian desktop >= 0.13.25 or mobile >= 1.1.1, Obsidian [provides a new API `requiestUrl`](https://forum.obsidian.md/t/obsidian-release-v0-13-25-insider-build/32701), that allows the plugin to fully bypass the CORS issue. As of Mar 2022, the latest public-released Obsidian desktop has supported this API, but the Obsidian mobile still stays in insider.
|
||||||
|
|
||||||
For using this plugin in Obsidian version < 0.13.25, we need to configure the server side to return the header `Access-Control-Allow-Origin` allowing the origins `app://obsidian.md` and `capacitor://localhost` and `http://localhost`. Here is an example [configuration for Amazon S3](./s3_cors_configure.md).
|
For using this plugin in Obsidian desktop < 0.13.25 or mobile < 1.1.1, we need to configure the server side to return the header `Access-Control-Allow-Origin` allowing the origins `app://obsidian.md` and `capacitor://localhost` and `http://localhost`. Here is an example [configuration for Amazon S3](./s3_cors_configure.md).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# How To Configure S3 CORS Rules
|
# How To Configure S3 CORS Rules
|
||||||
|
|
||||||
If you are using the latest version of this plugin AND Obsidian >= 0.13.25, you do not need to configure it any more. If you are using Obsidian < 0.13.25, you are required to configure the rules as following.
|
If you are using the latest version of this plugin AND Obsidian desktop >= 0.13.25, mobile >= 1.1.1, you do not need to configure it any more. If you are using Obsidian desktop < 0.13.25, moble < 1.1.1, you are required to configure the rules as following.
|
||||||
|
|
||||||
Thanks to [@NAL100 in the Discussion](https://github.com/fyears/remotely-save/discussions/28).
|
Thanks to [@NAL100 in the Discussion](https://github.com/fyears/remotely-save/discussions/28).
|
||||||
|
|
||||||
|
|||||||
@@ -2,41 +2,41 @@
|
|||||||
|
|
||||||
Here is an overview of the connectability ("connectable" or "not connectable" or "in the plan" or "never") to some services by this plugin.
|
Here is an overview of the connectability ("connectable" or "not connectable" or "in the plan" or "never") to some services by this plugin.
|
||||||
|
|
||||||
The plugin works under the browser environment in Obsidian, so CORS is an issue. Obsidian starts to provide a rich API `requestUrl` for version >= 0.13.25 to bypass the CORS issue. But if the users are still using an older version of Obsidian, they need to configure CORS on server.
|
The plugin works under the browser environment in Obsidian, so CORS is an issue. Obsidian starts to provide a rich API `requestUrl` for desktop version >= 0.13.25, mobile >= 1.1.1 to bypass the CORS issue. But if the users are still using an older version of Obsidian, they need to configure CORS on server.
|
||||||
|
|
||||||
The list is for information purposes only.
|
The list is for information purposes only.
|
||||||
|
|
||||||
| Service | Connectable | by S3 | by WebDAV | by other protocol | can bypass CORS issue in Obsidian>=0.13.25 | need CORS config in Obsidian<0.13.25 |
|
| Service | Connectable | by S3 | by WebDAV | by other protocol | can bypass CORS issue in latest Obsidian | need CORS config in old Obsidian |
|
||||||
| ------------------------------------------------------------------------------------- | ----------- | ----- | --------- | ---------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------ |
|
| ------------------------------------------------------------------------------------- | ----------- | ----- | --------- | ---------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------ |
|
||||||
| Amazon S3 | Yes | Yes | | | Yes | [CORS config needed.](./s3_cors_configure.md) |
|
| Amazon S3 | Yes | Yes | | | Yes | [CORS config needed.](./s3_cors_configure.md) |
|
||||||
| Tencent Cloud - Cloud Object Storage (COS) 腾讯云对象存储 | Yes | Yes | | | Yes | CORS config needed. |
|
| Tencent Cloud - Cloud Object Storage (COS) 腾讯云对象存储 | Yes | Yes | | | Yes | CORS config needed. |
|
||||||
| Alibaba Cloud - Object Storage Service 阿里云对象存储 | Yes | Yes | | | Yes | CORS config needed. |
|
| Alibaba Cloud - Object Storage Service 阿里云对象存储 | Yes | Yes | | | Yes | CORS config needed. |
|
||||||
| Backblaze B2 Cloud Storage | Yes | Yes | | | Yes | Its CORS rules doesn't allow no-http(s) origins. |
|
| Backblaze B2 Cloud Storage | Yes | Yes | | | Yes | Its CORS rules doesn't allow no-http(s) origins. |
|
||||||
| [Wasabi](https://wasabi.com) | ? | ? | | | | |
|
| [Wasabi](https://wasabi.com) | ? | ? | | | | |
|
||||||
| [filebase](https://filebase.com/) | Yes | Yes | | | Yes | CORS config needed. |
|
| [filebase](https://filebase.com/) | Yes | Yes | | | Yes | CORS config needed. |
|
||||||
| QingStor 青云 | ? | ? | | | | |
|
| QingStor 青云 | ? | ? | | | | |
|
||||||
| [MinIO](https://min.io/) | ? | ? | | | | |
|
| [MinIO](https://min.io/) | ? | ? | | | | |
|
||||||
| [WsgiDAV](https://github.com/mar10/wsgidav) | Yes | | Yes | | Yes | CORS rules can be set. |
|
| [WsgiDAV](https://github.com/mar10/wsgidav) | Yes | | Yes | | Yes | CORS rules can be set. |
|
||||||
| [Nginx `ngx_http_dav_module`](http://nginx.org/en/docs/http/ngx_http_dav_module.html) | Yes? | | Yes? | | Yes? | ? |
|
| [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. |
|
| NextCloud | Yes? | | Yes? | | Yes? | No CORS config by default. |
|
||||||
| OwnCloud | 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? | |
|
||||||
| `rclone serve webdav` | Yes | | Yes | | Yes | No CORS support. |
|
| `rclone serve webdav` | Yes | | Yes | | Yes | No CORS support. |
|
||||||
| [Nutstore 坚果云](https://www.jianguoyun.com/) | 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. |
|
| [TeraCLOUD](https://teracloud.jp/en/) | Yes | | Yes | | Yes | No CORS support. |
|
||||||
| Dropbox | Yes | | | Yes | | |
|
| Dropbox | Yes | | | Yes | | |
|
||||||
| OneDrive for personal | Yes | | | Yes | | |
|
| OneDrive for personal | Yes | | | Yes | | |
|
||||||
| OneDrive for Business | In the plan | | | ? | | |
|
| OneDrive for Business | In the plan | | | ? | | |
|
||||||
| Google Drive | In the plan | | | ? | | |
|
| Google Drive | In the plan | | | ? | | |
|
||||||
| [Box](https://www.box.com/) | ? | | | May be possible but needs further development. | | |
|
| [Box](https://www.box.com/) | ? | | | May be possible but needs further development. | | |
|
||||||
| Google Cloud Storage | ? | | | May be possible but needs further development. | | |
|
| Google Cloud Storage | ? | | | May be possible but needs further development. | | |
|
||||||
| Microsoft Azure Blob Storage | ? | | | May be possible but needs further development. | | |
|
| Microsoft Azure Blob Storage | ? | | | May be possible but needs further development. | | |
|
||||||
| [OpenStack Storage (Swift)](https://github.com/openstack/swift) | ? | | | May be possible but needs further development. | | |
|
| [OpenStack Storage (Swift)](https://github.com/openstack/swift) | ? | | | May be possible but needs further development. | | |
|
||||||
| https://put.io/ | ? | | | ? | | |
|
| https://put.io/ | ? | | | ? | | |
|
||||||
| Yandex Disk | ? | | | ? | | |
|
| Yandex Disk | ? | | | ? | | |
|
||||||
| FTP / FTPS | Never | | | Technically never possible to be implemented. | | |
|
| FTP / FTPS | Never | | | Technically never possible to be implemented. | | |
|
||||||
| SFTP | Never | | | Technically never possible to be implemented. | | |
|
| SFTP | Never | | | Technically never possible to be implemented. | | |
|
||||||
| Jottacloud | No | | | No. It seems that no open api is available. | | |
|
| Jottacloud | No | | | No. It seems that no open api is available. | | |
|
||||||
| Mega | Never | | | No. No js api is available. | | |
|
| Mega | Never | | | No. No js api is available. | | |
|
||||||
| Git | Never | | | No. Technically very hard, if not impossible, to be implemented. | | |
|
| Git | Never | | | No. Technically very hard, if not impossible, to be implemented. | | |
|
||||||
| | | | | | | |
|
| | | | | | | |
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "remotely-save",
|
"id": "remotely-save",
|
||||||
"name": "Remotely Save",
|
"name": "Remotely Save",
|
||||||
"version": "0.3.9",
|
"version": "0.3.12",
|
||||||
"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
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "remotely-save",
|
"name": "remotely-save",
|
||||||
"version": "0.3.9",
|
"version": "0.3.12",
|
||||||
"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",
|
||||||
|
|||||||
+4
-4
@@ -101,11 +101,11 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
return this.i18n.t(x, vars);
|
return this.i18n.t(x, vars);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNotice = (x: string) => {
|
const getNotice = (x: string, timeout?: number) => {
|
||||||
// only show notices in manual mode
|
// only show notices in manual mode
|
||||||
// no notice in auto mode
|
// no notice in auto mode
|
||||||
if (triggerSource === "manual" || triggerSource === "dry") {
|
if (triggerSource === "manual" || triggerSource === "dry") {
|
||||||
new Notice(x);
|
new Notice(x, timeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (this.syncStatus !== "idle") {
|
if (this.syncStatus !== "idle") {
|
||||||
@@ -331,8 +331,8 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
log.info(msg);
|
log.info(msg);
|
||||||
log.info(error);
|
log.info(error);
|
||||||
getNotice(msg);
|
getNotice(msg, 10 * 1000);
|
||||||
getNotice(error.message);
|
getNotice(error.message, 10 * 1000);
|
||||||
this.syncStatus = "idle";
|
this.syncStatus = "idle";
|
||||||
if (this.syncRibbon !== undefined) {
|
if (this.syncRibbon !== undefined) {
|
||||||
setIcon(this.syncRibbon, iconNameSyncWait);
|
setIcon(this.syncRibbon, iconNameSyncWait);
|
||||||
|
|||||||
+12
-5
@@ -271,15 +271,22 @@ export class RemoteClient {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
checkConnectivity = async () => {
|
checkConnectivity = async (callbackFunc?: any) => {
|
||||||
if (this.serviceType === "s3") {
|
if (this.serviceType === "s3") {
|
||||||
return await s3.checkConnectivity(this.s3Client, this.s3Config);
|
return await s3.checkConnectivity(
|
||||||
|
this.s3Client,
|
||||||
|
this.s3Config,
|
||||||
|
callbackFunc
|
||||||
|
);
|
||||||
} else if (this.serviceType === "webdav") {
|
} else if (this.serviceType === "webdav") {
|
||||||
return await webdav.checkConnectivity(this.webdavClient);
|
return await webdav.checkConnectivity(this.webdavClient, callbackFunc);
|
||||||
} else if (this.serviceType === "dropbox") {
|
} else if (this.serviceType === "dropbox") {
|
||||||
return await dropbox.checkConnectivity(this.dropboxClient);
|
return await dropbox.checkConnectivity(this.dropboxClient, callbackFunc);
|
||||||
} else if (this.serviceType === "onedrive") {
|
} else if (this.serviceType === "onedrive") {
|
||||||
return await onedrive.checkConnectivity(this.onedriveClient);
|
return await onedrive.checkConnectivity(
|
||||||
|
this.onedriveClient,
|
||||||
|
callbackFunc
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
throw Error(`not supported service type ${this.serviceType}`);
|
throw Error(`not supported service type ${this.serviceType}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -636,7 +636,10 @@ export const deleteFromRemote = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkConnectivity = async (client: WrappedDropboxClient) => {
|
export const checkConnectivity = async (
|
||||||
|
client: WrappedDropboxClient,
|
||||||
|
callbackFunc?: any
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const results = await getRemoteMeta(client, "/");
|
const results = await getRemoteMeta(client, "/");
|
||||||
if (results === undefined) {
|
if (results === undefined) {
|
||||||
@@ -644,8 +647,10 @@ export const checkConnectivity = async (client: WrappedDropboxClient) => {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("dropbox connectivity error:");
|
log.debug(err);
|
||||||
console.error(err);
|
if (callbackFunc !== undefined) {
|
||||||
|
callbackFunc(err);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -866,11 +866,18 @@ export const deleteFromRemote = async (
|
|||||||
await client.deleteJson(remoteFileName);
|
await client.deleteJson(remoteFileName);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkConnectivity = async (client: WrappedOnedriveClient) => {
|
export const checkConnectivity = async (
|
||||||
|
client: WrappedOnedriveClient,
|
||||||
|
callbackFunc?: any
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const k = await getUserDisplayName(client);
|
const k = await getUserDisplayName(client);
|
||||||
return k !== "<unknown display name>";
|
return k !== "<unknown display name>";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
log.debug(err);
|
||||||
|
if (callbackFunc !== undefined) {
|
||||||
|
callbackFunc(err);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-1
@@ -498,7 +498,8 @@ export const deleteFromRemote = async (
|
|||||||
*/
|
*/
|
||||||
export const checkConnectivity = async (
|
export const checkConnectivity = async (
|
||||||
s3Client: S3Client,
|
s3Client: S3Client,
|
||||||
s3Config: S3Config
|
s3Config: S3Config,
|
||||||
|
callbackFunc?: any
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const results = await s3Client.send(
|
const results = await s3Client.send(
|
||||||
@@ -509,10 +510,19 @@ export const checkConnectivity = async (
|
|||||||
results.$metadata === undefined ||
|
results.$metadata === undefined ||
|
||||||
results.$metadata.httpStatusCode === undefined
|
results.$metadata.httpStatusCode === undefined
|
||||||
) {
|
) {
|
||||||
|
const err = "results or $metadata or httStatusCode is undefined";
|
||||||
|
log.debug(err);
|
||||||
|
if (callbackFunc !== undefined) {
|
||||||
|
callbackFunc(err);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return results.$metadata.httpStatusCode === 200;
|
return results.$metadata.httpStatusCode === 200;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
log.debug(err);
|
||||||
|
if (callbackFunc !== undefined) {
|
||||||
|
callbackFunc(err);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+28
-3
@@ -204,7 +204,7 @@ export class WrappedWebdavClient {
|
|||||||
if (this.vaultFolderExists) {
|
if (this.vaultFolderExists) {
|
||||||
// pass
|
// pass
|
||||||
} else {
|
} else {
|
||||||
const res = await this.client.exists(`/${this.vaultName}`);
|
const res = await this.client.exists(`/${this.vaultName}/`);
|
||||||
if (res) {
|
if (res) {
|
||||||
// log.info("remote vault folder exits!");
|
// log.info("remote vault folder exits!");
|
||||||
this.vaultFolderExists = true;
|
this.vaultFolderExists = true;
|
||||||
@@ -223,7 +223,7 @@ export class WrappedWebdavClient {
|
|||||||
const res = await this.client.customRequest(`/${this.vaultName}`, {
|
const res = await this.client.customRequest(`/${this.vaultName}`, {
|
||||||
method: "PROPFIND",
|
method: "PROPFIND",
|
||||||
headers: {
|
headers: {
|
||||||
Depth: "Infinity",
|
Depth: "infinity",
|
||||||
},
|
},
|
||||||
responseType: "text",
|
responseType: "text",
|
||||||
});
|
});
|
||||||
@@ -523,15 +523,40 @@ export const deleteFromRemote = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkConnectivity = async (client: WrappedWebdavClient) => {
|
export const checkConnectivity = async (
|
||||||
|
client: WrappedWebdavClient,
|
||||||
|
callbackFunc?: any
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
client.webdavConfig.address.startsWith("http://") ||
|
||||||
|
client.webdavConfig.address.startsWith("https://")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const err = "Error: the url should start with http(s):// but it does not!";
|
||||||
|
log.debug(err);
|
||||||
|
if (callbackFunc !== undefined) {
|
||||||
|
callbackFunc(err);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await client.init();
|
await client.init();
|
||||||
const results = await getRemoteMeta(client, "/");
|
const results = await getRemoteMeta(client, "/");
|
||||||
if (results === undefined) {
|
if (results === undefined) {
|
||||||
|
const err = "results is undefined";
|
||||||
|
log.debug(err);
|
||||||
|
if (callbackFunc !== undefined) {
|
||||||
|
callbackFunc(err);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
log.debug(err);
|
||||||
|
if (callbackFunc !== undefined) {
|
||||||
|
callbackFunc(err);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+20
-4
@@ -826,11 +826,15 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
button.onClick(async () => {
|
button.onClick(async () => {
|
||||||
new Notice(t("settings_checkonnectivity_checking"));
|
new Notice(t("settings_checkonnectivity_checking"));
|
||||||
const client = new RemoteClient("s3", this.plugin.settings.s3);
|
const client = new RemoteClient("s3", this.plugin.settings.s3);
|
||||||
const res = await client.checkConnectivity();
|
const errors = { msg: "" };
|
||||||
|
const res = await client.checkConnectivity((err: any) => {
|
||||||
|
errors.msg = err;
|
||||||
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
new Notice(t("settings_s3_connect_succ"));
|
new Notice(t("settings_s3_connect_succ"));
|
||||||
} else {
|
} else {
|
||||||
new Notice(t("settings_s3_connect_fail"));
|
new Notice(t("settings_s3_connect_fail"));
|
||||||
|
new Notice(errors.msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -959,11 +963,15 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
() => self.plugin.saveSettings()
|
() => self.plugin.saveSettings()
|
||||||
);
|
);
|
||||||
|
|
||||||
const res = await client.checkConnectivity();
|
const errors = { msg: "" };
|
||||||
|
const res = await client.checkConnectivity((err: any) => {
|
||||||
|
errors.msg = `${err}`;
|
||||||
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
new Notice(t("settings_dropbox_connect_succ"));
|
new Notice(t("settings_dropbox_connect_succ"));
|
||||||
} else {
|
} else {
|
||||||
new Notice(t("settings_dropbox_connect_fail"));
|
new Notice(t("settings_dropbox_connect_fail"));
|
||||||
|
new Notice(errors.msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1074,11 +1082,15 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
() => self.plugin.saveSettings()
|
() => self.plugin.saveSettings()
|
||||||
);
|
);
|
||||||
|
|
||||||
const res = await client.checkConnectivity();
|
const errors = { msg: "" };
|
||||||
|
const res = await client.checkConnectivity((err: any) => {
|
||||||
|
errors.msg = `${err}`;
|
||||||
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
new Notice(t("settings_onedrive_connect_succ"));
|
new Notice(t("settings_onedrive_connect_succ"));
|
||||||
} else {
|
} else {
|
||||||
new Notice(t("settings_onedrive_connect_fail"));
|
new Notice(t("settings_onedrive_connect_fail"));
|
||||||
|
new Notice(errors.msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1230,7 +1242,10 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
this.app.vault.getName(),
|
this.app.vault.getName(),
|
||||||
() => self.plugin.saveSettings()
|
() => self.plugin.saveSettings()
|
||||||
);
|
);
|
||||||
const res = await client.checkConnectivity();
|
const errors = { msg: "" };
|
||||||
|
const res = await client.checkConnectivity((err: any) => {
|
||||||
|
errors.msg = `${err}`;
|
||||||
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
new Notice(t("settings_webdav_connect_succ"));
|
new Notice(t("settings_webdav_connect_succ"));
|
||||||
} else {
|
} else {
|
||||||
@@ -1244,6 +1259,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
corsErrMsg: corsErrMsg,
|
corsErrMsg: corsErrMsg,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
new Notice(errors.msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-5
@@ -323,7 +323,7 @@ const ensembleMixedStates = async (
|
|||||||
r = {
|
r = {
|
||||||
key: entry.path,
|
key: entry.path,
|
||||||
existLocal: true,
|
existLocal: true,
|
||||||
mtimeLocal: Math.max(entry.stat.mtime, entry.stat.ctime),
|
mtimeLocal: Math.max(entry.stat.mtime || 0, entry.stat.ctime || 0),
|
||||||
sizeLocal: entry.stat.size,
|
sizeLocal: entry.stat.size,
|
||||||
};
|
};
|
||||||
} else if (entry instanceof TFolder) {
|
} else if (entry instanceof TFolder) {
|
||||||
@@ -450,22 +450,30 @@ const assignOperationToFileInplace = (
|
|||||||
// 0. find anything inconsistent
|
// 0. find anything inconsistent
|
||||||
if (r.existLocal && (r.mtimeLocal === undefined || r.mtimeLocal <= 0)) {
|
if (r.existLocal && (r.mtimeLocal === undefined || r.mtimeLocal <= 0)) {
|
||||||
throw Error(
|
throw Error(
|
||||||
`Error: File ${r.key} has a last modified time <=0 or undefined in the local file system. It's abnormal and the plugin stops.`
|
`Error: Abnormal last modified time locally: ${JSON.stringify(
|
||||||
|
r,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (r.existRemote && (r.mtimeRemote === undefined || r.mtimeRemote <= 0)) {
|
if (r.existRemote && (r.mtimeRemote === undefined || r.mtimeRemote <= 0)) {
|
||||||
throw Error(
|
throw Error(
|
||||||
`Error: File ${r.key} has a last modified time <=0 or undefined on the remote service. It's abnormal and the plugin stops.`
|
`Error: Abnormal last modified time remotely: ${JSON.stringify(
|
||||||
|
r,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (r.deltimeLocal !== undefined && r.deltimeLocal <= 0) {
|
if (r.deltimeLocal !== undefined && r.deltimeLocal <= 0) {
|
||||||
throw Error(
|
throw Error(
|
||||||
`Error: File ${r.key} has a local deletion time <=0. It's abnormal and the plugin stops.`
|
`Error: Abnormal deletion time locally: ${JSON.stringify(r, null, 2)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (r.deltimeRemote !== undefined && r.deltimeRemote <= 0) {
|
if (r.deltimeRemote !== undefined && r.deltimeRemote <= 0) {
|
||||||
throw Error(
|
throw Error(
|
||||||
`Error: File ${r.key} has a remote deletion time <=0. It's abnormal and the plugin stops.`
|
`Error: Abnormal deletion time remotely: ${JSON.stringify(r, null, 2)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"0.3.2": "0.12.15",
|
"0.3.2": "0.12.15",
|
||||||
"0.3.9": "0.13.21"
|
"0.3.12": "0.13.21"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user