Compare commits

...
16 Commits
Author SHA1 Message Date
fyears e39edcea70 bump to 0.5.25
Release A New Version / build (20.x) (push) Failing after 46s
2024-10-20 17:12:42 +08:00
fyears 9ac7ecf828 fix undefined 2024-10-20 16:58:22 +08:00
fyears 50c08400ff add more prefix 2024-10-20 16:42:47 +08:00
fyears 7bc2fad88b bump to 0.5.24
Release A New Version / build (20.x) (push) Failing after 41s
2024-09-30 21:15:53 +08:00
fyears 6dc1c28361 add caddy 2024-09-30 21:15:23 +08:00
fyears 2220f511e3 fix webdav path with /.. leading 2024-09-30 20:51:55 +08:00
fyears 06de1a4ab0 bump to 0.5.23
Release A New Version / build (20.x) (push) Failing after 47s
2024-09-30 18:11:43 +08:00
fyears df184ff9d8 correct way to deal with .obsidian and bookmarks 2024-09-30 18:11:08 +08:00
fyears f50ba953f1 fix ci
Release A New Version / build (20.x) (push) Failing after 50s
2024-09-29 10:19:48 +08:00
fyears 55cb26bd22 bump to 0.5.22 2024-09-29 10:11:01 +08:00
fyears 36d3018efb fix ignore logic 2024-09-29 10:09:49 +08:00
fyears 421b43d937 update complex logic for config 2024-09-29 09:32:04 +08:00
xvqinghong 17708359a1 fix bug of unwished synchronizing Config Directory and Bookmarks File (#816)
In the original code, if we set syncConfigDir to false, which means we don't want to sync this directory, but it will also cause `if( syncConfigDir && isInsideObsFolder(key, configDir) )` statement NEVER be entered.  Thus, in the original code, our setting is actually be ignored.
2024-09-29 09:20:27 +08:00
fyears 9acae1b077 fix google drive expire 2024-09-29 08:59:14 +08:00
fyears 4ade975585 warn in console about expire 2024-09-29 08:58:57 +08:00
fyears e988fc7ae9 update ci 2024-09-09 00:39:19 +08:00
13 changed files with 241 additions and 94 deletions
+3 -3
View File
@@ -39,13 +39,13 @@ jobs:
steps: steps:
- name: Checkout codes - name: Checkout codes
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Checkout LFS file list - name: Checkout LFS file list
run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id
- name: LFS Cache - name: LFS Cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .git/lfs/objects path: .git/lfs/objects
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }} key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
@@ -60,7 +60,7 @@ jobs:
- run: npm install - run: npm install
- run: npm test - run: npm test
- run: npm run build - run: npm run build
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v4
with: with:
name: my-dist name: my-dist
path: | path: |
+11 -41
View File
@@ -42,13 +42,13 @@ jobs:
steps: steps:
- name: Checkout codes - name: Checkout codes
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Checkout LFS file list - name: Checkout LFS file list
run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id
- name: LFS Cache - name: LFS Cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .git/lfs/objects path: .git/lfs/objects
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }} key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
@@ -63,44 +63,14 @@ jobs:
- run: npm install - run: npm install
- run: npm test - run: npm test
- run: npm run build - run: npm run build
- name: Create Release - name: Create Release And Upload
id: create_release uses: softprops/action-gh-release@v2
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ github.ref }}
with: with:
tag_name: ${{ github.ref }} files: |
release_name: ${{ github.ref }} main.js
manifest.json
styles.css
token: ${{ secrets.GITHUB_TOKEN }}
draft: false draft: false
prerelease: true prerelease: false
- name: Upload main.js make_latest: true
id: upload-main
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./main.js
asset_name: main.js
asset_content_type: text/javascript
- name: Upload manifest.json
id: upload-manifest
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./manifest.json
asset_name: manifest.json
asset_content_type: application/json
- name: Upload styles.css
id: upload-styles
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./styles.css
asset_name: styles.css
asset_content_type: text/css
+1
View File
@@ -122,6 +122,7 @@ Additionally, the plugin author may occasionally visit Obsidian official forum a
- [Open Media Vault](./docs/remote_services/webdav_openmediavault/README.md) - [Open Media Vault](./docs/remote_services/webdav_openmediavault/README.md)
- [Nginx (`ngx_http_dav_module`, `nginx-dav-ext-module`, with Docker)](./docs/remote_services/webdav_nginx/README.md) - [Nginx (`ngx_http_dav_module`, `nginx-dav-ext-module`, with Docker)](./docs/remote_services/webdav_nginx/README.md)
- [Apache (with Docker)](./docs/remote_services/webdav_apache/README.md) - [Apache (with Docker)](./docs/remote_services/webdav_apache/README.md)
- [Caddy with `http.handlers.webdav` module](./docs/remote_services/webdav_caddy/README.md)
- Very old version of Obsidian needs [configuring CORS](./docs/remote_services/webdav_general/webav_cors.md). - Very old version of Obsidian needs [configuring CORS](./docs/remote_services/webdav_general/webav_cors.md).
- Your data would be synced to a `${vaultName}` sub folder on your webdav server. - Your data would be synced to a `${vaultName}` sub folder on your webdav server.
- 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**.
+1
View File
@@ -122,6 +122,7 @@
- [Open Media Vault](./docs/remote_services/webdav_openmediavault/README.md) - [Open Media Vault](./docs/remote_services/webdav_openmediavault/README.md)
- [Nginx (`ngx_http_dav_module`, `nginx-dav-ext-module`, with Docker)](./docs/remote_services/webdav_nginx/README.md) - [Nginx (`ngx_http_dav_module`, `nginx-dav-ext-module`, with Docker)](./docs/remote_services/webdav_nginx/README.md)
- [Apache (with Docker)](./docs/remote_services/webdav_apache/README.md) - [Apache (with Docker)](./docs/remote_services/webdav_apache/README.md)
- [Caddy with `http.handlers.webdav` module](./docs/remote_services/webdav_caddy/README.md)
- 非常旧版本的Obsidian需要[配置 CORS](./docs/remote_services/webdav_general/webav_cors.md)。 - 非常旧版本的Obsidian需要[配置 CORS](./docs/remote_services/webdav_general/webav_cors.md)。
- 你的数据会同步到你的webdav服务器上的 `${vaultName}` 子文件夹。 - 你的数据会同步到你的webdav服务器上的 `${vaultName}` 子文件夹。
- 基于密码的端到端加密也是可以的。但请注意,**vault 名称本身未加密**。 - 基于密码的端到端加密也是可以的。但请注意,**vault 名称本身未加密**。
@@ -0,0 +1,33 @@
# Caddy with `http.handlers.webdav` module
> modified from the instruction from @cyruz-git in https://github.com/remotely-save/remotely-save/issues/825
## Link
<https://caddyserver.com/download?package=github.com%2Fmholt%2Fcaddy-webdav>
## Steps
1. Download caddy with webdav module from <https://caddyserver.com/download?package=github.com%2Fmholt%2Fcaddy-webdav>. Or you can install Caddy then install the plugins.
2. Create a folder for storing webdav server files. Like `/usr/local/mywebdav`.
3. Create a `Caddyfile` (yeah the file name itself is `Caddyfile`.) like this:
```caddy
:8080 {
route /dav/* {
root /usr/local/mywebdav
basicauth {
# Username "Bob", password "hiccup"
Bob $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
uri strip_prefix /dav
webdav
}
}
```
The password hash is generated like [this](https://caddyserver.com/docs/caddyfile/directives/basic_auth).
4. In Remotely Save, setup:
* address `http://localhost:8080/dav/`
* username `Bob`
* password `hiccup`
* auth type: `basic`
5. Check the connection and sync!
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "remotely-save", "id": "remotely-save",
"name": "Remotely Save", "name": "Remotely Save",
"version": "0.5.21", "version": "0.5.25",
"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,7 +1,7 @@
{ {
"id": "remotely-save", "id": "remotely-save",
"name": "Remotely Save", "name": "Remotely Save",
"version": "0.5.21", "version": "0.5.25",
"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.5.21", "version": "0.5.25",
"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",
+4
View File
@@ -95,6 +95,10 @@ class GoogleDriveAuthModal extends Modal {
k.expires_in * 1000; k.expires_in * 1000;
this.plugin.settings.googledrive.accessTokenExpiresAtTimeMs = this.plugin.settings.googledrive.accessTokenExpiresAtTimeMs =
ts + k.expires_in * 1000 - 60 * 2 * 1000; ts + k.expires_in * 1000 - 60 * 2 * 1000;
// manually set it expired after 60 days;
this.plugin.settings.googledrive.credentialsShouldBeDeletedAtTimeMs =
Date.now() + 1000 * 60 * 60 * 24 * 59;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
// try to remove data in clipboard // try to remove data in clipboard
+97 -35
View File
@@ -202,15 +202,37 @@ export const checkIsSkipItemOrNotByName = (
} }
} }
} }
if (syncConfigDir && isInsideObsFolder(key, configDir)) {
if (finalIsIgnored === undefined) {
finalIsIgnored = false;
}
}
if (syncBookmarks && isBookmarksFile(key, configDir)) { // sync config, not sync bookmarks: sync config and **force syncing bookmarks as well**
if (finalIsIgnored === undefined) { // not sync config, sync bookmarks: sync bookmars, not other config
finalIsIgnored = false; // not sync config, not sync bookmarks: not sync config
if (finalIsIgnored === undefined) {
if (syncConfigDir) {
if (isInsideObsFolder(key, configDir)) {
// force sync everything
finalIsIgnored = false;
} else {
// not config files, do not judge now, do nothing
}
} else if (syncBookmarks) {
// not sync config, sync bookmarks
if (isBookmarksFile(key, configDir)) {
// sync everything of bookmarks
finalIsIgnored = false;
} else if (isInsideObsFolder(key, configDir)) {
// not sync any other thing in config
finalIsIgnored = true;
} else {
// not config files, do not judge now, do nothing
}
} else {
// not sync config, and not sync bookmarks
if (isInsideObsFolder(key, configDir)) {
// not sync any thing in config
finalIsIgnored = true;
} else {
// not config files, do not judge now, do nothing
}
} }
} }
@@ -256,17 +278,9 @@ export const checkIsSkipItemOrNotByName = (
* | yes, also apply to children | explictly ignored | yes | yes | yes | * | yes, also apply to children | explictly ignored | yes | yes | yes |
*/ */
export const getSkipItemsByList = ( export const getSkipItemsByList = (
skipOrNotResults: Record<string, IsSkipResult>, skipOrNotResults: Record<string, IsSkipResult>
ignorePaths: string[],
onlyAllowPaths: string[]
): string[] => { ): string[] => {
const allPotentialKeys = Object.keys(skipOrNotResults); const allPotentialKeys = Object.keys(skipOrNotResults);
if (
allPotentialKeys.length === 0 ||
(ignorePaths.length === 0 && onlyAllowPaths.length === 0)
) {
return [];
}
// from short(shadow) to long(deep) , ascending // from short(shadow) to long(deep) , ascending
const sortedKeys = allPotentialKeys.sort((k1, k2) => k1.length - k2.length); const sortedKeys = allPotentialKeys.sort((k1, k2) => k1.length - k2.length);
@@ -291,7 +305,14 @@ export const getSkipItemsByList = (
} }
// we deal with explictly allow list secondly, apply them to PARENTS if possible // we deal with explictly allow list secondly, apply them to PARENTS if possible
const enableAllowMode = skipOrNotResults[allPotentialKeys[0]].enableAllowMode; let enableAllowMode = false;
if (
allPotentialKeys.length > 0 &&
allPotentialKeys[0] !== undefined &&
skipOrNotResults[allPotentialKeys[0]] !== undefined
) {
enableAllowMode = skipOrNotResults[allPotentialKeys[0]].enableAllowMode;
}
if (enableAllowMode) { if (enableAllowMode) {
for (let index = 0; index < sortedKeys.length; index++) { for (let index = 0; index < sortedKeys.length; index++) {
// reverse order, long(deep) to short(shadow), ascending // reverse order, long(deep) to short(shadow), ascending
@@ -329,6 +350,7 @@ export const getSkipItemsByList = (
result.push(key); result.push(key);
} }
} }
console.debug(`finalIsIgnored list= ${JSON.stringify(result)}`);
return result; return result;
}; };
@@ -495,11 +517,7 @@ const ensembleMixedEnties = async (
profiler?.insertSize("sizeof finalMappings", finalMappings); profiler?.insertSize("sizeof finalMappings", finalMappings);
// we check the skipOrNotResults again! in case we adjust some paths! // we check the skipOrNotResults again! in case we adjust some paths!
const allReallySkipKeys = getSkipItemsByList( const allReallySkipKeys = getSkipItemsByList(skipOrNotResults);
skipOrNotResults,
ignorePaths,
onlyAllowPaths
);
for (const key of allReallySkipKeys) { for (const key of allReallySkipKeys) {
delete finalMappings[key]; delete finalMappings[key];
} }
@@ -625,14 +643,36 @@ const getSyncPlanInplace = async (
mixedEntry.change = false; mixedEntry.change = false;
keptFolder.add(getParentFolder(key)); keptFolder.add(getParentFolder(key));
} else if (syncDirection === "incremental_pull_and_delete_only") { } else if (syncDirection === "incremental_pull_and_delete_only") {
mixedEntry.decisionBranch = 135; if (
mixedEntry.decision = "folder_to_be_deleted_on_local"; key === `${configDir}/` ||
mixedEntry.change = true; key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian folder!
mixedEntry.decisionBranch = 137;
mixedEntry.decision = "folder_existed_both_then_do_nothing";
mixedEntry.change = false;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 135;
mixedEntry.decision = "folder_to_be_deleted_on_local";
mixedEntry.change = true;
}
} else { } else {
// bidirectional // bidirectional
mixedEntry.decisionBranch = 124; if (
mixedEntry.decision = "folder_to_be_deleted_on_local"; key === `${configDir}/` ||
mixedEntry.change = true; key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian folder!
mixedEntry.decisionBranch = 138;
mixedEntry.decision = "folder_existed_both_then_do_nothing";
mixedEntry.change = false;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 124;
mixedEntry.decision = "folder_to_be_deleted_on_local";
mixedEntry.change = true;
}
} }
} else { } else {
// then the folder is created on local // then the folder is created on local
@@ -1074,13 +1114,35 @@ const getSyncPlanInplace = async (
mixedEntry.decision = "conflict_created_then_do_nothing"; mixedEntry.decision = "conflict_created_then_do_nothing";
mixedEntry.change = false; mixedEntry.change = false;
} else if (syncDirection === "incremental_pull_and_delete_only") { } else if (syncDirection === "incremental_pull_and_delete_only") {
mixedEntry.decisionBranch = 39; if (
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local"; key === `${configDir}/` ||
mixedEntry.change = true; key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian/bookmarks.json file!
mixedEntry.decisionBranch = 139;
mixedEntry.decision = "conflict_created_then_keep_local";
mixedEntry.change = true;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 39;
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local";
mixedEntry.change = true;
}
} else { } else {
mixedEntry.decisionBranch = 7; if (
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local"; key === `${configDir}/` ||
mixedEntry.change = true; key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian/bookmarks.json file!
mixedEntry.decisionBranch = 140;
mixedEntry.decision = "conflict_created_then_keep_local";
mixedEntry.change = true;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 7;
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local";
mixedEntry.change = true;
}
} }
} else { } else {
// if A is in the previous list and MODIFIED, A has been deleted by B but modified by A // if A is in the previous list and MODIFIED, A has been deleted by B but modified by A
+47
View File
@@ -252,6 +252,10 @@ const fromDriveItemToEntity = (x: DriveItem, remoteBaseDir: string): Entity => {
// why?? /drive/root:/Apps/Graph // why?? /drive/root:/Apps/Graph
const FIFTH_COMMON_PREFIX_REGEX = /^\/drive\/root:\/[^\/]+\/Graph\//g; const FIFTH_COMMON_PREFIX_REGEX = /^\/drive\/root:\/[^\/]+\/Graph\//g;
// why again?? /drive/root:/Apps/Graph 1
const SIXTH_COMMON_PREFIX_REGEX = /^\/drive\/root:\/[^\/]+\/Graph 1\//g;
const SIXTH_COMMON_PREFIX_REGEX_V2 = /^\/drive\/root:\/[^\/]+\/Graph%201\//g;
// or the root is absolute path /Livefolders, // or the root is absolute path /Livefolders,
// e.g.: /Livefolders/应用/remotely-save/${remoteBaseDir} // e.g.: /Livefolders/应用/remotely-save/${remoteBaseDir}
const SECOND_COMMON_PREFIX_REGEX = /^\/Livefolders\/[^\/]+\/remotely-save\//g; const SECOND_COMMON_PREFIX_REGEX = /^\/Livefolders\/[^\/]+\/remotely-save\//g;
@@ -278,6 +282,11 @@ const fromDriveItemToEntity = (x: DriveItem, remoteBaseDir: string): Entity => {
const fullPathOriginal = `${x.parentReference.path}/${x.name}`; const fullPathOriginal = `${x.parentReference.path}/${x.name}`;
const matchFirstPrefixRes = fullPathOriginal.match(FIRST_COMMON_PREFIX_REGEX); const matchFirstPrefixRes = fullPathOriginal.match(FIRST_COMMON_PREFIX_REGEX);
const matchFifthPrefixRes = fullPathOriginal.match(FIFTH_COMMON_PREFIX_REGEX); const matchFifthPrefixRes = fullPathOriginal.match(FIFTH_COMMON_PREFIX_REGEX);
const matchSixthPrefixRes = fullPathOriginal.match(SIXTH_COMMON_PREFIX_REGEX);
const matchSixthV2PrefixRes = fullPathOriginal.match(
SIXTH_COMMON_PREFIX_REGEX_V2
);
const matchSecondPrefixRes = fullPathOriginal.match( const matchSecondPrefixRes = fullPathOriginal.match(
SECOND_COMMON_PREFIX_REGEX SECOND_COMMON_PREFIX_REGEX
); );
@@ -317,6 +326,40 @@ const fromDriveItemToEntity = (x: DriveItem, remoteBaseDir: string): Entity => {
key = fullPathOriginal.substring(foundPrefix.length + 1); key = fullPathOriginal.substring(foundPrefix.length + 1);
} }
// sixth
else if (
matchSixthPrefixRes !== null &&
fullPathOriginal.startsWith(`${matchSixthPrefixRes[0]}${remoteBaseDir}`)
) {
const foundPrefix = `${matchSixthPrefixRes[0]}${remoteBaseDir}`;
key = fullPathOriginal.substring(foundPrefix.length + 1);
} else if (
matchSixthPrefixRes !== null &&
fullPathOriginal.startsWith(
`${matchSixthPrefixRes[0]}${remoteBaseDirEncoded}`
)
) {
const foundPrefix = `${matchSixthPrefixRes[0]}${remoteBaseDirEncoded}`;
key = fullPathOriginal.substring(foundPrefix.length + 1);
}
// sixth v2
else if (
matchSixthV2PrefixRes !== null &&
fullPathOriginal.startsWith(`${matchSixthV2PrefixRes[0]}${remoteBaseDir}`)
) {
const foundPrefix = `${matchSixthV2PrefixRes[0]}${remoteBaseDir}`;
key = fullPathOriginal.substring(foundPrefix.length + 1);
} else if (
matchSixthV2PrefixRes !== null &&
fullPathOriginal.startsWith(
`${matchSixthV2PrefixRes[0]}${remoteBaseDirEncoded}`
)
) {
const foundPrefix = `${matchSixthV2PrefixRes[0]}${remoteBaseDirEncoded}`;
key = fullPathOriginal.substring(foundPrefix.length + 1);
}
// second // second
else if ( else if (
matchSecondPrefixRes !== null && matchSecondPrefixRes !== null &&
@@ -374,6 +417,8 @@ const fromDriveItemToEntity = (x: DriveItem, remoteBaseDir: string): Entity => {
fullPathOriginal=${fullPathOriginal} fullPathOriginal=${fullPathOriginal}
matchFirstPrefixRes=${matchFirstPrefixRes} matchFirstPrefixRes=${matchFirstPrefixRes}
matchFifthPrefixRes=${matchFifthPrefixRes} matchFifthPrefixRes=${matchFifthPrefixRes}
matchSixthPrefixRes=${matchSixthPrefixRes}
matchSixthV2PrefixRes=${matchSixthV2PrefixRes}
matchSecondPrefixRes=${matchSecondPrefixRes} matchSecondPrefixRes=${matchSecondPrefixRes}
matchThirdPrefixRes=${matchThirdPrefixRes} matchThirdPrefixRes=${matchThirdPrefixRes}
${constructFromDriveItemToEntityError(x)}` ${constructFromDriveItemToEntityError(x)}`
@@ -388,6 +433,8 @@ ${constructFromDriveItemToEntityError(x)}`
fullPathOriginal=${fullPathOriginal} fullPathOriginal=${fullPathOriginal}
matchFirstPrefixRes=${matchFirstPrefixRes} matchFirstPrefixRes=${matchFirstPrefixRes}
matchFifthPrefixRes=${matchFifthPrefixRes} matchFifthPrefixRes=${matchFifthPrefixRes}
matchSixthPrefixRes=${matchSixthPrefixRes}
matchSixthV2PrefixRes=${matchSixthV2PrefixRes}
matchSecondPrefixRes=${matchSecondPrefixRes} matchSecondPrefixRes=${matchSecondPrefixRes}
matchThirdPrefixRes=${matchThirdPrefixRes} matchThirdPrefixRes=${matchThirdPrefixRes}
${constructFromDriveItemToEntityError(x)}` ${constructFromDriveItemToEntityError(x)}`
+33 -12
View File
@@ -138,7 +138,6 @@ if (VALID_REQURL) {
); );
} }
import isEqual from "lodash/isEqual";
// @ts-ignore // @ts-ignore
// biome-ignore lint: we want to ts-ignore the next line // biome-ignore lint: we want to ts-ignore the next line
import { AuthType, BufferLike, createClient } from "webdav/dist/web/index.js"; import { AuthType, BufferLike, createClient } from "webdav/dist/web/index.js";
@@ -169,23 +168,37 @@ const getWebdavPath = (fileOrFolderPath: string, remoteBaseDir: string) => {
return key; return key;
}; };
/**
* sometimes the path startswith /../../......
* we want to make sure the path is compatible
*/
const stripLeadingPath = (x: string) => {
let y = x;
while (y.startsWith("/..")) {
y = y.slice("/..".length);
}
return y;
};
const getNormPath = (fileOrFolderPath: string, remoteBaseDir: string) => { const getNormPath = (fileOrFolderPath: string, remoteBaseDir: string) => {
const strippedFileOrFolderPath = stripLeadingPath(fileOrFolderPath);
if ( if (
!( !(
fileOrFolderPath === `/${remoteBaseDir}` || strippedFileOrFolderPath === `/${remoteBaseDir}` ||
fileOrFolderPath.startsWith(`/${remoteBaseDir}/`) strippedFileOrFolderPath.startsWith(`/${remoteBaseDir}/`)
) )
) { ) {
throw Error( throw Error(
`"${fileOrFolderPath}" doesn't starts with "/${remoteBaseDir}/"` `"${fileOrFolderPath}" after stripping doesn't starts with "/${remoteBaseDir}/"`
); );
} }
const result = strippedFileOrFolderPath.slice(`/${remoteBaseDir}/`.length);
return fileOrFolderPath.slice(`/${remoteBaseDir}/`.length); return result;
}; };
const fromWebdavItemToEntity = (x: FileStat, remoteBaseDir: string): Entity => { const fromWebdavItemToEntity = (x: FileStat, remoteBaseDir: string): Entity => {
let key = getNormPath(x.filename, remoteBaseDir); let key = getNormPath(x.filename, remoteBaseDir);
if (x.type === "directory" && !key.endsWith("/")) { if (x.type === "directory" && !key.endsWith("/")) {
key = `${key}/`; key = `${key}/`;
} }
@@ -447,15 +460,17 @@ export class FakeFsWebdav extends FakeFs {
// console.debug(itemsToFetchChunks); // console.debug(itemsToFetchChunks);
const subContents = [] as FileStat[]; const subContents = [] as FileStat[];
for (const singleChunk of itemsToFetchChunks) { for (const singleChunk of itemsToFetchChunks) {
const r = singleChunk.map((x) => { const r = singleChunk.map(async (x) => {
return this.client.getDirectoryContents(x, { let k = (await this.client.getDirectoryContents(x, {
deep: false, deep: false,
details: false /* no need for verbose details here */, details: false /* no need for verbose details here */,
// TODO: to support .obsidian, // TODO: to support .obsidian,
// we need to load all files including dot, // we need to load all files including dot,
// anyway to reduce the resources? // anyway to reduce the resources?
// glob: "/**" /* avoid dot files by using glob */, // glob: "/**" /* avoid dot files by using glob */,
}) as Promise<FileStat[]>; })) as FileStat[];
k = k.filter((sub) => stripLeadingPath(sub.filename) !== x);
return k;
}); });
const r3 = await Promise.all(r); const r3 = await Promise.all(r);
for (const r4 of r3) { for (const r4 of r3) {
@@ -477,7 +492,7 @@ export class FakeFsWebdav extends FakeFs {
const f = subContents[i]; const f = subContents[i];
contents.push(f); contents.push(f);
if (f.type === "directory") { if (f.type === "directory") {
q.push(f.filename); q.push(stripLeadingPath(f.filename));
} }
} }
} }
@@ -495,7 +510,11 @@ export class FakeFsWebdav extends FakeFs {
} }
)) as FileStat[]; )) as FileStat[];
} }
return contents.map((x) => fromWebdavItemToEntity(x, this.remoteBaseDir));
const result = contents
.map((x) => fromWebdavItemToEntity(x, this.remoteBaseDir))
.filter((x) => x.keyRaw !== "/");
return result;
} }
async walkPartial(): Promise<Entity[]> { async walkPartial(): Promise<Entity[]> {
@@ -508,7 +527,9 @@ export class FakeFsWebdav extends FakeFs {
details: false /* no need for verbose details here */, details: false /* no need for verbose details here */,
} }
)) as FileStat[]; )) as FileStat[];
return contents.map((x) => fromWebdavItemToEntity(x, this.remoteBaseDir)); return contents
.map((x) => fromWebdavItemToEntity(x, this.remoteBaseDir))
.filter((x) => x.keyRaw !== "/");
} }
async stat(key: string): Promise<Entity> { async stat(key: string): Promise<Entity> {
+8
View File
@@ -1560,6 +1560,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.dropbox.refreshToken !== "" && this.settings.dropbox.refreshToken !== "" &&
current >= this.settings!.dropbox!.credentialsShouldBeDeletedAtTime! current >= this.settings!.dropbox!.credentialsShouldBeDeletedAtTime!
) { ) {
console.warn(`dropbox expired`);
dropboxExpired = true; dropboxExpired = true;
this.settings.dropbox = cloneDeep(DEFAULT_DROPBOX_CONFIG); this.settings.dropbox = cloneDeep(DEFAULT_DROPBOX_CONFIG);
needSave = true; needSave = true;
@@ -1570,6 +1571,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.onedrive.refreshToken !== "" && this.settings.onedrive.refreshToken !== "" &&
current >= this.settings!.onedrive!.credentialsShouldBeDeletedAtTime! current >= this.settings!.onedrive!.credentialsShouldBeDeletedAtTime!
) { ) {
console.warn(`onedrive expired`);
onedriveExpired = true; onedriveExpired = true;
this.settings.onedrive = cloneDeep(DEFAULT_ONEDRIVE_CONFIG); this.settings.onedrive = cloneDeep(DEFAULT_ONEDRIVE_CONFIG);
needSave = true; needSave = true;
@@ -1580,6 +1582,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.onedrivefull.refreshToken !== "" && this.settings.onedrivefull.refreshToken !== "" &&
current >= this.settings!.onedrivefull!.credentialsShouldBeDeletedAtTime! current >= this.settings!.onedrivefull!.credentialsShouldBeDeletedAtTime!
) { ) {
console.warn(`onedrive full expired`);
onedriveFullExpired = true; onedriveFullExpired = true;
this.settings.onedrivefull = cloneDeep(DEFAULT_ONEDRIVEFULL_CONFIG); this.settings.onedrivefull = cloneDeep(DEFAULT_ONEDRIVEFULL_CONFIG);
needSave = true; needSave = true;
@@ -1590,6 +1593,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.googledrive.refreshToken !== "" && this.settings.googledrive.refreshToken !== "" &&
current >= this.settings!.googledrive!.credentialsShouldBeDeletedAtTimeMs! current >= this.settings!.googledrive!.credentialsShouldBeDeletedAtTimeMs!
) { ) {
console.warn(`google drive expired`);
googleDriveExpired = true; googleDriveExpired = true;
this.settings.googledrive = cloneDeep(DEFAULT_GOOGLEDRIVE_CONFIG); this.settings.googledrive = cloneDeep(DEFAULT_GOOGLEDRIVE_CONFIG);
needSave = true; needSave = true;
@@ -1600,6 +1604,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.box.refreshToken !== "" && this.settings.box.refreshToken !== "" &&
current >= this.settings!.box!.credentialsShouldBeDeletedAtTimeMs! current >= this.settings!.box!.credentialsShouldBeDeletedAtTimeMs!
) { ) {
console.warn(`box expired`);
boxExpired = true; boxExpired = true;
this.settings.box = cloneDeep(DEFAULT_BOX_CONFIG); this.settings.box = cloneDeep(DEFAULT_BOX_CONFIG);
needSave = true; needSave = true;
@@ -1610,6 +1615,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.pcloud.accessToken !== "" && this.settings.pcloud.accessToken !== "" &&
current >= this.settings!.pcloud!.credentialsShouldBeDeletedAtTimeMs! current >= this.settings!.pcloud!.credentialsShouldBeDeletedAtTimeMs!
) { ) {
console.warn(`pcloud expired`);
pCloudExpired = true; pCloudExpired = true;
this.settings.pcloud = cloneDeep(DEFAULT_PCLOUD_CONFIG); this.settings.pcloud = cloneDeep(DEFAULT_PCLOUD_CONFIG);
needSave = true; needSave = true;
@@ -1620,6 +1626,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.yandexdisk.refreshToken !== "" && this.settings.yandexdisk.refreshToken !== "" &&
current >= this.settings!.yandexdisk!.credentialsShouldBeDeletedAtTimeMs! current >= this.settings!.yandexdisk!.credentialsShouldBeDeletedAtTimeMs!
) { ) {
console.warn(`yandex disk expired`);
yandexDiskExpired = true; yandexDiskExpired = true;
this.settings.yandexdisk = cloneDeep(DEFAULT_YANDEXDISK_CONFIG); this.settings.yandexdisk = cloneDeep(DEFAULT_YANDEXDISK_CONFIG);
needSave = true; needSave = true;
@@ -1630,6 +1637,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.koofr.refreshToken !== "" && this.settings.koofr.refreshToken !== "" &&
current >= this.settings!.koofr!.credentialsShouldBeDeletedAtTimeMs! current >= this.settings!.koofr!.credentialsShouldBeDeletedAtTimeMs!
) { ) {
console.warn(`koofr expired`);
koofrExpired = true; koofrExpired = true;
this.settings.koofr = cloneDeep(DEFAULT_KOOFR_CONFIG); this.settings.koofr = cloneDeep(DEFAULT_KOOFR_CONFIG);
needSave = true; needSave = true;