sync webdav
This commit is contained in:
@@ -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: `点击测试`,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user