This commit is contained in:
Gabe Yuan
2023-09-20 17:47:23 +08:00
parent 01ebc184ad
commit 489bc9534b
10 changed files with 117 additions and 153 deletions

View File

@@ -21,7 +21,7 @@ import { sha256 } from "../libs/utils";
* @param {*} data * @param {*} data
* @returns * @returns
*/ */
export const apiSyncData = async (url, key, data, isBg = false) => export const apiSyncData = async (url, key, data) =>
fetchPolyfill(url, { fetchPolyfill(url, {
headers: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
@@ -29,16 +29,14 @@ export const apiSyncData = async (url, key, data, isBg = false) =>
}, },
method: "POST", method: "POST",
body: JSON.stringify(data), body: JSON.stringify(data),
isBg,
}); });
/** /**
* 下载数据 * 下载数据
* @param {*} url * @param {*} url
* @param {*} isBg
* @returns * @returns
*/ */
export const apiFetch = (url, isBg = false) => fetchPolyfill(url, { isBg }); export const apiFetch = (url) => fetchPolyfill(url);
/** /**
* 谷歌翻译 * 谷歌翻译

View File

@@ -32,7 +32,7 @@ browser.runtime.onStartup.addListener(async () => {
console.log("browser onStartup"); console.log("browser onStartup");
// 同步数据 // 同步数据
await trySyncSettingAndRules(true); await trySyncSettingAndRules();
// 清除缓存 // 清除缓存
const setting = await getSettingWithDefault(); const setting = await getSettingWithDefault();
@@ -41,7 +41,7 @@ browser.runtime.onStartup.addListener(async () => {
} }
// 同步订阅规则 // 同步订阅规则
trySyncAllSubRules(setting, true); trySyncAllSubRules(setting);
}); });
/** /**

View File

@@ -302,10 +302,11 @@ export const DEFAULT_SYNC = {
syncUrl: "", // 数据同步接口 syncUrl: "", // 数据同步接口
syncUser: "", // 数据同步用户名 syncUser: "", // 数据同步用户名
syncKey: "", // 数据同步密钥 syncKey: "", // 数据同步密钥
settingUpdateAt: 0, syncMeta: {}, // 数据更新及同步信息
settingSyncAt: 0, // settingUpdateAt: 0,
rulesUpdateAt: 0, // settingSyncAt: 0,
rulesSyncAt: 0, // rulesUpdateAt: 0,
// rulesSyncAt: 0,
subRulesSyncAt: 0, // 订阅规则同步时间 subRulesSyncAt: 0, // 订阅规则同步时间
dataCaches: {}, // 缓存同步时间 dataCaches: {}, // 缓存同步时间
}; };

View File

@@ -1,8 +1,9 @@
import { STOKEY_RULES, DEFAULT_RULES } from "../config"; import { STOKEY_RULES, DEFAULT_RULES, KV_RULES_KEY } from "../config";
import { useStorage } from "./Storage"; import { useStorage } from "./Storage";
import { trySyncRules } from "../libs/sync"; import { trySyncRules } from "../libs/sync";
import { checkRules } from "../libs/rules"; import { checkRules } from "../libs/rules";
import { useCallback } from "react"; import { useCallback } from "react";
import { useSyncMeta } from "./Sync";
/** /**
* 规则 hook * 规则 hook
@@ -10,13 +11,15 @@ import { useCallback } from "react";
*/ */
export function useRules() { export function useRules() {
const { data: list, save } = useStorage(STOKEY_RULES, DEFAULT_RULES); const { data: list, save } = useStorage(STOKEY_RULES, DEFAULT_RULES);
const { updateSyncMeta } = useSyncMeta();
const updateRules = useCallback( const updateRules = useCallback(
async (rules) => { async (rules) => {
await save(rules); await save(rules);
trySyncRules(false, true); await updateSyncMeta(KV_RULES_KEY);
trySyncRules();
}, },
[save] [save, updateSyncMeta]
); );
const add = useCallback( const add = useCallback(

View File

@@ -1,8 +1,9 @@
import { STOKEY_SETTING, DEFAULT_SETTING } from "../config"; import { STOKEY_SETTING, DEFAULT_SETTING, KV_SETTING_KEY } from "../config";
import { useStorage } from "./Storage"; import { useStorage } from "./Storage";
import { trySyncSetting } from "../libs/sync"; import { trySyncSetting } from "../libs/sync";
import { createContext, useCallback, useContext, useMemo } from "react"; import { createContext, useCallback, useContext, useMemo } from "react";
import { debounce } from "../libs/utils"; import { debounce } from "../libs/utils";
import { useSyncMeta } from "./Sync";
const SettingContext = createContext({ const SettingContext = createContext({
setting: null, setting: null,
@@ -12,11 +13,12 @@ const SettingContext = createContext({
export function SettingProvider({ children }) { export function SettingProvider({ children }) {
const { data, update, reload } = useStorage(STOKEY_SETTING, DEFAULT_SETTING); const { data, update, reload } = useStorage(STOKEY_SETTING, DEFAULT_SETTING);
const { updateSyncMeta } = useSyncMeta();
const syncSetting = useMemo( const syncSetting = useMemo(
() => () =>
debounce(() => { debounce(() => {
trySyncSetting(false, true); trySyncSetting();
}, [2000]), }, [2000]),
[] []
); );
@@ -24,9 +26,10 @@ export function SettingProvider({ children }) {
const updateSetting = useCallback( const updateSetting = useCallback(
async (obj) => { async (obj) => {
await update(obj); await update(obj);
await updateSyncMeta(KV_SETTING_KEY);
syncSetting(); syncSetting();
}, },
[update, syncSetting] [update, syncSetting, updateSyncMeta]
); );
if (!data) { if (!data) {

View File

@@ -11,6 +11,23 @@ export function useSync() {
return { sync: data, updateSync: update, reloadSync: reload }; return { sync: data, updateSync: update, reloadSync: reload };
} }
/**
* update syncmeta hook
* @returns
*/
export function useSyncMeta() {
const { sync, updateSync } = useSync();
const updateSyncMeta = useCallback(
async (key) => {
const syncMeta = sync?.syncMeta || {};
syncMeta[key] = { ...(syncMeta[key] || {}), updateAt: Date.now() };
await updateSync({ syncMeta });
},
[sync, updateSync]
);
return { updateSyncMeta };
}
/** /**
* caches sync hook * caches sync hook
* @param {*} url * @param {*} url

View File

@@ -13,6 +13,7 @@ import {
DEFAULT_FETCH_LIMIT, DEFAULT_FETCH_LIMIT,
} from "../config"; } from "../config";
import { msAuth } from "./auth"; import { msAuth } from "./auth";
import { isBg } from "./browser";
/** /**
* 油猴脚本的请求封装 * 油猴脚本的请求封装
@@ -176,13 +177,13 @@ export const fetchData = async (
* @param {*} opts * @param {*} opts
* @returns * @returns
*/ */
export const fetchPolyfill = async (input, { isBg = false, ...opts } = {}) => { export const fetchPolyfill = async (input, opts) => {
if (!input.trim()) { if (!input.trim()) {
throw new Error("URL is empty"); throw new Error("URL is empty");
} }
// 插件 // 插件
if (isExt && !isBg) { if (isExt && !isBg()) {
const res = await sendBgMsg(MSG_FETCH, { input, opts }); const res = await sendBgMsg(MSG_FETCH, { input, opts });
if (res.error) { if (res.error) {
throw new Error(res.error); throw new Error(res.error);

View File

@@ -149,5 +149,5 @@ export const saveRule = async (newRule) => {
rules.unshift(newRule); rules.unshift(newRule);
} }
await setRules(rules); await setRules(rules);
trySyncRules(false, true); trySyncRules();
}; };

View File

@@ -25,8 +25,8 @@ const updateSyncDataCache = async (url) => {
* @param {*} url * @param {*} url
* @returns * @returns
*/ */
export const syncSubRules = async (url, isBg = false) => { export const syncSubRules = async (url) => {
const res = await apiFetch(url, isBg); const res = await apiFetch(url);
const rules = checkRules(res).filter( const rules = checkRules(res).filter(
({ pattern }) => !isAllchar(pattern, GLOBAL_KEY) ({ pattern }) => !isAllchar(pattern, GLOBAL_KEY)
); );
@@ -41,10 +41,10 @@ export const syncSubRules = async (url, isBg = false) => {
* @param {*} url * @param {*} url
* @returns * @returns
*/ */
export const syncAllSubRules = async (subrulesList, isBg = false) => { export const syncAllSubRules = async (subrulesList) => {
for (let subrules of subrulesList) { for (let subrules of subrulesList) {
try { try {
await syncSubRules(subrules.url, isBg); await syncSubRules(subrules.url);
await updateSyncDataCache(subrules.url); await updateSyncDataCache(subrules.url);
} catch (err) { } catch (err) {
console.log(`[sync subrule error]: ${subrules.url}`, err); console.log(`[sync subrule error]: ${subrules.url}`, err);
@@ -57,14 +57,14 @@ export const syncAllSubRules = async (subrulesList, isBg = false) => {
* @param {*} url * @param {*} url
* @returns * @returns
*/ */
export const trySyncAllSubRules = async ({ subrulesList }, isBg = false) => { export const trySyncAllSubRules = async ({ subrulesList }) => {
try { try {
const { subRulesSyncAt } = await getSyncWithDefault(); const { subRulesSyncAt } = await getSyncWithDefault();
const now = Date.now(); const now = Date.now();
const interval = 24 * 60 * 60 * 1000; // 间隔一天 const interval = 24 * 60 * 60 * 1000; // 间隔一天
if (now - subRulesSyncAt > interval) { if (now - subRulesSyncAt > interval) {
// 同步订阅规则 // 同步订阅规则
await syncAllSubRules(subrulesList, isBg); await syncAllSubRules(subrulesList);
await updateSync({ subRulesSyncAt: now }); await updateSync({ subRulesSyncAt: now });
// 同步修复规则 // 同步修复规则

View File

@@ -26,117 +26,90 @@ getPatcher().patch("request", (opts) => {
}); });
}); });
const syncByWebdav = async ({ const syncByWebdav = async (data, { syncUrl, syncUser, syncKey }) => {
key,
value,
syncUrl,
syncUser,
syncKey,
updateAt = 0,
syncAt = 0,
isForce = false,
}) => {
const client = createClient(syncUrl, { const client = createClient(syncUrl, {
username: syncUser, username: syncUser,
password: syncKey, password: syncKey,
}); });
const pathname = `/${APP_LCNAME}`; const pathname = `/${APP_LCNAME}`;
const filename = `/${APP_LCNAME}/${key}`; const filename = `/${APP_LCNAME}/${data.key}`;
const data = JSON.stringify(value, null, " ");
if ((await client.exists(pathname)) === false) { if ((await client.exists(pathname)) === false) {
await client.createDirectory(pathname); await client.createDirectory(pathname);
} }
const isExist = await client.exists(filename); const isExist = await client.exists(filename);
if (isExist && !isForce) { if (isExist) {
const { lastmod } = await client.stat(filename); const cont = await client.getFileContents(filename, { format: "text" });
const fileUpdateAt = Date.parse(lastmod); const webData = JSON.parse(cont);
if (syncAt === 0 || fileUpdateAt > updateAt) { if (webData.updateAt >= data.updateAt) {
const data = await client.getFileContents(filename, { format: "text" }); return webData;
return { updateAt: fileUpdateAt, value: JSON.parse(data) };
} }
} }
await client.putFileContents(filename, data); await client.putFileContents(filename, JSON.stringify(data, null, " "));
const { lastmod } = await client.stat(filename); return data;
const fileUpdateAt = Date.parse(lastmod);
return { updateAt: fileUpdateAt, value };
}; };
const syncByWorker = async ({ const syncByWorker = async (data, { syncUrl, syncKey }) => {
key, return await apiSyncData(`${syncUrl}/sync`, syncKey, data);
value, };
syncUrl,
syncKey, const syncData = async (key, valueFn) => {
updateAt = 0, const {
syncAt = 0, syncType,
isBg = false, syncUrl,
isForce = false, syncUser,
}) => {
if (isForce) {
updateAt = Date.now();
}
return await apiSyncData(
`${syncUrl}/sync`,
syncKey, syncKey,
{ syncMeta = {},
key, } = await getSyncWithDefault();
value, if (!syncUrl || !syncKey || (syncType === OPT_SYNCTYPE_WEBDAV && !syncUser)) {
updateAt: syncAt === 0 ? 0 : updateAt, throw new Error("sync setting err");
}, }
isBg
); let { updateAt = 0, syncAt = 0 } = syncMeta[key] || {};
syncAt === 0 && (updateAt = 0);
const value = await valueFn();
const data = {
key,
value: JSON.stringify(value),
updateAt,
};
const args = {
syncUrl,
syncUser,
syncKey,
};
const res =
syncType === OPT_SYNCTYPE_WEBDAV
? await syncByWebdav(data, args)
: await syncByWorker(data, args);
syncMeta[key] = {
updateAt: res.updateAt,
syncAt: Date.now(),
};
await updateSync({ syncMeta });
return [JSON.parse(res.value), res.updateAt > updateAt];
}; };
/** /**
* 同步设置 * 同步设置
* @returns * @returns
*/ */
const syncSetting = async (isBg = false, isForce = false) => { const syncSetting = async () => {
const { const [value, isNew] = await syncData(KV_SETTING_KEY, getSettingWithDefault);
syncType, if (isNew) {
syncUrl, await setSetting(value);
syncUser,
syncKey,
settingUpdateAt = 0,
settingSyncAt = 0,
} = await getSyncWithDefault();
if (!syncUrl || !syncKey || (syncType === OPT_SYNCTYPE_WEBDAV && !syncUser)) {
return;
} }
const setting = await getSettingWithDefault();
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);
}
await updateSync({
settingUpdateAt: res.updateAt,
settingSyncAt: Date.now(),
});
return res.value;
}; };
export const trySyncSetting = async (isBg = false, isForce = false) => { export const trySyncSetting = async () => {
try { try {
return await syncSetting(isBg, isForce); await syncSetting();
} catch (err) { } catch (err) {
console.log("[sync setting]", err); console.log("[sync setting]", err);
} }
@@ -146,50 +119,16 @@ export const trySyncSetting = async (isBg = false, isForce = false) => {
* 同步规则 * 同步规则
* @returns * @returns
*/ */
const syncRules = async (isBg = false, isForce = false) => { const syncRules = async () => {
const { const [value, isNew] = await syncData(KV_RULES_KEY, getRulesWithDefault);
syncType, if (isNew) {
syncUrl, await setRules(value);
syncUser,
syncKey,
rulesUpdateAt = 0,
rulesSyncAt = 0,
} = await getSyncWithDefault();
if (!syncUrl || !syncKey || (syncType === OPT_SYNCTYPE_WEBDAV && !syncUser)) {
return;
} }
const rules = await getRulesWithDefault();
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);
}
await updateSync({
rulesUpdateAt: res.updateAt,
rulesSyncAt: Date.now(),
});
return res.value;
}; };
export const trySyncRules = async (isBg = false, isForce = false) => { export const trySyncRules = async () => {
try { try {
return await syncRules(isBg, isForce); await syncRules();
} catch (err) { } catch (err) {
console.log("[sync user rules]", err); console.log("[sync user rules]", err);
} }
@@ -219,10 +158,12 @@ export const syncShareRules = async ({ rules, syncUrl, syncKey }) => {
* 同步个人设置和规则 * 同步个人设置和规则
* @returns * @returns
*/ */
export const syncSettingAndRules = async (isBg = false) => { export const syncSettingAndRules = async () => {
return [await syncSetting(isBg), await syncRules(isBg)]; await syncSetting();
await syncRules();
}; };
export const trySyncSettingAndRules = async (isBg = false) => { export const trySyncSettingAndRules = async () => {
return [await trySyncSetting(isBg), await trySyncRules(isBg)]; await trySyncSetting();
await trySyncRules();
}; };