-
Notifications
You must be signed in to change notification settings - Fork 323
安装资源 转移至 chrome.storage.local 的 tempStorage. 代码部份放在 OPFS/temp_install_codes
#1318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/v1.4
Are you sure you want to change the base?
Changes from 1 commit
7ed623c
509bb2c
cf5205f
cf96571
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,4 @@ | ||
| export const CACHE_KEY_IMPORT_FILE = "importFile:"; // importFile 导入文件 | ||
| export const CACHE_KEY_TAB_SCRIPT = "tabScript:"; | ||
| export const CACHE_KEY_SCRIPT_INFO = "scriptInfo:"; // 加载脚本信息时的缓存 | ||
| export const CACHE_KEY_FAVICON = "favicon:"; | ||
| export const CACHE_KEY_SET_VALUE = "setValue:"; | ||
| export const CACHE_KEY_PERMISSION = "permission:"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { Repo } from "./repo"; | ||
|
|
||
| export const TempStorageItemType = { | ||
| tempCode: 1, | ||
| } as const; | ||
|
|
||
| export type TempStorageItemType = ValueOf<typeof TempStorageItemType>; | ||
|
|
||
| export interface TempStorageItem { | ||
| key: string; | ||
| value: any; | ||
| savedAt: number; | ||
| type: TempStorageItemType; | ||
| } | ||
|
|
||
| export const TEMP_ENTRY_MIN_TIME = 60_000; | ||
|
|
||
| export class TempStorageDAO extends Repo<TempStorageItem> { | ||
| constructor() { | ||
| super("tempStorage"); | ||
| } | ||
|
|
||
| save(value: TempStorageItem) { | ||
| return super._save(value.key, value); | ||
| } | ||
|
|
||
| async getValue<T>(key: string) { | ||
| return (await super.get(key))?.value as T | undefined; | ||
| } | ||
|
|
||
| async entries(type?: TempStorageItemType) { | ||
| const data = await super.find((key, value) => { | ||
| return type ? value.type === type : true; | ||
| }); | ||
| return data; | ||
| } | ||
|
|
||
| async staleEntries(keeps: Set<string>) { | ||
| const now = Date.now(); | ||
| const entries = await new TempStorageDAO().entries(); | ||
| return entries.filter((entry) => !keeps.has(entry.key) && now - entry.savedAt > TEMP_ENTRY_MIN_TIME); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||
| import { cacheInstance } from "@App/app/cache"; | ||||||
| import { TEMP_ENTRY_MIN_TIME, TempStorageDAO, TempStorageItemType } from "@App/app/repo/tempStorage"; | ||||||
| import { removeCachedCodes } from "@App/pkg/utils/scriptInstall"; | ||||||
| import { timeoutExecution } from "@App/pkg/utils/timer"; | ||||||
|
|
||||||
| export const cleanupStaleTempStorageEntries = () => { | ||||||
| // 清除旧记录 | ||||||
| const delay = Math.floor(5000 * Math.random()) + 10000; // 使用随机时间避免浏览器重启时大量Tabs同时执行清除 | ||||||
| timeoutExecution( | ||||||
| `cid_100_cleanupStaleTempStorageEntries`, | ||||||
| () => { | ||||||
| cacheInstance | ||||||
| .tx(`keepTemp`, (val: Record<string, number> | undefined, tx) => { | ||||||
| const now = Date.now(); | ||||||
| const keeps = new Set<string>(); | ||||||
| const out: Record<string, number> = {}; | ||||||
| for (const [k, ts] of Object.entries(val ?? {})) { | ||||||
| if (ts > 0 && now - ts < TEMP_ENTRY_MIN_TIME) { | ||||||
| keeps.add(`${k}`); | ||||||
| out[k] = ts; | ||||||
| } | ||||||
| } | ||||||
| tx.set(out); | ||||||
| return keeps; | ||||||
| }) | ||||||
| .then(async (keeps) => { | ||||||
| const stales = await new TempStorageDAO().staleEntries(keeps); | ||||||
| if (stales.length) { | ||||||
| // 清理缓存 | ||||||
| const list = stales.map((e) => e.key); | ||||||
| const list1 = stales.filter((entry) => entry.type === TempStorageItemType.tempCode).map((e) => e.key); | ||||||
| await removeCachedCodes(list1); | ||||||
| cacheInstance.dels(list); | ||||||
|
||||||
| cacheInstance.dels(list); | |
| await new TempStorageDAO().deletes(list); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,7 +20,8 @@ | |||||||||||||||||
| import type { Subscribe } from "@App/app/repo/subscribe"; | ||||||||||||||||||
| import { i18nDescription, i18nName } from "@App/locales/locales"; | ||||||||||||||||||
| import { useTranslation } from "react-i18next"; | ||||||||||||||||||
| import { createScriptInfo, type ScriptInfo } from "@App/pkg/utils/scriptInstall"; | ||||||||||||||||||
| import { createScriptInfoLocal, createTempCodeEntry, type ScriptInfo } from "@App/pkg/utils/scriptInstall"; | ||||||||||||||||||
| import { getTempCode } from "@App/pkg/utils/scriptInstall"; | ||||||||||||||||||
| import { parseMetadata, prepareScriptByCode, prepareSubscribeByCode } from "@App/pkg/utils/script"; | ||||||||||||||||||
| import { nextTimeDisplay } from "@App/pkg/utils/cron"; | ||||||||||||||||||
| import { scriptClient, subscribeClient } from "../store/features/script"; | ||||||||||||||||||
|
|
@@ -29,12 +30,12 @@ | |||||||||||||||||
| import { dayFormat } from "@App/pkg/utils/day_format"; | ||||||||||||||||||
| import { intervalExecution, timeoutExecution } from "@App/pkg/utils/timer"; | ||||||||||||||||||
| import { useSearchParams } from "react-router-dom"; | ||||||||||||||||||
| import { CACHE_KEY_SCRIPT_INFO } from "@App/app/cache_key"; | ||||||||||||||||||
| import { cacheInstance } from "@App/app/cache"; | ||||||||||||||||||
| import { formatBytes, isPermissionOk } from "@App/pkg/utils/utils"; | ||||||||||||||||||
| import { ScriptIcons } from "../options/routes/utils"; | ||||||||||||||||||
| import { bytesDecode, detectEncoding } from "@App/pkg/utils/encoding"; | ||||||||||||||||||
| import { prettyUrl } from "@App/pkg/utils/url-utils"; | ||||||||||||||||||
| import { TempStorageDAO, TempStorageItemType } from "@App/app/repo/tempStorage"; | ||||||||||||||||||
|
|
||||||||||||||||||
| const backgroundPromptShownKey = "background_prompt_shown"; | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -129,51 +130,26 @@ | |||||||||||||||||
| return { code, metadata }; | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| const cleanupStaleInstallInfo = (uuid: string) => { | ||||||||||||||||||
| // 页面打开时不清除当前uuid,每30秒更新一次记录 | ||||||||||||||||||
| const f = () => { | ||||||||||||||||||
| cacheInstance.tx(`scriptInfoKeeps`, (val: Record<string, number> | undefined, tx) => { | ||||||||||||||||||
| val = val || {}; | ||||||||||||||||||
| val[uuid] = Date.now(); | ||||||||||||||||||
| tx.set(val); | ||||||||||||||||||
| }); | ||||||||||||||||||
| }; | ||||||||||||||||||
| f(); | ||||||||||||||||||
| setInterval(f, 30_000); | ||||||||||||||||||
|
|
||||||||||||||||||
| // 页面打开后清除旧记录 | ||||||||||||||||||
| const delay = Math.floor(5000 * Math.random()) + 10000; // 使用随机时间避免浏览器重启时大量Tabs同时执行清除 | ||||||||||||||||||
| timeoutExecution( | ||||||||||||||||||
| `${cIdKey}cleanupStaleInstallInfo`, | ||||||||||||||||||
| () => { | ||||||||||||||||||
| cacheInstance | ||||||||||||||||||
| .tx(`scriptInfoKeeps`, (val: Record<string, number> | undefined, tx) => { | ||||||||||||||||||
| const now = Date.now(); | ||||||||||||||||||
| const keeps = new Set<string>(); | ||||||||||||||||||
| const out: Record<string, number> = {}; | ||||||||||||||||||
| for (const [k, ts] of Object.entries(val ?? {})) { | ||||||||||||||||||
| if (ts > 0 && now - ts < 60_000) { | ||||||||||||||||||
| keeps.add(`${CACHE_KEY_SCRIPT_INFO}${k}`); | ||||||||||||||||||
| out[k] = ts; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| tx.set(out); | ||||||||||||||||||
| return keeps; | ||||||||||||||||||
| }) | ||||||||||||||||||
| .then(async (keeps) => { | ||||||||||||||||||
| const list = await cacheInstance.list(); | ||||||||||||||||||
| const filtered = list.filter((key) => key.startsWith(CACHE_KEY_SCRIPT_INFO) && !keeps.has(key)); | ||||||||||||||||||
| if (filtered.length) { | ||||||||||||||||||
| // 清理缓存 | ||||||||||||||||||
| cacheInstance.dels(filtered); | ||||||||||||||||||
| } | ||||||||||||||||||
| }); | ||||||||||||||||||
| }, | ||||||||||||||||||
| delay | ||||||||||||||||||
| ); | ||||||||||||||||||
| const cIdKey = `(cid_${Math.random()})`; | ||||||||||||||||||
|
|
||||||||||||||||||
| let activeSessionKey = ""; | ||||||||||||||||||
| let keepAliveTimerId: ReturnType<typeof setTimeout> | number = 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| const updateSessionTimestamp = () => { | ||||||||||||||||||
| cacheInstance.tx(`keepTemp`, (val: Record<string, number> | undefined, tx) => { | ||||||||||||||||||
| val = val || {}; | ||||||||||||||||||
| val[activeSessionKey] = Date.now(); | ||||||||||||||||||
| tx.set(val); | ||||||||||||||||||
| }); | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| const cIdKey = `(cid_${Math.random()})`; | ||||||||||||||||||
| const startKeepAlive = (key: string) => { | ||||||||||||||||||
| activeSessionKey = key; | ||||||||||||||||||
| // 页面打开时不清除当前uuid,每30秒更新一次记录 | ||||||||||||||||||
| updateSessionTimestamp(); | ||||||||||||||||||
| clearInterval(keepAliveTimerId); | ||||||||||||||||||
| keepAliveTimerId = setInterval(updateSessionTimestamp, 30_000); | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| function App() { | ||||||||||||||||||
| const [enable, setEnable] = useState<boolean>(false); | ||||||||||||||||||
|
|
@@ -235,14 +211,16 @@ | |||||||||||||||||
|
|
||||||||||||||||||
| let paramOptions = {}; | ||||||||||||||||||
| if (uuid) { | ||||||||||||||||||
| startKeepAlive(uuid); | ||||||||||||||||||
| const cachedInfo = await scriptClient.getInstallInfo(uuid); | ||||||||||||||||||
| cleanupStaleInstallInfo(uuid); | ||||||||||||||||||
| if (cachedInfo?.[0]) isKnownUpdate = true; | ||||||||||||||||||
| info = cachedInfo?.[1] || undefined; | ||||||||||||||||||
| paramOptions = cachedInfo?.[2] || {}; | ||||||||||||||||||
| if (!info) { | ||||||||||||||||||
| throw new Error("fetch script info failed"); | ||||||||||||||||||
| } | ||||||||||||||||||
| const code = await getTempCode(uuid); | ||||||||||||||||||
| if (code) info.code = code; | ||||||||||||||||||
|
||||||||||||||||||
| if (code) info.code = code; | |
| // 这里从 OPFS 读不到代码(getTempCode 返回空字符串)应视为错误,避免后续拿到空代码导致空白安装页 | |
| if (code === "") { | |
| throw new Error("failed to load script code from temp storage"); | |
| } | |
| if (code) { | |
| info.code = code; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
把uuid拆开的意义何在
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
每次调用都会启动cleanupStaleTempStorageEntries,而且内部逻辑复杂;我觉得做一个定时任务,每隔多久,扫描一次全部的,清理一下就可以了