data sync
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
.yarn
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
|||||||
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
79
src/apis/data.js
Normal file
79
src/apis/data.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { fetchPolyfill } from "../libs/fetch";
|
||||||
|
import {
|
||||||
|
KV_HEADER_KEY,
|
||||||
|
KV_RULES_KEY,
|
||||||
|
KV_SETTING_KEY,
|
||||||
|
STOKEY_RULES,
|
||||||
|
STOKEY_SETTING,
|
||||||
|
STOKEY_RULES_UPDATE_AT,
|
||||||
|
} from "../config";
|
||||||
|
import { getSetting, getRules } from "../libs";
|
||||||
|
import storage from "../libs/storage";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步数据
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const apiSyncData = async ({ key, value, updateAt }) => {
|
||||||
|
const { syncUrl, syncKey } = await getSetting();
|
||||||
|
if (!syncUrl || !syncKey) {
|
||||||
|
console.log("data sync should set the api and key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return fetchPolyfill(syncUrl, {
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
[KV_HEADER_KEY]: syncKey,
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ key, value, updateAt }),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步rules
|
||||||
|
* @param {*} value
|
||||||
|
* @param {*} updateAt
|
||||||
|
*/
|
||||||
|
export const apiSyncRules = async (value, updateAt) => {
|
||||||
|
const res = await apiSyncData({
|
||||||
|
key: KV_RULES_KEY,
|
||||||
|
value,
|
||||||
|
updateAt,
|
||||||
|
});
|
||||||
|
console.log("res", res);
|
||||||
|
if (res && res.updateAt > updateAt) {
|
||||||
|
await storage.setObj(STOKEY_RULES, res.value);
|
||||||
|
await storage.setObj(STOKEY_RULES_UPDATE_AT, res.updateAt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步setting
|
||||||
|
* @param {*} value
|
||||||
|
* @param {*} updateAt
|
||||||
|
*/
|
||||||
|
export const apiSyncSetting = async (value, updateAt) => {
|
||||||
|
const res = await apiSyncData({
|
||||||
|
key: KV_SETTING_KEY,
|
||||||
|
value,
|
||||||
|
updateAt,
|
||||||
|
});
|
||||||
|
console.log("res", res);
|
||||||
|
if (res && res.updateAt > updateAt) {
|
||||||
|
await storage.setObj(STOKEY_SETTING, res.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步全部数据
|
||||||
|
*/
|
||||||
|
export const apiSyncAll = async () => {
|
||||||
|
const setting = await getSetting();
|
||||||
|
const rules = await getRules();
|
||||||
|
const settingUpdateAt = setting.updateAt;
|
||||||
|
const rulesUpdateAt = (await storage.getObj(STOKEY_RULES_UPDATE_AT)) || 1;
|
||||||
|
await apiSyncSetting(setting, settingUpdateAt);
|
||||||
|
await apiSyncRules(rules, rulesUpdateAt);
|
||||||
|
};
|
||||||
@@ -68,10 +68,10 @@ const apiMicrosoftTranslate = (text, to, from) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAI 翻译
|
* OpenAI 翻译
|
||||||
* @param {*} text
|
* @param {*} text
|
||||||
* @param {*} to
|
* @param {*} to
|
||||||
* @param {*} from
|
* @param {*} from
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const apiOpenaiTranslate = async (text, to, from) => {
|
const apiOpenaiTranslate = async (text, to, from) => {
|
||||||
const { openaiUrl, openaiModel, openaiPrompt } = await getSetting();
|
const { openaiUrl, openaiModel, openaiPrompt } = await getSetting();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { fetchData, setFetchLimit } from "./libs/fetch";
|
import { fetchData, setFetchLimit } from "./libs/fetch";
|
||||||
import storage from "./libs/storage";
|
import storage from "./libs/storage";
|
||||||
import { getSetting } from "./libs";
|
import { getSetting } from "./libs";
|
||||||
|
import { apiSyncAll } from "./apis/data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插件安装
|
* 插件安装
|
||||||
@@ -26,6 +27,15 @@ browser.runtime.onInstalled.addListener(() => {
|
|||||||
*/
|
*/
|
||||||
browser.runtime.onStartup.addListener(async () => {
|
browser.runtime.onStartup.addListener(async () => {
|
||||||
console.log("onStartup");
|
console.log("onStartup");
|
||||||
|
|
||||||
|
// 同步数据
|
||||||
|
try {
|
||||||
|
await apiSyncAll();
|
||||||
|
} catch (err) {
|
||||||
|
console.log("[sync all]", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除缓存
|
||||||
const { clearCache } = await getSetting();
|
const { clearCache } = await getSetting();
|
||||||
if (clearCache) {
|
if (clearCache) {
|
||||||
caches.delete(CACHE_NAME);
|
caches.delete(CACHE_NAME);
|
||||||
|
|||||||
@@ -206,4 +206,12 @@ export const I18N = {
|
|||||||
zh: `重启浏览器时清除缓存`,
|
zh: `重启浏览器时清除缓存`,
|
||||||
en: `Clear cache when restarting browser`,
|
en: `Clear cache when restarting browser`,
|
||||||
},
|
},
|
||||||
|
data_sync_url: {
|
||||||
|
zh: `数据同步接口`,
|
||||||
|
en: `Data Sync API`,
|
||||||
|
},
|
||||||
|
data_sync_key: {
|
||||||
|
zh: `数据同步密钥`,
|
||||||
|
en: `Data Sync Key`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ export const APP_LCNAME = APP_NAME.toLowerCase();
|
|||||||
export const STOKEY_MSAUTH = `${APP_NAME}_msauth`;
|
export const STOKEY_MSAUTH = `${APP_NAME}_msauth`;
|
||||||
export const STOKEY_SETTING = `${APP_NAME}_setting`;
|
export const STOKEY_SETTING = `${APP_NAME}_setting`;
|
||||||
export const STOKEY_RULES = `${APP_NAME}_rules`;
|
export const STOKEY_RULES = `${APP_NAME}_rules`;
|
||||||
|
export const STOKEY_RULES_UPDATE_AT = `${APP_NAME}_rules_update_at`;
|
||||||
|
|
||||||
|
export const KV_HEADER_KEY = "X-KISS-PSK";
|
||||||
|
export const KV_RULES_KEY = "KT_RULES";
|
||||||
|
export const KV_SETTING_KEY = "KT_SETTING";
|
||||||
|
|
||||||
export const CACHE_NAME = `${APP_NAME}_cache`;
|
export const CACHE_NAME = `${APP_NAME}_cache`;
|
||||||
|
|
||||||
@@ -130,6 +135,9 @@ export const DEFAULT_SETTING = {
|
|||||||
openaiKey: "",
|
openaiKey: "",
|
||||||
openaiModel: "gpt-4",
|
openaiModel: "gpt-4",
|
||||||
openaiPrompt: `You will be provided with a sentence in ${PROMPT_PLACE_FROM}, and your task is to translate it into ${PROMPT_PLACE_TO}.`,
|
openaiPrompt: `You will be provided with a sentence in ${PROMPT_PLACE_FROM}, and your task is to translate it into ${PROMPT_PLACE_TO}.`,
|
||||||
|
syncUrl: "", // 数据同步接口
|
||||||
|
syncKey: "", // 数据同步密钥
|
||||||
|
updateAt: 1, // 更新时间
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_RULES = [
|
export const DEFAULT_RULES = [
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import {
|
|||||||
OPT_STYLE_ALL,
|
OPT_STYLE_ALL,
|
||||||
OPT_LANGS_FROM,
|
OPT_LANGS_FROM,
|
||||||
OPT_LANGS_TO,
|
OPT_LANGS_TO,
|
||||||
|
STOKEY_RULES_UPDATE_AT,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import storage from "../libs/storage";
|
import storage from "../libs/storage";
|
||||||
import { useStorages } from "./Storage";
|
import { useStorages } from "./Storage";
|
||||||
import { matchValue } from "../libs/utils";
|
import { matchValue } from "../libs/utils";
|
||||||
|
import { apiSyncRules } from "../apis/data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 匹配规则增删改查 hook
|
* 匹配规则增删改查 hook
|
||||||
@@ -17,6 +19,17 @@ export function useRules() {
|
|||||||
const storages = useStorages();
|
const storages = useStorages();
|
||||||
const list = storages?.[STOKEY_RULES] || [];
|
const list = storages?.[STOKEY_RULES] || [];
|
||||||
|
|
||||||
|
const update = async (rules) => {
|
||||||
|
const now = Date.now();
|
||||||
|
await storage.setObj(STOKEY_RULES, rules);
|
||||||
|
await storage.setObj(STOKEY_RULES_UPDATE_AT, now);
|
||||||
|
try {
|
||||||
|
await apiSyncRules(rules, now);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("[sync rules]", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const add = async (rule) => {
|
const add = async (rule) => {
|
||||||
const rules = [...list];
|
const rules = [...list];
|
||||||
if (rule.pattern === "*") {
|
if (rule.pattern === "*") {
|
||||||
@@ -26,7 +39,7 @@ export function useRules() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rules.unshift(rule);
|
rules.unshift(rule);
|
||||||
await storage.setObj(STOKEY_RULES, rules);
|
await update(rules);
|
||||||
};
|
};
|
||||||
|
|
||||||
const del = async (pattern) => {
|
const del = async (pattern) => {
|
||||||
@@ -35,7 +48,7 @@ export function useRules() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rules = rules.filter((item) => item.pattern !== pattern);
|
rules = rules.filter((item) => item.pattern !== pattern);
|
||||||
await storage.setObj(STOKEY_RULES, rules);
|
await update(rules);
|
||||||
};
|
};
|
||||||
|
|
||||||
const put = async (pattern, obj) => {
|
const put = async (pattern, obj) => {
|
||||||
@@ -45,13 +58,13 @@ export function useRules() {
|
|||||||
}
|
}
|
||||||
const rule = rules.find((r) => r.pattern === pattern);
|
const rule = rules.find((r) => r.pattern === pattern);
|
||||||
rule && Object.assign(rule, obj);
|
rule && Object.assign(rule, obj);
|
||||||
await storage.setObj(STOKEY_RULES, rules);
|
await update(rules);
|
||||||
};
|
};
|
||||||
|
|
||||||
const merge = async (newRules) => {
|
const merge = async (newRules) => {
|
||||||
|
const rules = [...list];
|
||||||
const fromLangs = OPT_LANGS_FROM.map((item) => item[0]);
|
const fromLangs = OPT_LANGS_FROM.map((item) => item[0]);
|
||||||
const toLangs = OPT_LANGS_TO.map((item) => item[0]);
|
const toLangs = OPT_LANGS_TO.map((item) => item[0]);
|
||||||
const rules = [...list];
|
|
||||||
newRules
|
newRules
|
||||||
.filter(
|
.filter(
|
||||||
({ pattern, selector }) =>
|
({ pattern, selector }) =>
|
||||||
@@ -89,7 +102,7 @@ export function useRules() {
|
|||||||
rules.unshift(newRule);
|
rules.unshift(newRule);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await storage.setObj(STOKEY_RULES, rules);
|
await update(rules);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { list, add, del, put, merge };
|
return { list, add, del, put, merge };
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
import { STOKEY_SETTING } from "../config";
|
import { STOKEY_SETTING } from "../config";
|
||||||
import storage from "../libs/storage";
|
import storage from "../libs/storage";
|
||||||
import { useStorages } from "./Storage";
|
import { useStorages } from "./Storage";
|
||||||
@@ -16,7 +17,7 @@ export function useSetting() {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function useSettingUpdate() {
|
export function useSettingUpdate() {
|
||||||
return async (obj) => {
|
return useCallback(async (obj) => {
|
||||||
await storage.putObj(STOKEY_SETTING, obj);
|
await storage.putObj(STOKEY_SETTING, { ...obj, updateAt: Date.now() });
|
||||||
};
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
STOKEY_MSAUTH,
|
STOKEY_MSAUTH,
|
||||||
DEFAULT_SETTING,
|
DEFAULT_SETTING,
|
||||||
DEFAULT_RULES,
|
DEFAULT_RULES,
|
||||||
|
STOKEY_RULES_UPDATE_AT,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import storage from "../libs/storage";
|
import storage from "../libs/storage";
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ export const defaultStorage = {
|
|||||||
[STOKEY_MSAUTH]: null,
|
[STOKEY_MSAUTH]: null,
|
||||||
[STOKEY_SETTING]: DEFAULT_SETTING,
|
[STOKEY_SETTING]: DEFAULT_SETTING,
|
||||||
[STOKEY_RULES]: DEFAULT_RULES,
|
[STOKEY_RULES]: DEFAULT_RULES,
|
||||||
|
[STOKEY_RULES_UPDATE_AT]: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const StoragesContext = createContext(null);
|
const StoragesContext = createContext(null);
|
||||||
|
|||||||
@@ -9,12 +9,22 @@ import { useSetting, useSettingUpdate } from "../../hooks/Setting";
|
|||||||
import { limitNumber } from "../../libs/utils";
|
import { limitNumber } from "../../libs/utils";
|
||||||
import { useI18n } from "../../hooks/I18n";
|
import { useI18n } from "../../hooks/I18n";
|
||||||
import { UI_LANGS } from "../../config";
|
import { UI_LANGS } from "../../config";
|
||||||
|
import { apiSyncAll } from "../../apis/data";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const setting = useSetting();
|
const setting = useSetting();
|
||||||
const updateSetting = useSettingUpdate();
|
const updateSetting = useSettingUpdate();
|
||||||
|
|
||||||
|
const handleSyncBlur = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
await apiSyncAll();
|
||||||
|
} catch (err) {
|
||||||
|
console.log("sync data", err);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!setting) {
|
if (!setting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -28,6 +38,8 @@ export default function Settings() {
|
|||||||
openaiModel,
|
openaiModel,
|
||||||
openaiPrompt,
|
openaiPrompt,
|
||||||
clearCache,
|
clearCache,
|
||||||
|
syncUrl,
|
||||||
|
syncKey,
|
||||||
} = setting;
|
} = setting;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -102,6 +114,7 @@ export default function Settings() {
|
|||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
size="small"
|
size="small"
|
||||||
|
type="password"
|
||||||
label={i18n("openai_key")}
|
label={i18n("openai_key")}
|
||||||
defaultValue={openaiKey}
|
defaultValue={openaiKey}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -135,6 +148,31 @@ export default function Settings() {
|
|||||||
minRows={2}
|
minRows={2}
|
||||||
maxRows={10}
|
maxRows={10}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
size="small"
|
||||||
|
label={i18n("data_sync_url")}
|
||||||
|
defaultValue={syncUrl}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateSetting({
|
||||||
|
syncUrl: e.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onBlur={handleSyncBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
size="small"
|
||||||
|
type="password"
|
||||||
|
label={i18n("data_sync_key")}
|
||||||
|
defaultValue={syncKey}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateSetting({
|
||||||
|
syncKey: e.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onBlur={handleSyncBlur}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user