From 489bc9534b7f5361e81193d87d33c6befc5bfc5c Mon Sep 17 00:00:00 2001 From: Gabe Yuan Date: Wed, 20 Sep 2023 17:47:23 +0800 Subject: [PATCH] fix sync --- src/apis/index.js | 6 +- src/background.js | 4 +- src/config/index.js | 9 +- src/hooks/Rules.js | 9 +- src/hooks/Setting.js | 9 +- src/hooks/Sync.js | 17 ++++ src/libs/fetch.js | 5 +- src/libs/rules.js | 2 +- src/libs/subRules.js | 12 +-- src/libs/sync.js | 197 +++++++++++++++---------------------------- 10 files changed, 117 insertions(+), 153 deletions(-) diff --git a/src/apis/index.js b/src/apis/index.js index 55ac733..41a4b0b 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -21,7 +21,7 @@ import { sha256 } from "../libs/utils"; * @param {*} data * @returns */ -export const apiSyncData = async (url, key, data, isBg = false) => +export const apiSyncData = async (url, key, data) => fetchPolyfill(url, { headers: { "Content-type": "application/json", @@ -29,16 +29,14 @@ export const apiSyncData = async (url, key, data, isBg = false) => }, method: "POST", body: JSON.stringify(data), - isBg, }); /** * 下载数据 * @param {*} url - * @param {*} isBg * @returns */ -export const apiFetch = (url, isBg = false) => fetchPolyfill(url, { isBg }); +export const apiFetch = (url) => fetchPolyfill(url); /** * 谷歌翻译 diff --git a/src/background.js b/src/background.js index 6f1d7ae..9915457 100644 --- a/src/background.js +++ b/src/background.js @@ -32,7 +32,7 @@ browser.runtime.onStartup.addListener(async () => { console.log("browser onStartup"); // 同步数据 - await trySyncSettingAndRules(true); + await trySyncSettingAndRules(); // 清除缓存 const setting = await getSettingWithDefault(); @@ -41,7 +41,7 @@ browser.runtime.onStartup.addListener(async () => { } // 同步订阅规则 - trySyncAllSubRules(setting, true); + trySyncAllSubRules(setting); }); /** diff --git a/src/config/index.js b/src/config/index.js index 0edd8e7..cd66c27 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -302,10 +302,11 @@ export const DEFAULT_SYNC = { syncUrl: "", // 数据同步接口 syncUser: "", // 数据同步用户名 syncKey: "", // 数据同步密钥 - settingUpdateAt: 0, - settingSyncAt: 0, - rulesUpdateAt: 0, - rulesSyncAt: 0, + syncMeta: {}, // 数据更新及同步信息 + // settingUpdateAt: 0, + // settingSyncAt: 0, + // rulesUpdateAt: 0, + // rulesSyncAt: 0, subRulesSyncAt: 0, // 订阅规则同步时间 dataCaches: {}, // 缓存同步时间 }; diff --git a/src/hooks/Rules.js b/src/hooks/Rules.js index 6f3c653..ca914db 100644 --- a/src/hooks/Rules.js +++ b/src/hooks/Rules.js @@ -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 { trySyncRules } from "../libs/sync"; import { checkRules } from "../libs/rules"; import { useCallback } from "react"; +import { useSyncMeta } from "./Sync"; /** * 规则 hook @@ -10,13 +11,15 @@ import { useCallback } from "react"; */ export function useRules() { const { data: list, save } = useStorage(STOKEY_RULES, DEFAULT_RULES); + const { updateSyncMeta } = useSyncMeta(); const updateRules = useCallback( async (rules) => { await save(rules); - trySyncRules(false, true); + await updateSyncMeta(KV_RULES_KEY); + trySyncRules(); }, - [save] + [save, updateSyncMeta] ); const add = useCallback( diff --git a/src/hooks/Setting.js b/src/hooks/Setting.js index 71e7e68..8167a42 100644 --- a/src/hooks/Setting.js +++ b/src/hooks/Setting.js @@ -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 { trySyncSetting } from "../libs/sync"; import { createContext, useCallback, useContext, useMemo } from "react"; import { debounce } from "../libs/utils"; +import { useSyncMeta } from "./Sync"; const SettingContext = createContext({ setting: null, @@ -12,11 +13,12 @@ const SettingContext = createContext({ export function SettingProvider({ children }) { const { data, update, reload } = useStorage(STOKEY_SETTING, DEFAULT_SETTING); + const { updateSyncMeta } = useSyncMeta(); const syncSetting = useMemo( () => debounce(() => { - trySyncSetting(false, true); + trySyncSetting(); }, [2000]), [] ); @@ -24,9 +26,10 @@ export function SettingProvider({ children }) { const updateSetting = useCallback( async (obj) => { await update(obj); + await updateSyncMeta(KV_SETTING_KEY); syncSetting(); }, - [update, syncSetting] + [update, syncSetting, updateSyncMeta] ); if (!data) { diff --git a/src/hooks/Sync.js b/src/hooks/Sync.js index e805ef3..9fb9a53 100644 --- a/src/hooks/Sync.js +++ b/src/hooks/Sync.js @@ -11,6 +11,23 @@ export function useSync() { 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 * @param {*} url diff --git a/src/libs/fetch.js b/src/libs/fetch.js index 09df8d9..b41b110 100644 --- a/src/libs/fetch.js +++ b/src/libs/fetch.js @@ -13,6 +13,7 @@ import { DEFAULT_FETCH_LIMIT, } from "../config"; import { msAuth } from "./auth"; +import { isBg } from "./browser"; /** * 油猴脚本的请求封装 @@ -176,13 +177,13 @@ export const fetchData = async ( * @param {*} opts * @returns */ -export const fetchPolyfill = async (input, { isBg = false, ...opts } = {}) => { +export const fetchPolyfill = async (input, opts) => { if (!input.trim()) { throw new Error("URL is empty"); } // 插件 - if (isExt && !isBg) { + if (isExt && !isBg()) { const res = await sendBgMsg(MSG_FETCH, { input, opts }); if (res.error) { throw new Error(res.error); diff --git a/src/libs/rules.js b/src/libs/rules.js index 8835d3b..35f98e1 100644 --- a/src/libs/rules.js +++ b/src/libs/rules.js @@ -149,5 +149,5 @@ export const saveRule = async (newRule) => { rules.unshift(newRule); } await setRules(rules); - trySyncRules(false, true); + trySyncRules(); }; diff --git a/src/libs/subRules.js b/src/libs/subRules.js index d8a845d..a7a6039 100644 --- a/src/libs/subRules.js +++ b/src/libs/subRules.js @@ -25,8 +25,8 @@ const updateSyncDataCache = async (url) => { * @param {*} url * @returns */ -export const syncSubRules = async (url, isBg = false) => { - const res = await apiFetch(url, isBg); +export const syncSubRules = async (url) => { + const res = await apiFetch(url); const rules = checkRules(res).filter( ({ pattern }) => !isAllchar(pattern, GLOBAL_KEY) ); @@ -41,10 +41,10 @@ export const syncSubRules = async (url, isBg = false) => { * @param {*} url * @returns */ -export const syncAllSubRules = async (subrulesList, isBg = false) => { +export const syncAllSubRules = async (subrulesList) => { for (let subrules of subrulesList) { try { - await syncSubRules(subrules.url, isBg); + await syncSubRules(subrules.url); await updateSyncDataCache(subrules.url); } catch (err) { console.log(`[sync subrule error]: ${subrules.url}`, err); @@ -57,14 +57,14 @@ export const syncAllSubRules = async (subrulesList, isBg = false) => { * @param {*} url * @returns */ -export const trySyncAllSubRules = async ({ subrulesList }, isBg = false) => { +export const trySyncAllSubRules = async ({ subrulesList }) => { try { const { subRulesSyncAt } = await getSyncWithDefault(); const now = Date.now(); const interval = 24 * 60 * 60 * 1000; // 间隔一天 if (now - subRulesSyncAt > interval) { // 同步订阅规则 - await syncAllSubRules(subrulesList, isBg); + await syncAllSubRules(subrulesList); await updateSync({ subRulesSyncAt: now }); // 同步修复规则 diff --git a/src/libs/sync.js b/src/libs/sync.js index 5cf59d6..5bdf38f 100644 --- a/src/libs/sync.js +++ b/src/libs/sync.js @@ -26,117 +26,90 @@ getPatcher().patch("request", (opts) => { }); }); -const syncByWebdav = async ({ - key, - value, - syncUrl, - syncUser, - syncKey, - updateAt = 0, - syncAt = 0, - isForce = false, -}) => { +const syncByWebdav = async (data, { syncUrl, syncUser, syncKey }) => { const client = createClient(syncUrl, { username: syncUser, password: syncKey, }); const pathname = `/${APP_LCNAME}`; - const filename = `/${APP_LCNAME}/${key}`; - const data = JSON.stringify(value, null, " "); + const filename = `/${APP_LCNAME}/${data.key}`; 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) }; + if (isExist) { + const cont = await client.getFileContents(filename, { format: "text" }); + const webData = JSON.parse(cont); + if (webData.updateAt >= data.updateAt) { + return webData; } } - await client.putFileContents(filename, data); - const { lastmod } = await client.stat(filename); - const fileUpdateAt = Date.parse(lastmod); - return { updateAt: fileUpdateAt, value }; + await client.putFileContents(filename, JSON.stringify(data, null, " ")); + return data; }; -const syncByWorker = async ({ - key, - value, - syncUrl, - syncKey, - updateAt = 0, - syncAt = 0, - isBg = false, - isForce = false, -}) => { - if (isForce) { - updateAt = Date.now(); - } - return await apiSyncData( - `${syncUrl}/sync`, +const syncByWorker = async (data, { syncUrl, syncKey }) => { + return await apiSyncData(`${syncUrl}/sync`, syncKey, data); +}; + +const syncData = async (key, valueFn) => { + const { + syncType, + syncUrl, + syncUser, syncKey, - { - key, - value, - updateAt: syncAt === 0 ? 0 : updateAt, - }, - isBg - ); + syncMeta = {}, + } = await getSyncWithDefault(); + if (!syncUrl || !syncKey || (syncType === OPT_SYNCTYPE_WEBDAV && !syncUser)) { + throw new Error("sync setting err"); + } + + 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 */ -const syncSetting = async (isBg = false, isForce = false) => { - const { - syncType, - syncUrl, - syncUser, - syncKey, - settingUpdateAt = 0, - settingSyncAt = 0, - } = await getSyncWithDefault(); - if (!syncUrl || !syncKey || (syncType === OPT_SYNCTYPE_WEBDAV && !syncUser)) { - return; +const syncSetting = async () => { + const [value, isNew] = await syncData(KV_SETTING_KEY, getSettingWithDefault); + if (isNew) { + await setSetting(value); } - - 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 { - return await syncSetting(isBg, isForce); + await syncSetting(); } catch (err) { console.log("[sync setting]", err); } @@ -146,50 +119,16 @@ export const trySyncSetting = async (isBg = false, isForce = false) => { * 同步规则 * @returns */ -const syncRules = async (isBg = false, isForce = false) => { - const { - syncType, - syncUrl, - syncUser, - syncKey, - rulesUpdateAt = 0, - rulesSyncAt = 0, - } = await getSyncWithDefault(); - if (!syncUrl || !syncKey || (syncType === OPT_SYNCTYPE_WEBDAV && !syncUser)) { - return; +const syncRules = async () => { + const [value, isNew] = await syncData(KV_RULES_KEY, getRulesWithDefault); + if (isNew) { + await setRules(value); } - - 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 { - return await syncRules(isBg, isForce); + await syncRules(); } catch (err) { console.log("[sync user rules]", err); } @@ -219,10 +158,12 @@ export const syncShareRules = async ({ rules, syncUrl, syncKey }) => { * 同步个人设置和规则 * @returns */ -export const syncSettingAndRules = async (isBg = false) => { - return [await syncSetting(isBg), await syncRules(isBg)]; +export const syncSettingAndRules = async () => { + await syncSetting(); + await syncRules(); }; -export const trySyncSettingAndRules = async (isBg = false) => { - return [await trySyncSetting(isBg), await trySyncRules(isBg)]; +export const trySyncSettingAndRules = async () => { + await trySyncSetting(); + await trySyncRules(); };