diff --git a/README.en.md b/README.en.md
index 5cf1fee..2363c88 100644
--- a/README.en.md
+++ b/README.en.md
@@ -33,6 +33,7 @@ If you also like a little more simplicity, welcome to pick it up.
- [ ] DeepL
- [ ] Upload to app Store
- [x] Open source
+- [x] Data Synchronization Function
### Guide
@@ -42,3 +43,7 @@ cd kiss-translator
yarn
yarn dist
```
+
+### Data Sync
+
+Goto: [https://github.com/fishjar/kiss-worker](https://github.com/fishjar/kiss-worker)
diff --git a/README.md b/README.md
index f3d6d3e..41d3cd4 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@
- [ ] DeepL
- [ ] 上架应用市场
- [x] 开放源代码
+- [x] 数据同步功能
### 指引
@@ -42,3 +43,7 @@ cd kiss-translator
yarn
yarn dist
```
+
+### 数据同步
+
+移步: [https://github.com/fishjar/kiss-worker](https://github.com/fishjar/kiss-worker)
diff --git a/src/apis/data.js b/src/apis/data.js
deleted file mode 100644
index 60ecd29..0000000
--- a/src/apis/data.js
+++ /dev/null
@@ -1,79 +0,0 @@
-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);
-};
diff --git a/src/apis/index.js b/src/apis/index.js
index c65e8f2..196c3db 100644
--- a/src/apis/index.js
+++ b/src/apis/index.js
@@ -8,9 +8,27 @@ import {
OPT_LANGS_SPECIAL,
PROMPT_PLACE_FROM,
PROMPT_PLACE_TO,
+ KV_HEADER_KEY,
} from "../config";
import { getSetting, detectLang } from "../libs";
+/**
+ * 同步数据
+ * @param {*} url
+ * @param {*} key
+ * @param {*} data
+ * @returns
+ */
+export const apiSyncData = async (url, key, data) =>
+ fetchPolyfill(url, {
+ headers: {
+ "Content-type": "application/json",
+ [KV_HEADER_KEY]: key,
+ },
+ method: "POST",
+ body: JSON.stringify(data),
+ });
+
/**
* 谷歌翻译
* @param {*} text
diff --git a/src/background.js b/src/background.js
index 73958d8..3754a24 100644
--- a/src/background.js
+++ b/src/background.js
@@ -4,14 +4,16 @@ import {
MSG_FETCH_LIMIT,
DEFAULT_SETTING,
DEFAULT_RULES,
+ DEFAULT_SYNC,
STOKEY_SETTING,
STOKEY_RULES,
+ STOKEY_SYNC,
CACHE_NAME,
} from "./config";
import { fetchData, setFetchLimit } from "./libs/fetch";
import storage from "./libs/storage";
import { getSetting } from "./libs";
-import { apiSyncAll } from "./apis/data";
+import { syncAll } from "./libs/sync";
/**
* 插件安装
@@ -20,6 +22,7 @@ browser.runtime.onInstalled.addListener(() => {
console.log("onInstalled");
storage.trySetObj(STOKEY_SETTING, DEFAULT_SETTING);
storage.trySetObj(STOKEY_RULES, DEFAULT_RULES);
+ storage.trySetObj(STOKEY_SYNC, DEFAULT_SYNC);
});
/**
@@ -29,11 +32,7 @@ browser.runtime.onStartup.addListener(async () => {
console.log("onStartup");
// 同步数据
- try {
- await apiSyncAll();
- } catch (err) {
- console.log("[sync all]", err);
- }
+ await syncAll();
// 清除缓存
const { clearCache } = await getSetting();
diff --git a/src/config/i18n.js b/src/config/i18n.js
index 0d0de2b..0e66ce6 100644
--- a/src/config/i18n.js
+++ b/src/config/i18n.js
@@ -22,6 +22,10 @@ export const I18N = {
zh: `规则设置`,
en: `Rules Setting`,
},
+ sync_setting: {
+ zh: `同步设置`,
+ en: `Sync Setting`,
+ },
about: {
zh: `关于`,
en: `About`,
@@ -94,9 +98,13 @@ export const I18N = {
zh: `添加`,
en: `Add`,
},
- advanced_warn: {
- zh: `如不明白,谨慎修改!不同的浏览器,选择器规则不一定通用。`,
- en: `If you don't understand, modify it carefully! Different browsers, the selector rules are not necessarily universal.`,
+ sync_warn: {
+ zh: `数据同步功能只是按修改时间简单覆盖,谨慎使用!`,
+ en: `The data synchronization function is simply overwritten according to the modification time, use it with caution!`,
+ },
+ about_sync_api: {
+ zh: `查看关于数据同步接口部署`,
+ en: `View About Data Synchronization Interface Deployment`,
},
style_none: {
zh: `无`,
@@ -139,8 +147,8 @@ export const I18N = {
en: `Multiple URLs can be separated by English commas ","`,
},
selector_helper: {
- zh: `遵循CSS选择器规则,但不同浏览器,可能支持不同,有些不同的写法。`,
- en: `Follow the CSS selector rules, but different browsers may support different, and some have different ways of writing.`,
+ zh: `遵循CSS选择器规则,但不同浏览器,有些不同的写法。`,
+ en: `Follow the CSS selector rules, but different browsers have some different ways of writing.`,
},
translate_switch: {
zh: `开启翻译`,
diff --git a/src/config/index.js b/src/config/index.js
index c0032d2..1636db9 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -8,7 +8,7 @@ export const APP_LCNAME = APP_NAME.toLowerCase();
export const STOKEY_MSAUTH = `${APP_NAME}_msauth`;
export const STOKEY_SETTING = `${APP_NAME}_setting`;
export const STOKEY_RULES = `${APP_NAME}_rules`;
-export const STOKEY_RULES_UPDATE_AT = `${APP_NAME}_rules_update_at`;
+export const STOKEY_SYNC = `${APP_NAME}_sync`;
export const KV_HEADER_KEY = "X-KISS-PSK";
export const KV_RULES_KEY = "KT_RULES";
@@ -26,6 +26,7 @@ export const THEME_LIGHT = "light";
export const THEME_DARK = "dark";
export const URL_APP_HOMEPAGE = "https://github.com/fishjar/kiss-translator";
+export const URL_KISS_WORKER = "https://github.com/fishjar/kiss-worker";
export const URL_RAW_PREFIX =
"https://raw.githubusercontent.com/fishjar/kiss-translator/master";
export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth";
@@ -135,9 +136,6 @@ export const DEFAULT_SETTING = {
openaiKey: "",
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}.`,
- syncUrl: "", // 数据同步接口
- syncKey: "", // 数据同步密钥
- updateAt: 1, // 更新时间
};
export const DEFAULT_RULES = [
@@ -151,3 +149,12 @@ export const DEFAULT_RULES = [
export const TRANS_MIN_LENGTH = 5; // 最短翻译长度
export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度
+
+export const DEFAULT_SYNC = {
+ syncUrl: "", // 数据同步接口
+ syncKey: "", // 数据同步密钥
+ settingUpdateAt: 0,
+ settingSyncAt: 0,
+ rulesUpdateAt: 0,
+ rulesSyncAt: 0,
+};
diff --git a/src/hooks/Rules.js b/src/hooks/Rules.js
index e0f606b..8fb77da 100644
--- a/src/hooks/Rules.js
+++ b/src/hooks/Rules.js
@@ -4,12 +4,12 @@ import {
OPT_STYLE_ALL,
OPT_LANGS_FROM,
OPT_LANGS_TO,
- STOKEY_RULES_UPDATE_AT,
} from "../config";
import storage from "../libs/storage";
import { useStorages } from "./Storage";
import { matchValue } from "../libs/utils";
-import { apiSyncRules } from "../apis/data";
+import { syncRules } from "../libs/sync";
+import { useSync } from "./Sync";
/**
* 匹配规则增删改查 hook
@@ -18,13 +18,14 @@ import { apiSyncRules } from "../apis/data";
export function useRules() {
const storages = useStorages();
const list = storages?.[STOKEY_RULES] || [];
+ const sync = useSync();
const update = async (rules) => {
- const now = Date.now();
+ const updateAt = sync.opt?.rulesUpdateAt ? Date.now() : 0;
await storage.setObj(STOKEY_RULES, rules);
- await storage.setObj(STOKEY_RULES_UPDATE_AT, now);
+ await sync.update({ rulesUpdateAt: updateAt });
try {
- await apiSyncRules(rules, now);
+ await syncRules();
} catch (err) {
console.log("[sync rules]", err);
}
diff --git a/src/hooks/Setting.js b/src/hooks/Setting.js
index b228832..a5cc8d7 100644
--- a/src/hooks/Setting.js
+++ b/src/hooks/Setting.js
@@ -1,7 +1,7 @@
-import { useCallback } from "react";
import { STOKEY_SETTING } from "../config";
import storage from "../libs/storage";
import { useStorages } from "./Storage";
+import { useSync } from "./Sync";
/**
* 设置hook
@@ -17,7 +17,10 @@ export function useSetting() {
* @returns
*/
export function useSettingUpdate() {
- return useCallback(async (obj) => {
- await storage.putObj(STOKEY_SETTING, { ...obj, updateAt: Date.now() });
- }, []);
+ const sync = useSync();
+ return async (obj) => {
+ const updateAt = sync.opt?.settingUpdateAt ? Date.now() : 0;
+ await storage.putObj(STOKEY_SETTING, obj);
+ await sync.update({ settingUpdateAt: updateAt });
+ };
}
diff --git a/src/hooks/Storage.js b/src/hooks/Storage.js
index e4dcf10..78c8beb 100644
--- a/src/hooks/Storage.js
+++ b/src/hooks/Storage.js
@@ -4,9 +4,10 @@ import {
STOKEY_SETTING,
STOKEY_RULES,
STOKEY_MSAUTH,
+ STOKEY_SYNC,
DEFAULT_SETTING,
DEFAULT_RULES,
- STOKEY_RULES_UPDATE_AT,
+ DEFAULT_SYNC,
} from "../config";
import storage from "../libs/storage";
@@ -17,7 +18,7 @@ export const defaultStorage = {
[STOKEY_MSAUTH]: null,
[STOKEY_SETTING]: DEFAULT_SETTING,
[STOKEY_RULES]: DEFAULT_RULES,
- [STOKEY_RULES_UPDATE_AT]: 1,
+ [STOKEY_SYNC]: DEFAULT_SYNC,
};
const StoragesContext = createContext(null);
diff --git a/src/hooks/Sync.js b/src/hooks/Sync.js
new file mode 100644
index 0000000..c74aa1e
--- /dev/null
+++ b/src/hooks/Sync.js
@@ -0,0 +1,20 @@
+import { useCallback } from "react";
+import { STOKEY_SYNC } from "../config";
+import storage from "../libs/storage";
+import { useStorages } from "./Storage";
+
+/**
+ * sync hook
+ * @returns
+ */
+export function useSync() {
+ const storages = useStorages();
+ const opt = storages?.[STOKEY_SYNC];
+ const update = useCallback(async (obj) => {
+ await storage.putObj(STOKEY_SYNC, obj);
+ }, []);
+ return {
+ opt,
+ update,
+ };
+}
diff --git a/src/libs/sync.js b/src/libs/sync.js
new file mode 100644
index 0000000..38d590e
--- /dev/null
+++ b/src/libs/sync.js
@@ -0,0 +1,74 @@
+import {
+ STOKEY_SYNC,
+ DEFAULT_SYNC,
+ KV_SETTING_KEY,
+ KV_RULES_KEY,
+ STOKEY_SETTING,
+ STOKEY_RULES,
+} from "../config";
+import storage from "../libs/storage";
+import { getSetting, getRules } from ".";
+import { apiSyncData } from "../apis";
+
+const loadOpt = async () => (await storage.getObj(STOKEY_SYNC)) || DEFAULT_SYNC;
+
+export const syncSetting = async () => {
+ const { syncUrl, syncKey, settingUpdateAt } = await loadOpt();
+ if (!syncUrl || !syncKey) {
+ return;
+ }
+
+ const setting = await getSetting();
+ const res = await apiSyncData(syncUrl, syncKey, {
+ key: KV_SETTING_KEY,
+ value: setting,
+ updateAt: settingUpdateAt,
+ });
+
+ if (res && res.updateAt > settingUpdateAt) {
+ await storage.putObj(STOKEY_SYNC, {
+ settingUpdateAt: res.updateAt,
+ settingSyncAt: res.updateAt,
+ });
+ await storage.setObj(STOKEY_SETTING, res.value);
+ } else {
+ await storage.putObj(STOKEY_SYNC, {
+ settingSyncAt: res.updateAt,
+ });
+ }
+};
+
+export const syncRules = async () => {
+ const { syncUrl, syncKey, rulesUpdateAt } = await loadOpt();
+ if (!syncUrl || !syncKey) {
+ return;
+ }
+
+ const rules = await getRules();
+ const res = await apiSyncData(syncUrl, syncKey, {
+ key: KV_RULES_KEY,
+ value: rules,
+ updateAt: rulesUpdateAt,
+ });
+
+ if (res && res.updateAt > rulesUpdateAt) {
+ await storage.putObj(STOKEY_SYNC, {
+ rulesUpdateAt: res.updateAt,
+ rulesSyncAt: res.updateAt,
+ });
+ await storage.setObj(STOKEY_RULES, res.value);
+ } else {
+ await storage.putObj(STOKEY_SYNC, {
+ rulesSyncAt: res.updateAt,
+ });
+ }
+};
+
+export const syncAll = async () => {
+ try {
+ await syncSetting();
+ await syncRules();
+ } catch (err) {
+ console.log("[sync all]", err);
+ }
+};
diff --git a/src/views/Options/Layout.js b/src/views/Options/Layout.js
index b811f47..8a98419 100644
--- a/src/views/Options/Layout.js
+++ b/src/views/Options/Layout.js
@@ -6,6 +6,7 @@ import Box from "@mui/material/Box";
import Navigator from "./Navigator";
import Header from "./Header";
import { useTheme } from "@mui/material/styles";
+import { syncAll } from "../../libs/sync";
export default function Layout() {
const navWidth = 256;
@@ -20,6 +21,7 @@ export default function Layout() {
useEffect(() => {
setOpen(false);
+ syncAll();
}, [location]);
return (
diff --git a/src/views/Options/Navigator.js b/src/views/Options/Navigator.js
index b651ef9..e4b1b88 100644
--- a/src/views/Options/Navigator.js
+++ b/src/views/Options/Navigator.js
@@ -9,6 +9,7 @@ import SettingsIcon from "@mui/icons-material/Settings";
import InfoIcon from "@mui/icons-material/Info";
import DesignServicesIcon from "@mui/icons-material/DesignServices";
import { useI18n } from "../../hooks/I18n";
+import SyncIcon from "@mui/icons-material/Sync";
function LinkItem({ label, url, icon }) {
const match = useMatch(url);
@@ -35,6 +36,12 @@ export default function Navigator(props) {
url: "/rules",
icon: ,
},
+ {
+ id: "sync",
+ label: i18n("sync_setting"),
+ url: "/sync",
+ icon: ,
+ },
{ id: "about", label: i18n("about"), url: "/about", icon: },
];
return (
diff --git a/src/views/Options/Rules.js b/src/views/Options/Rules.js
index 2408c60..376438b 100644
--- a/src/views/Options/Rules.js
+++ b/src/views/Options/Rules.js
@@ -10,7 +10,6 @@ import {
OPT_STYLE_ALL,
} from "../../config";
import { useState, useRef } from "react";
-import Alert from "@mui/material/Alert";
import { useI18n } from "../../hooks/I18n";
import Typography from "@mui/material/Typography";
import Accordion from "@mui/material/Accordion";
@@ -354,8 +353,6 @@ export default function Rules() {
return (
- {i18n("advanced_warn")}
-
);
diff --git a/src/views/Options/SyncSetting.js b/src/views/Options/SyncSetting.js
new file mode 100644
index 0000000..9805a89
--- /dev/null
+++ b/src/views/Options/SyncSetting.js
@@ -0,0 +1,59 @@
+import Box from "@mui/material/Box";
+import Stack from "@mui/material/Stack";
+import TextField from "@mui/material/TextField";
+import { useI18n } from "../../hooks/I18n";
+import { useSync } from "../../hooks/Sync";
+import { syncAll } from "../../libs/sync";
+import Alert from "@mui/material/Alert";
+import Link from "@mui/material/Link";
+import { URL_KISS_WORKER } from "../../config";
+
+export default function SyncSetting() {
+ const i18n = useI18n();
+ const sync = useSync();
+
+ if (!sync.opt) {
+ return;
+ }
+
+ const { syncUrl, syncKey } = sync.opt;
+ const handleSyncBlur = () => {
+ syncAll();
+ };
+
+ return (
+
+
+ {i18n("sync_warn")}
+
+ {
+ sync.update({
+ syncUrl: e.target.value,
+ });
+ }}
+ onBlur={handleSyncBlur}
+ helperText={
+ {i18n("about_sync_api")}
+ }
+ />
+
+ {
+ sync.update({
+ syncKey: e.target.value,
+ });
+ }}
+ onBlur={handleSyncBlur}
+ />
+
+
+ );
+}
diff --git a/src/views/Options/index.js b/src/views/Options/index.js
index 4137f56..2e55c18 100644
--- a/src/views/Options/index.js
+++ b/src/views/Options/index.js
@@ -3,6 +3,7 @@ import About from "./About";
import Rules from "./Rules";
import Setting from "./Setting";
import Layout from "./Layout";
+import SyncSetting from "./SyncSetting";
export default function Options() {
return (
@@ -10,6 +11,7 @@ export default function Options() {
}>
} />
} />
+ } />
} />