sync webdav

This commit is contained in:
Gabe Yuan
2023-09-18 17:36:10 +08:00
parent 92ffda5220
commit 3494bb1297
7 changed files with 312 additions and 17 deletions

View File

@@ -1 +1,3 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-3.6.3.cjs

View File

@@ -15,6 +15,7 @@
"react-markdown": "^8.0.7",
"react-router-dom": "^6.10.0",
"react-scripts": "5.0.1",
"webdav": "^5.3.0",
"webextension-polyfill": "^0.10.0"
},
"scripts": {
@@ -61,5 +62,6 @@
"@babel/preset-env": "^7.22.10",
"react-app-rewired": "^2.2.1",
"wrangler": "^3.4.0"
}
},
"packageManager": "yarn@3.6.3"
}

View File

@@ -435,10 +435,18 @@ export const I18N = {
zh: `重启浏览器时清除缓存`,
en: `Clear cache when restarting browser`,
},
data_sync_type: {
zh: `数据同步方式`,
en: `Data Sync Type`,
},
data_sync_url: {
zh: `数据同步接口`,
en: `Data Sync API`,
},
data_sync_user: {
zh: `数据同步账户`,
en: `Data Sync User`,
},
data_sync_key: {
zh: `数据同步密钥`,
en: `Data Sync Key`,
@@ -460,8 +468,8 @@ export const I18N = {
en: `Sorry, something went wrong!`,
},
error_sync_setting: {
zh: `您的同步设置未填写,无法在线分享。`,
en: `Your sync settings are missing and cannot be shared online.`,
zh: `您的同步类型必须为“KISS-Worker”且需填写完整`,
en: `Your sync type must be "KISS-Worker" and must be filled in completely`,
},
click_test: {
zh: `点击测试`,

View File

@@ -1,4 +1,5 @@
import {
APP_LCNAME,
KV_SETTING_KEY,
KV_RULES_KEY,
KV_RULES_SHARE_KEY,
@@ -15,6 +16,45 @@ import {
} from "./storage";
import { apiSyncData } from "../apis";
import { sha256 } from "./utils";
import { createClient } from "webdav";
const syncByWebdav = async ({
key,
value,
syncUrl,
syncUser,
syncKey,
updateAt = 0,
syncAt = 0,
isForce = false,
}) => {
const client = createClient(syncUrl, {
username: syncUser,
password: syncKey,
});
const pathname = `/${APP_LCNAME}`;
const filename = `/${APP_LCNAME}/${key}`;
const data = JSON.stringify(value, null, " ");
if ((await client.exists(pathname)) === false) {
await client.createDirectory(pathname);
}
const isExist = await client.exists(filename);
if (isExist && !isForce) {
const { lastmod } = await client.stat(filename);
const fileUpdateAt = Date.parse(lastmod);
if (syncAt === 0 || fileUpdateAt > updateAt) {
const data = await client.getFileContents(filename, { format: "text" });
return { updateAt: fileUpdateAt, value: JSON.parse(data) };
}
}
await client.putFileContents(filename, data);
const { lastmod } = await client.stat(filename);
const fileUpdateAt = Date.parse(lastmod);
return { updateAt: fileUpdateAt, value };
};
const syncByWorker = async ({
key,
@@ -59,16 +99,21 @@ const syncSetting = async (isBg = false, isForce = false) => {
}
const setting = await getSettingWithDefault();
const res = await syncByWorker({
const args = {
key: KV_SETTING_KEY,
value: setting,
syncUrl,
syncUser,
syncKey,
updateAt: settingUpdateAt,
syncAt: settingSyncAt,
isBg,
isForce,
});
};
const res =
syncType === OPT_SYNCTYPE_WEBDAV
? await syncByWebdav(args)
: await syncByWorker(args);
if (res.updateAt > settingUpdateAt) {
await setSetting(res.value);
@@ -107,16 +152,21 @@ const syncRules = async (isBg = false, isForce = false) => {
}
const rules = await getRulesWithDefault();
const res = await syncByWorker({
const args = {
key: KV_RULES_KEY,
value: rules,
syncUrl,
syncUser,
syncKey,
updateAt: rulesUpdateAt,
syncAt: rulesSyncAt,
isBg,
isForce,
});
};
const res =
syncType === OPT_SYNCTYPE_WEBDAV
? await syncByWebdav(args)
: await syncByWorker(args);
if (res.updateAt > rulesUpdateAt) {
await setRules(res.value);
@@ -143,14 +193,15 @@ export const trySyncRules = async (isBg = false, isForce = false) => {
* @returns
*/
export const syncShareRules = async ({ rules, syncUrl, syncKey }) => {
await syncByWorker({
const args = {
key: KV_RULES_SHARE_KEY,
value: rules,
syncUrl,
syncKey,
updateAt: Date.now(),
syncAt: Date.now(),
});
};
await syncByWorker(args);
const psk = await sha256(syncKey, KV_SALT_SHARE);
const shareUrl = `${syncUrl}/rules?psk=${psk}`;
return shareUrl;

View File

@@ -14,6 +14,7 @@ import {
OPT_STYLE_DIY,
OPT_STYLE_USE_COLOR,
URL_KISS_RULES_NEW_ISSUE,
OPT_SYNCTYPE_WORKER,
} from "../../config";
import { useState, useRef, useEffect, useMemo } from "react";
import { useI18n } from "../../hooks/I18n";
@@ -445,8 +446,8 @@ function ShareButton({ rules, injectRules, selectedUrl }) {
const i18n = useI18n();
const handleClick = async () => {
try {
const { syncUrl, syncKey } = await getSyncWithDefault();
if (!syncUrl || !syncKey) {
const { syncType, syncUrl, syncKey } = await getSyncWithDefault();
if (syncType !== OPT_SYNCTYPE_WORKER || !syncUrl || !syncKey) {
alert.warning(i18n("error_sync_setting"));
return;
}

View File

@@ -5,7 +5,13 @@ import { useI18n } from "../../hooks/I18n";
import { useSync } from "../../hooks/Sync";
import Alert from "@mui/material/Alert";
import Link from "@mui/material/Link";
import { URL_KISS_WORKER } from "../../config";
import MenuItem from "@mui/material/MenuItem";
import {
URL_KISS_WORKER,
OPT_SYNCTYPE_ALL,
OPT_SYNCTYPE_WORKER,
OPT_SYNCTYPE_WEBDAV,
} from "../../config";
import { useState } from "react";
import { syncSettingAndRules } from "../../libs/sync";
import Button from "@mui/material/Button";
@@ -48,13 +54,33 @@ export default function SyncSetting() {
return;
}
const { syncUrl = "", syncKey = "" } = sync;
const {
syncType = OPT_SYNCTYPE_WORKER,
syncUrl = "",
syncUser = "",
syncKey = "",
} = sync;
return (
<Box>
<Stack spacing={3}>
<Alert severity="warning">{i18n("sync_warn")}</Alert>
<TextField
select
size="small"
name="syncType"
value={syncType}
label={i18n("data_sync_type")}
onChange={handleChange}
>
{OPT_SYNCTYPE_ALL.map((item) => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</TextField>
<TextField
size="small"
label={i18n("data_sync_url")}
@@ -62,12 +88,24 @@ export default function SyncSetting() {
value={syncUrl}
onChange={handleChange}
helperText={
syncType === OPT_SYNCTYPE_WORKER && (
<Link href={URL_KISS_WORKER} target="_blank">
{i18n("about_sync_api")}
</Link>
)
}
/>
{syncType === OPT_SYNCTYPE_WEBDAV && (
<TextField
size="small"
label={i18n("data_sync_user")}
name="syncUser"
value={syncUser}
onChange={handleChange}
/>
)}
<TextField
size="small"
type="password"

195
yarn.lock
View File

@@ -1666,6 +1666,18 @@ __metadata:
languageName: node
linkType: hard
"@buttercup/fetch@npm:^0.1.1":
version: 0.1.2
resolution: "@buttercup/fetch@npm:0.1.2"
dependencies:
node-fetch: ^3.3.0
dependenciesMeta:
node-fetch:
optional: true
checksum: 005ed59a4189183d4ad17c97ccb3558c0d9e2d89411f7d1c4387df7c2a68a0fde889c973e1518ce14f7456306d0bbaeb9e4624b736f578439c5384197c3f4963
languageName: node
linkType: hard
"@cloudflare/kv-asset-handler@npm:^0.2.0":
version: 0.2.0
resolution: "@cloudflare/kv-asset-handler@npm:0.2.0"
@@ -4665,6 +4677,13 @@ __metadata:
languageName: node
linkType: hard
"base-64@npm:^1.0.0":
version: 1.0.0
resolution: "base-64@npm:1.0.0"
checksum: d10b64a1fc9b2c5a5f39f1ce1e6c9d1c5b249222bbfa3a0604c592d90623caf74419983feadd8a170f27dc0c3389704f72faafa3e645aeb56bfc030c93ff074a
languageName: node
linkType: hard
"base64-js@npm:^1.3.1":
version: 1.5.1
resolution: "base64-js@npm:1.5.1"
@@ -4880,6 +4899,13 @@ __metadata:
languageName: node
linkType: hard
"byte-length@npm:^1.0.2":
version: 1.0.2
resolution: "byte-length@npm:1.0.2"
checksum: 69e2b00a14a81f675ea9946135c42ee1a1d9f689d5ba1327eb6700fcde2ccacbd09b42f7e514de1d2b763960251d8c790b3d7304a5a1a27b1457e34c129be8c7
languageName: node
linkType: hard
"bytes@npm:3.0.0":
version: 3.0.0
resolution: "bytes@npm:3.0.0"
@@ -5040,6 +5066,13 @@ __metadata:
languageName: node
linkType: hard
"charenc@npm:0.0.2":
version: 0.0.2
resolution: "charenc@npm:0.0.2"
checksum: 81dcadbe57e861d527faf6dd3855dc857395a1c4d6781f4847288ab23cffb7b3ee80d57c15bba7252ffe3e5e8019db767757ee7975663ad2ca0939bb8fcaf2e5
languageName: node
linkType: hard
"check-types@npm:^11.1.1":
version: 11.2.2
resolution: "check-types@npm:11.2.2"
@@ -5447,6 +5480,13 @@ __metadata:
languageName: node
linkType: hard
"crypt@npm:0.0.2":
version: 0.0.2
resolution: "crypt@npm:0.0.2"
checksum: baf4c7bbe05df656ec230018af8cf7dbe8c14b36b98726939cef008d473f6fe7a4fad906cfea4062c93af516f1550a3f43ceb4d6615329612c6511378ed9fe34
languageName: node
linkType: hard
"crypto-random-string@npm:^2.0.0":
version: 2.0.0
resolution: "crypto-random-string@npm:2.0.0"
@@ -5739,6 +5779,13 @@ __metadata:
languageName: node
linkType: hard
"data-uri-to-buffer@npm:^4.0.0":
version: 4.0.1
resolution: "data-uri-to-buffer@npm:4.0.1"
checksum: 0d0790b67ffec5302f204c2ccca4494f70b4e2d940fea3d36b09f0bb2b8539c2e86690429eb1f1dc4bcc9e4df0644193073e63d9ee48ac9fce79ec1506e4aa4c
languageName: node
linkType: hard
"data-urls@npm:^2.0.0":
version: 2.0.0
resolution: "data-urls@npm:2.0.0"
@@ -7028,6 +7075,17 @@ __metadata:
languageName: node
linkType: hard
"fast-xml-parser@npm:^4.2.4":
version: 4.2.7
resolution: "fast-xml-parser@npm:4.2.7"
dependencies:
strnum: ^1.0.5
bin:
fxparser: src/cli/cli.js
checksum: d8b0c9e04756f6c43fa0399428f30149acadae21350e42e26e8fe98e24e6afa6b9b00aa554453795036b00e9fee974a1b556fe2ba18be391d51a9bf1ab790e7c
languageName: node
linkType: hard
"fastq@npm:^1.6.0":
version: 1.15.0
resolution: "fastq@npm:1.15.0"
@@ -7055,6 +7113,16 @@ __metadata:
languageName: node
linkType: hard
"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4":
version: 3.2.0
resolution: "fetch-blob@npm:3.2.0"
dependencies:
node-domexception: ^1.0.0
web-streams-polyfill: ^3.0.3
checksum: f19bc28a2a0b9626e69fd7cf3a05798706db7f6c7548da657cbf5026a570945f5eeaedff52007ea35c8bcd3d237c58a20bf1543bc568ab2422411d762dd3d5bf
languageName: node
linkType: hard
"file-entry-cache@npm:^6.0.1":
version: 6.0.1
resolution: "file-entry-cache@npm:6.0.1"
@@ -7277,6 +7345,15 @@ __metadata:
languageName: node
linkType: hard
"formdata-polyfill@npm:^4.0.10":
version: 4.0.10
resolution: "formdata-polyfill@npm:4.0.10"
dependencies:
fetch-blob: ^3.1.2
checksum: 82a34df292afadd82b43d4a740ce387bc08541e0a534358425193017bf9fb3567875dc5f69564984b1da979979b70703aa73dee715a17b6c229752ae736dd9db
languageName: node
linkType: hard
"forwarded@npm:0.2.0":
version: 0.2.0
resolution: "forwarded@npm:0.2.0"
@@ -7778,6 +7855,13 @@ __metadata:
languageName: node
linkType: hard
"hot-patcher@npm:^2.0.0":
version: 2.0.1
resolution: "hot-patcher@npm:2.0.1"
checksum: 1f30c5d59ba6dcb2b81485d492dbee9520ac262b1502ed1eeca44a52d92f48f8ebc1055a5e462db344a91f834f7cf19d69a2327158b68be7b4858145a0fc8f83
languageName: node
linkType: hard
"hpack.js@npm:^2.1.6":
version: 2.1.6
resolution: "hpack.js@npm:2.1.6"
@@ -8212,6 +8296,13 @@ __metadata:
languageName: node
linkType: hard
"is-buffer@npm:~1.1.6":
version: 1.1.6
resolution: "is-buffer@npm:1.1.6"
checksum: 4a186d995d8bbf9153b4bd9ff9fd04ae75068fe695d29025d25e592d9488911eeece84eefbd8fa41b8ddcc0711058a71d4c466dcf6f1f6e1d83830052d8ca707
languageName: node
linkType: hard
"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7":
version: 1.2.7
resolution: "is-callable@npm:1.2.7"
@@ -9425,6 +9516,7 @@ __metadata:
react-markdown: ^8.0.7
react-router-dom: ^6.10.0
react-scripts: 5.0.1
webdav: ^5.3.0
webextension-polyfill: ^0.10.0
wrangler: ^3.4.0
languageName: unknown
@@ -9477,6 +9569,13 @@ __metadata:
languageName: node
linkType: hard
"layerr@npm:^2.0.1":
version: 2.0.1
resolution: "layerr@npm:2.0.1"
checksum: a57b0ceff240b4869571b0b87c0bc28dc71b131a652c6da5fee03d6f590581c5a57f794edb2ec5ee7c8f7030bf99a4e090516efbd92a2e7382336d60a3d10763
languageName: node
linkType: hard
"leven@npm:^3.1.0":
version: 3.1.0
resolution: "leven@npm:3.1.0"
@@ -9724,6 +9823,17 @@ __metadata:
languageName: node
linkType: hard
"md5@npm:^2.3.0":
version: 2.3.0
resolution: "md5@npm:2.3.0"
dependencies:
charenc: 0.0.2
crypt: 0.0.2
is-buffer: ~1.1.6
checksum: a63cacf4018dc9dee08c36e6f924a64ced735b37826116c905717c41cebeb41a522f7a526ba6ad578f9c80f02cb365033ccd67fe186ffbcc1a1faeb75daa9b6e
languageName: node
linkType: hard
"mdast-util-definitions@npm:^5.0.0":
version: 5.1.2
resolution: "mdast-util-definitions@npm:5.1.2"
@@ -10191,6 +10301,15 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:^7.4.6":
version: 7.4.6
resolution: "minimatch@npm:7.4.6"
dependencies:
brace-expansion: ^2.0.1
checksum: 1a6c8d22618df9d2a88aabeef1de5622eb7b558e9f8010be791cb6b0fa6e102d39b11c28d75b855a1e377b12edc7db8ff12a99c20353441caa6a05e78deb5da9
languageName: node
linkType: hard
"minimatch@npm:^9.0.1":
version: 9.0.3
resolution: "minimatch@npm:9.0.3"
@@ -10422,6 +10541,13 @@ __metadata:
languageName: node
linkType: hard
"nested-property@npm:^4.0.0":
version: 4.0.0
resolution: "nested-property@npm:4.0.0"
checksum: 9c86f2c722429e167876d5becf276139a6aa4b8732b6d9e32de9aa44dfd017702b60614cc87aec961dea47ae50dae0951d5b5f66fc30288f18bf581c16e42ca2
languageName: node
linkType: hard
"no-case@npm:^3.0.4":
version: 3.0.4
resolution: "no-case@npm:3.0.4"
@@ -10441,6 +10567,13 @@ __metadata:
languageName: node
linkType: hard
"node-domexception@npm:^1.0.0":
version: 1.0.0
resolution: "node-domexception@npm:1.0.0"
checksum: ee1d37dd2a4eb26a8a92cd6b64dfc29caec72bff5e1ed9aba80c294f57a31ba4895a60fd48347cf17dd6e766da0ae87d75657dfd1f384ebfa60462c2283f5c7f
languageName: node
linkType: hard
"node-environment-flags@npm:^1.0.5":
version: 1.0.6
resolution: "node-environment-flags@npm:1.0.6"
@@ -10451,6 +10584,17 @@ __metadata:
languageName: node
linkType: hard
"node-fetch@npm:^3.3.0":
version: 3.3.2
resolution: "node-fetch@npm:3.3.2"
dependencies:
data-uri-to-buffer: ^4.0.0
fetch-blob: ^3.1.4
formdata-polyfill: ^4.0.10
checksum: 06a04095a2ddf05b0830a0d5302699704d59bda3102894ea64c7b9d4c865ecdff2d90fd042df7f5bc40337266961cb6183dcc808ea4f3000d024f422b462da92
languageName: node
linkType: hard
"node-forge@npm:^1":
version: 1.3.1
resolution: "node-forge@npm:1.3.1"
@@ -10913,6 +11057,13 @@ __metadata:
languageName: node
linkType: hard
"path-posix@npm:^1.0.0":
version: 1.0.0
resolution: "path-posix@npm:1.0.0"
checksum: 4f64ad212de6ad8d0dbfa440cac8b924303c25c30301769ad0501e29e83a5b9d469e8133753f999ad37482c9c8d3511129e4d83db55d2e4b1555b183c9749ae8
languageName: node
linkType: hard
"path-scurry@npm:^1.10.1":
version: 1.10.1
resolution: "path-scurry@npm:1.10.1"
@@ -13579,6 +13730,13 @@ __metadata:
languageName: node
linkType: hard
"strnum@npm:^1.0.5":
version: 1.0.5
resolution: "strnum@npm:1.0.5"
checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2
languageName: node
linkType: hard
"style-loader@npm:^3.3.1":
version: 3.3.3
resolution: "style-loader@npm:3.3.3"
@@ -14377,7 +14535,14 @@ __metadata:
languageName: node
linkType: hard
"url-parse@npm:^1.5.3":
"url-join@npm:^5.0.0":
version: 5.0.0
resolution: "url-join@npm:5.0.0"
checksum: 5921384a8ad4395b49ce4b50aa26efbc429cebe0bc8b3660ad693dd12fd859747b5369be0443e60e53a7850b2bc9d7d0687bcb94386662b40e743596bbf38101
languageName: node
linkType: hard
"url-parse@npm:^1.5.10, url-parse@npm:^1.5.3":
version: 1.5.10
resolution: "url-parse@npm:1.5.10"
dependencies:
@@ -14538,6 +14703,34 @@ __metadata:
languageName: node
linkType: hard
"web-streams-polyfill@npm:^3.0.3":
version: 3.2.1
resolution: "web-streams-polyfill@npm:3.2.1"
checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02
languageName: node
linkType: hard
"webdav@npm:^5.3.0":
version: 5.3.0
resolution: "webdav@npm:5.3.0"
dependencies:
"@buttercup/fetch": ^0.1.1
base-64: ^1.0.0
byte-length: ^1.0.2
fast-xml-parser: ^4.2.4
he: ^1.2.0
hot-patcher: ^2.0.0
layerr: ^2.0.1
md5: ^2.3.0
minimatch: ^7.4.6
nested-property: ^4.0.0
path-posix: ^1.0.0
url-join: ^5.0.0
url-parse: ^1.5.10
checksum: 66f85e181c7d51f0ce0a55f4aed6f0347b73b3e1a4dd2e3312c39a4d966f71135423247e11ff7d4991074c16a7fb2ab30577774849edfd529a2cc9354c413114
languageName: node
linkType: hard
"webextension-polyfill@npm:^0.10.0":
version: 0.10.0
resolution: "webextension-polyfill@npm:0.10.0"