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

@@ -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={
<Link href={URL_KISS_WORKER} target="_blank">
{i18n("about_sync_api")}
</Link>
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"