diff --git a/src/apis/baidu.js b/src/apis/baidu.js index 467ec17..88c0b9e 100644 --- a/src/apis/baidu.js +++ b/src/apis/baidu.js @@ -1,243 +1,5 @@ import queryString from "query-string"; -import { getBdauth, setBdauth } from "../libs/storage"; -import { - URL_BAIDU_WEB, - URL_BAIDU_TRANSAPI_V2, - URL_BAIDU_TRANSAPI, - DEFAULT_USER_AGENT, -} from "../config"; -import { fetchApi } from "../libs/fetch"; - -/* eslint-disable */ -function n(t, e) { - for (var n = 0; n < e.length - 2; n += 3) { - var r = e.charAt(n + 2); - (r = "a" <= r ? r.charCodeAt(0) - 87 : Number(r)), - (r = "+" === e.charAt(n + 1) ? t >>> r : t << r), - (t = "+" === e.charAt(n) ? (t + r) & 4294967295 : t ^ r); - } - return t; -} - -function e(t, e) { - (null == e || e > t.length) && (e = t.length); - for (var n = 0, r = new Array(e); n < e; n++) r[n] = t[n]; - return r; -} - -/* eslint-disable */ -function getSign(t, gtk, r = null) { - var o, - i = t.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g); - if (null === i) { - var a = t.length; - a > 30 && - (t = "" - .concat(t.substr(0, 10)) - .concat(t.substr(Math.floor(a / 2) - 5, 10)) - .concat(t.substr(-10, 10))); - } else { - for ( - var s = t.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), - c = 0, - u = s.length, - l = []; - c < u; - c++ - ) - "" !== s[c] && - l.push.apply( - l, - (function (t) { - if (Array.isArray(t)) return e(t); - })((o = s[c].split(""))) || - (function (t) { - if ( - ("undefined" != typeof Symbol && null != t[Symbol.iterator]) || - null != t["@@iterator"] - ) - return Array.from(t); - })(o) || - (function (t, n) { - if (t) { - if ("string" == typeof t) return e(t, n); - var r = Object.prototype.toString.call(t).slice(8, -1); - return ( - "Object" === r && t.constructor && (r = t.constructor.name), - "Map" === r || "Set" === r - ? Array.from(t) - : "Arguments" === r || - /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r) - ? e(t, n) - : void 0 - ); - } - })(o) || - (function () { - throw new TypeError( - "Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." - ); - })() - ), - c !== u - 1 && l.push(i[c]); - var p = l.length; - p > 30 && - (t = - l.slice(0, 10).join("") + - l.slice(Math.floor(p / 2) - 5, Math.floor(p / 2) + 5).join("") + - l.slice(-10).join("")); - } - for ( - var d = "" - .concat(String.fromCharCode(103)) - .concat(String.fromCharCode(116)) - .concat(String.fromCharCode(107)), - h = (null !== r ? r : (r = gtk || "") || "").split("."), - f = Number(h[0]) || 0, - m = Number(h[1]) || 0, - g = [], - y = 0, - v = 0; - v < t.length; - v++ - ) { - var _ = t.charCodeAt(v); - _ < 128 - ? (g[y++] = _) - : (_ < 2048 - ? (g[y++] = (_ >> 6) | 192) - : (55296 == (64512 & _) && - v + 1 < t.length && - 56320 == (64512 & t.charCodeAt(v + 1)) - ? ((_ = 65536 + ((1023 & _) << 10) + (1023 & t.charCodeAt(++v))), - (g[y++] = (_ >> 18) | 240), - (g[y++] = ((_ >> 12) & 63) | 128)) - : (g[y++] = (_ >> 12) | 224), - (g[y++] = ((_ >> 6) & 63) | 128)), - (g[y++] = (63 & _) | 128)); - } - for ( - var b = f, - w = - "" - .concat(String.fromCharCode(43)) - .concat(String.fromCharCode(45)) - .concat(String.fromCharCode(97)) + - "" - .concat(String.fromCharCode(94)) - .concat(String.fromCharCode(43)) - .concat(String.fromCharCode(54)), - k = - "" - .concat(String.fromCharCode(43)) - .concat(String.fromCharCode(45)) - .concat(String.fromCharCode(51)) + - "" - .concat(String.fromCharCode(94)) - .concat(String.fromCharCode(43)) - .concat(String.fromCharCode(98)) + - "" - .concat(String.fromCharCode(43)) - .concat(String.fromCharCode(45)) - .concat(String.fromCharCode(102)), - x = 0; - x < g.length; - x++ - ) - b = n((b += g[x]), w); - return ( - (b = n(b, k)), - (b ^= m) < 0 && (b = 2147483648 + (2147483647 & b)), - "".concat((b %= 1e6).toString(), ".").concat(b ^ f) - ); -} - -const getToken = async () => { - const res = await fetchApi({ - input: URL_BAIDU_WEB, - init: { - headers: { - "Content-type": "text/html; charset=utf-8", - }, - }, - }); - - if (!res.ok) { - throw new Error(res.statusText); - } - - const text = await res.text(); - const token = text.match(/token: '(.*)',/)[1]; - const gtk = text.match(/gtk = "(.*)";/)[1]; - const exp = Date.now() + 8 * 60 * 60 * 1000; - - if (!token || !gtk) { - throw new Error("[baidu] get token error"); - } - - return { token, gtk, exp }; -}; - -/** - * 闭包缓存token,减少对storage查询 - * @returns - */ -const _bdAuth = () => { - let store; - - return async () => { - const now = Date.now(); - - // 查询内存缓存 - if (store && store.exp > now) { - return store; - } - - // 查询storage缓存 - store = await getBdauth(); - if (store && store.exp > now) { - return store; - } - - // 缓存没有或失效,查询接口 - store = await getToken(); - await setBdauth(store); - return store; - }; -}; - -const bdAuth = _bdAuth(); - -/** - * 失效作废 - * @param {*} param0 - * @returns - */ -export const genBaiduV2 = async ({ text, from, to }) => { - const { token, gtk } = await bdAuth(); - const sign = getSign(text, gtk); - const data = { - from, - to, - query: text, - simple_means_flag: 3, - sign, - token, - domain: "common", - ts: Date.now(), - }; - - const input = `${URL_BAIDU_TRANSAPI_V2}?from=${from}&to=${to}`; - const init = { - headers: { - "content-type": "application/x-www-form-urlencoded; charset=UTF-8", - }, - method: "POST", - body: queryString.stringify(data), - }; - - return [input, init]; -}; +import { URL_BAIDU_TRANSAPI, DEFAULT_USER_AGENT } from "../config"; export const genBaidu = async ({ text, from, to }) => { const data = { diff --git a/src/apis/index.js b/src/apis/index.js index 85d9573..d436a51 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -1,5 +1,5 @@ import queryString from "query-string"; -import { fetchPolyfill } from "../libs/fetch"; +import { fetchData } from "../libs/fetch"; import { OPT_TRANS_GOOGLE, OPT_TRANS_MICROSOFT, @@ -37,7 +37,7 @@ import { sha256 } from "../libs/utils"; * @returns */ export const apiSyncData = async (url, key, data) => - fetchPolyfill(url, { + fetchData(url, { headers: { "Content-type": "application/json", Authorization: `Bearer ${await sha256(key, KV_SALT_SYNC)}`, @@ -51,7 +51,7 @@ export const apiSyncData = async (url, key, data) => * @param {*} url * @returns */ -export const apiFetch = (url) => fetchPolyfill(url); +export const apiFetch = (url) => fetchData(url); /** * 百度语言识别 @@ -59,7 +59,7 @@ export const apiFetch = (url) => fetchPolyfill(url); * @returns */ export const apiBaiduLangdetect = async (text) => { - const res = await fetchPolyfill(URL_BAIDU_LANGDETECT, { + const res = await fetchData(URL_BAIDU_LANGDETECT, { headers: { "Content-type": "application/json", }, @@ -83,7 +83,7 @@ export const apiBaiduLangdetect = async (text) => { * @returns */ export const apiBaiduSuggest = async (text) => { - const res = await fetchPolyfill(URL_BAIDU_SUGGEST, { + const res = await fetchData(URL_BAIDU_SUGGEST, { headers: { "Content-type": "application/json", }, @@ -110,7 +110,7 @@ export const apiBaiduSuggest = async (text) => { */ export const apiBaiduTTS = (text, lan = "uk", spd = 3) => { const url = `${URL_BAIDU_TTS}?${queryString.stringify({ lan, text, spd })}`; - return fetchPolyfill(url, { + return fetchData(url, { useCache: false, // 为避免缓存过快增长,禁用缓存语音数据 }); }; @@ -128,7 +128,7 @@ export const apiTencentLangdetect = async (text) => { text, }); - const res = await fetchPolyfill(URL_TENCENT_TRANSMART, { + const res = await fetchData(URL_TENCENT_TRANSMART, { headers: { "Content-type": "application/json", }, @@ -187,7 +187,7 @@ export const apiTranslate = async ({ to, }; - const res = await fetchPolyfill( + const res = await fetchData( `${URL_CACHE_TRAN}?${queryString.stringify(cacheOpts)}`, { useCache, diff --git a/src/libs/req.js b/src/apis/trans.js similarity index 92% rename from src/libs/req.js rename to src/apis/trans.js index 54553c1..35d24e9 100644 --- a/src/libs/req.js +++ b/src/apis/trans.js @@ -24,9 +24,9 @@ import { INPUT_PLACE_TEXT, INPUT_PLACE_KEY, } from "../config"; -import { msAuth } from "./auth"; -import { genDeeplFree } from "../apis/deepl"; -import { genBaidu } from "../apis/baidu"; +import { msAuth } from "../libs/auth"; +import { genDeeplFree } from "./deepl"; +import { genBaidu } from "./baidu"; const keyMap = new Map(); const urlMap = new Map(); @@ -49,23 +49,6 @@ const keyPick = (translator, key = "", cacheMap) => { return keys[curIndex]; }; -/** - * 构造缓存 request - * @param {*} request - * @returns - */ -export const newCacheReq = async (input, init) => { - let request = new Request(input, init); - if (request.method !== "GET") { - const body = await request.text(); - const cacheUrl = new URL(request.url); - cacheUrl.pathname += body; - request = new Request(cacheUrl.toString(), { method: "GET" }); - } - - return request; -}; - const genGoogle = ({ text, from, to, url, key }) => { const params = { client: "gtx", @@ -324,11 +307,11 @@ const genCustom = ({ text, from, to, url, key, customOption }) => { }; /** - * 构造翻译接口 request + * 构造翻译接口请求参数 * @param {*} * @returns */ -export const newTransReq = ({ translator, text, from, to }, apiSetting) => { +export const genTransReq = ({ translator, text, from, to }, apiSetting) => { const args = { text, from, to, ...apiSetting }; switch (translator) { diff --git a/src/background.js b/src/background.js index f054f2c..0e62e7f 100644 --- a/src/background.js +++ b/src/background.js @@ -1,8 +1,7 @@ import browser from "webextension-polyfill"; import { MSG_FETCH, - MSG_FETCH_LIMIT, - MSG_FETCH_CLEAR, + MSG_GET_HTTPCACHE, MSG_TRANS_TOGGLE, MSG_OPEN_OPTIONS, MSG_SAVE_RULE, @@ -21,7 +20,7 @@ import { } from "./config"; import { getSettingWithDefault, tryInitDefaultData } from "./libs/storage"; import { trySyncSettingAndRules } from "./libs/sync"; -import { fetchData, fetchPool } from "./libs/fetch"; +import { fetchHandle, getHttpCache } from "./libs/fetch"; import { sendTabMsg } from "./libs/msg"; import { trySyncAllSubRules } from "./libs/subRules"; import { tryClearCaches } from "./libs"; @@ -169,13 +168,10 @@ browser.runtime.onStartup.addListener(async () => { browser.runtime.onMessage.addListener(async ({ action, args }) => { switch (action) { case MSG_FETCH: - const { input, opts } = args; - return await fetchData(input, opts); - case MSG_FETCH_LIMIT: - const { interval, limit } = args; - return fetchPool.update(interval, limit); - case MSG_FETCH_CLEAR: - return fetchPool.clear(); + return await fetchHandle(args); + case MSG_GET_HTTPCACHE: + const { input, init } = args; + return await getHttpCache(input, init); case MSG_OPEN_OPTIONS: return await browser.runtime.openOptionsPage(); case MSG_SAVE_RULE: diff --git a/src/config/index.js b/src/config/index.js index 1001af6..405a77b 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -51,8 +51,7 @@ export const KV_SALT_SHARE = "KISS-Translator-SHARE"; export const CACHE_NAME = `${APP_NAME}_cache`; export const MSG_FETCH = "fetch"; -export const MSG_FETCH_LIMIT = "fetch_limit"; -export const MSG_FETCH_CLEAR = "fetch_clear"; +export const MSG_GET_HTTPCACHE = "get_httpcache"; export const MSG_OPEN_OPTIONS = "open_options"; export const MSG_SAVE_RULE = "save_rule"; export const MSG_TRANS_TOGGLE = "trans_toggle"; diff --git a/src/libs/auth.js b/src/libs/auth.js index 51cfc23..ee7bf64 100644 --- a/src/libs/auth.js +++ b/src/libs/auth.js @@ -1,6 +1,6 @@ import { getMsauth, setMsauth } from "./storage"; import { URL_MICROSOFT_AUTH } from "../config"; -import { fetchData } from "./fetch"; +import { fetchHandle } from "./fetch"; import { kissLog } from "./log"; const parseMSToken = (token) => { @@ -35,7 +35,7 @@ const _msAuth = () => { } // 缓存没有或失效,查询接口 - token = await fetchData(URL_MICROSOFT_AUTH); + token = await fetchHandle({ input: URL_MICROSOFT_AUTH }); exp = parseMSToken(token); await setMsauth({ token, exp }); return [token, exp]; diff --git a/src/libs/fetch.js b/src/libs/fetch.js index 5465673..148be3f 100644 --- a/src/libs/fetch.js +++ b/src/libs/fetch.js @@ -3,19 +3,36 @@ import { sendBgMsg } from "./msg"; import { taskPool } from "./pool"; import { MSG_FETCH, - MSG_FETCH_LIMIT, - MSG_FETCH_CLEAR, + MSG_GET_HTTPCACHE, CACHE_NAME, DEFAULT_FETCH_INTERVAL, DEFAULT_FETCH_LIMIT, } from "../config"; import { isBg } from "./browser"; -import { newCacheReq, newTransReq } from "./req"; +import { genTransReq } from "../apis/trans"; import { kissLog } from "./log"; import { blobToBase64 } from "./utils"; const TIMEOUT = 5000; +/** + * 构造缓存 request + * @param {*} input + * @param {*} init + * @returns + */ +const newCacheReq = async (input, init) => { + let request = new Request(input, init); + if (request.method !== "GET") { + const body = await request.text(); + const cacheUrl = new URL(request.url); + cacheUrl.pathname += body; + request = new Request(cacheUrl.toString(), { method: "GET" }); + } + + return request; +}; + /** * 油猴脚本的请求封装 * @param {*} input @@ -31,7 +48,7 @@ export const fetchGM = async (input, { method = "GET", headers, body } = {}) => data: body, // withCredentials: true, timeout: TIMEOUT, - onload: ({ response, responseHeaders, status, statusText, ...opts }) => { + onload: ({ response, responseHeaders, status, statusText }) => { const headers = {}; responseHeaders.split("\n").forEach((line) => { const [name, value] = line.split(":").map((item) => item.trim()); @@ -55,9 +72,9 @@ export const fetchGM = async (input, { method = "GET", headers, body } = {}) => * @param {*} param0 * @returns */ -export const fetchApi = async ({ input, init, transOpts, apiSetting }) => { +export const fetchPatcher = async (input, init, transOpts, apiSetting) => { if (transOpts?.translator) { - [input, init] = await newTransReq(transOpts, apiSetting); + [input, init] = await genTransReq(transOpts, apiSetting); } if (!input) { @@ -99,55 +116,13 @@ export const fetchApi = async ({ input, init, transOpts, apiSetting }) => { }; /** - * 请求数据统一接口 - * @param {*} param0 + * 解析 response + * @param {*} res * @returns */ -export const fetchHandle = async ({ - input, - useCache, - transOpts, - apiSetting, - ...init -}) => { - const cacheReq = await newCacheReq(input, init); - let res; - - // 查询缓存 - if (useCache) { - try { - const cache = await caches.open(CACHE_NAME); - res = await cache.match(cacheReq); - } catch (err) { - kissLog(err, "cache match"); - } - } - +const parseResponse = async (res) => { if (!res) { - // 发送请求 - res = await fetchApi({ input, init, transOpts, apiSetting }); - if (!res) { - throw new Error("Unknow error"); - } else if (!res.ok) { - const msg = { - url: res.url, - status: res.status, - }; - if (res.headers.get("Content-Type")?.includes("json")) { - msg.response = await res.json(); - } - throw new Error(JSON.stringify(msg)); - } - - // 插入缓存 - if (useCache) { - try { - const cache = await caches.open(CACHE_NAME); - await cache.put(cacheReq, res.clone()); - } catch (err) { - kissLog(err, "cache put"); - } - } + return null; } const contentType = res.headers.get("Content-Type"); @@ -160,47 +135,142 @@ export const fetchHandle = async ({ return await res.text(); }; +/** + * 查询 caches + * @param {*} input + * @param {*} param1 + * @returns + */ +export const getHttpCache = async (input, { method, headers, body }) => { + try { + const req = await newCacheReq(input, { method, headers, body }); + const cache = await caches.open(CACHE_NAME); + const res = await cache.match(req); + return parseResponse(res); + } catch (err) { + kissLog(err, "get cache"); + } + return null; +}; + +/** + * 插入 caches + * @param {*} input + * @param {*} param1 + * @param {*} res + */ +export const putHttpCache = async (input, { method, headers, body }, res) => { + try { + const req = await newCacheReq(input, { method, headers, body }); + const cache = await caches.open(CACHE_NAME); + await cache.put(req, res); + } catch (err) { + kissLog(err, "put cache"); + } +}; + +/** + * 处理请求 + * @param {*} param0 + * @returns + */ +export const fetchHandle = async ({ + input, + useCache, + transOpts, + apiSetting, + ...init +}) => { + // 发送请求 + const res = await fetchPatcher(input, init, transOpts, apiSetting); + if (!res) { + throw new Error("Unknow error"); + } else if (!res.ok) { + const msg = { + url: res.url, + status: res.status, + }; + if (res.headers.get("Content-Type")?.includes("json")) { + msg.response = await res.json(); + } + throw new Error(JSON.stringify(msg)); + } + + // 插入缓存 + if (useCache) { + await putHttpCache(input, init, res.clone()); + } + + return parseResponse(res); +}; + +/** + * fetch 兼容性封装 + * @param {*} args + * @returns + */ +export const fetchPolyfill = (args) => { + // 插件 + if (isExt && !isBg()) { + return sendBgMsg(MSG_FETCH, args); + } + + // 油猴/网页/BackgroundPage + return fetchHandle(args); +}; + +/** + * getHttpCache 兼容性封装 + * @param {*} input + * @param {*} init + * @returns + */ +export const getHttpCachePolyfill = (input, init) => { + // 插件 + if (isExt && !isBg()) { + return sendBgMsg(MSG_GET_HTTPCACHE, { input, init }); + } + + // 油猴/网页/BackgroundPage + return getHttpCache(input, init); +}; + /** * 请求池实例 */ export const fetchPool = taskPool( - fetchHandle, + fetchPolyfill, null, DEFAULT_FETCH_INTERVAL, DEFAULT_FETCH_LIMIT ); /** - * 请求池分发 + * 数据请求 * @param {*} input * @param {*} param1 * @returns */ -export const fetchData = (input, { usePool, ...opts } = {}) => { - if (usePool) { - return fetchPool.push({ input, ...opts }); - } - return fetchHandle({ input, ...opts }); -}; - -/** - * fetch 兼容性封装 - * @param {*} input - * @param {*} opts - * @returns - */ -export const fetchPolyfill = async (input, opts) => { +export const fetchData = async (input, { useCache, usePool, ...args } = {}) => { if (!input?.trim()) { throw new Error("URL is empty"); } - // 插件 - if (isExt && !isBg()) { - return await sendBgMsg(MSG_FETCH, { input, opts }); + // 查询缓存 + if (useCache) { + const cache = await getHttpCachePolyfill(input, args); + if (cache) { + return cache; + } } - // 油猴/网页/BackgroundPage - return await fetchData(input, opts); + // 通过任务池发送请求 + if (usePool) { + return fetchPool.push({ input, useCache, ...args }); + } + + // 直接请求 + return fetchPolyfill({ input, useCache, ...args }); }; /** @@ -208,21 +278,13 @@ export const fetchPolyfill = async (input, opts) => { * @param {*} interval * @param {*} limit */ -export const updateFetchPool = async (interval, limit) => { - if (isExt) { - await sendBgMsg(MSG_FETCH_LIMIT, { interval, limit }); - } else { - fetchPool.update(interval, limit); - } +export const updateFetchPool = (interval, limit) => { + fetchPool.update(interval, limit); }; /** * 清空任务池 */ -export const clearFetchPool = async () => { - if (isExt) { - await sendBgMsg(MSG_FETCH_CLEAR); - } else { - fetchPool.clear(); - } +export const clearFetchPool = () => { + fetchPool.clear(); }; diff --git a/src/libs/sync.js b/src/libs/sync.js index 42f0c5d..37c5c6c 100644 --- a/src/libs/sync.js +++ b/src/libs/sync.js @@ -20,13 +20,14 @@ import { import { apiSyncData } from "../apis"; import { sha256, removeEndchar } from "./utils"; import { createClient, getPatcher } from "webdav"; -import { fetchApi } from "./fetch"; +import { fetchPatcher } from "./fetch"; import { kissLog } from "./log"; getPatcher().patch("request", (opts) => { - return fetchApi({ - input: opts.url, - init: { method: opts.method, headers: opts.headers, body: opts.data }, + return fetchPatcher(opts.url, { + method: opts.method, + headers: opts.headers, + body: opts.data, }); });