From 44b6f11b195b2e7082c195a3835d0a64b199cdef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Tue, 15 Apr 2025 18:06:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9D=83=E9=99=90=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rspack.config.ts | 10 ++ src/app/service/service_worker/gm_api.ts | 34 ++++- .../service_worker/permission_verify.ts | 25 ++- src/locales/locales.ts | 24 +-- src/pages/components/GMApiSetting/index.tsx | 19 ++- src/pages/confirm/App.tsx | 142 ++++++++++++++++++ src/pages/confirm/main.tsx | 33 ++++ src/pages/options/routes/Setting.tsx | 38 +++-- src/pkg/config/config.ts | 24 +++ src/service_worker.ts | 3 +- 10 files changed, 307 insertions(+), 45 deletions(-) create mode 100644 src/pages/confirm/App.tsx create mode 100644 src/pages/confirm/main.tsx diff --git a/rspack.config.ts b/rspack.config.ts index c3c8051..a0373ed 100644 --- a/rspack.config.ts +++ b/rspack.config.ts @@ -35,6 +35,7 @@ export default defineConfig({ inject: `${src}/inject.ts`, popup: `${src}/pages/popup/main.tsx`, install: `${src}/pages/install/main.tsx`, + confirm: `${src}/pages/confirm/main.tsx`, options: `${src}/pages/options/main.tsx`, "editor.worker": "monaco-editor/esm/vs/editor/editor.worker.js", "ts.worker": "monaco-editor/esm/vs/language/typescript/ts.worker.js", @@ -151,6 +152,15 @@ export default defineConfig({ minify: true, chunks: ["install"], }), + , + new rspack.HtmlRspackPlugin({ + filename: `${dist}/ext/src/confirm.html`, + template: `${src}/pages/template.html`, + inject: "head", + title: "Confirm - ScriptCat", + minify: true, + chunks: ["confirm"], + }), new rspack.HtmlRspackPlugin({ filename: `${dist}/ext/src/options.html`, template: `${src}/pages/options.html`, diff --git a/src/app/service/service_worker/gm_api.ts b/src/app/service/service_worker/gm_api.ts index 6e87e9f..efb4af1 100644 --- a/src/app/service/service_worker/gm_api.ts +++ b/src/app/service/service_worker/gm_api.ts @@ -3,7 +3,7 @@ import Logger from "@App/app/logger/logger"; import { Script, ScriptDAO } from "@App/app/repo/scripts"; import { ExtMessageSender, GetSender, Group, MessageSend } from "@Packages/message/server"; import { ValueService } from "@App/app/service/service_worker/value"; -import PermissionVerify from "./permission_verify"; +import PermissionVerify, { ConfirmParam } from "./permission_verify"; import { connect, sendMessage } from "@Packages/message/client"; import Cache, { incr } from "@App/app/cache"; import EventEmitter from "eventemitter3"; @@ -13,6 +13,7 @@ import { getIcon, isFirefox } from "@App/pkg/utils/utils"; import { PopupService } from "./popup"; import { act } from "react"; import { MockMessageConnect } from "@Packages/message/mock_message"; +import i18next, { i18nName } from "@App/locales/locales"; // GMApi,处理脚本的GM API调用请求 @@ -357,8 +358,35 @@ export default class GMApi { }); } - // TODO: maxRedirects实现 - @PermissionVerify.API() + @PermissionVerify.API({ + confirm: async (request: Request) => { + console.log("confirm", request); + const config = request.params[0]; + const url = new URL(config.url); + if (request.script.metadata.connect) { + const { connect } = request.script.metadata; + for (let i = 0; i < connect.length; i += 1) { + if (url.hostname.endsWith(connect[i])) { + return Promise.resolve(true); + } + } + } + const metadata: { [key: string]: string } = {}; + metadata[i18next.t("script_name")] = i18nName(request.script); + metadata[i18next.t("request_domain")] = url.hostname; + metadata[i18next.t("request_url")] = config.url; + + return Promise.resolve({ + permission: "cors", + permissionValue: url.hostname, + title: i18next.t("script_accessing_cross_origin_resource"), + metadata, + describe: i18next.t("confirm_operation_description"), + wildcard: true, + permissionContent: i18next.t("domain"), + } as ConfirmParam); + }, + }) async GM_xmlhttpRequest(request: Request, sender: GetSender) { if (request.params.length === 0) { throw new Error("param is failed"); diff --git a/src/app/service/service_worker/permission_verify.ts b/src/app/service/service_worker/permission_verify.ts index 95cdd60..a4064f2 100644 --- a/src/app/service/service_worker/permission_verify.ts +++ b/src/app/service/service_worker/permission_verify.ts @@ -183,6 +183,7 @@ export default class PermissionVerify { } return Promise.resolve(model); }); + console.log("confirm", request, confirm); // 有查询到结果,进入判断,不再需要用户确认 if (ret) { if (ret.allow) { @@ -249,9 +250,27 @@ export default class PermissionVerify { // 弹出窗口让用户进行确认 async confirmWindow(script: Script, confirm: ConfirmParam): Promise { - return Promise.resolve({ - allow: true, - type: 1, + return new Promise((resolve, reject) => { + const uuid = uuidv4(); + // 超时处理 + const timeout = setTimeout(() => { + this.confirmMap.delete(uuid); + reject(new Error("permission confirm timeout")); + }, 40 * 1000); + // 保存到map中 + this.confirmMap.set(uuid, { + confirm, + script, + resolve: (value: UserConfirm) => { + clearTimeout(timeout); + resolve(value); + }, + reject, + }); + // 打开窗口 + chrome.tabs.create({ + url: chrome.runtime.getURL(`src/confirm.html?uuid=${uuid}`), + }); }); } } diff --git a/src/locales/locales.ts b/src/locales/locales.ts index 2889b6b..3739a4b 100644 --- a/src/locales/locales.ts +++ b/src/locales/locales.ts @@ -10,10 +10,11 @@ import zhTW from "./zh-TW/translation.json"; import achUG from "./ach-UG/translation.json"; import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-tw"; +import { systemConfig } from "@App/pages/store/global"; i18n.use(initReactI18next).init({ fallbackLng: "zh-CN", - lng: localStorage.language || chrome.i18n.getUILanguage(), + lng: chrome.i18n.getUILanguage(), interpolation: { escapeValue: false, // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape }, @@ -26,22 +27,13 @@ i18n.use(initReactI18next).init({ }, }); -if (!localStorage.language) { - chrome.i18n.getAcceptLanguages((lngs) => { - // 遍历数组寻找匹配语言 - for (let i = 0; i < lngs.length; i += 1) { - const lng = lngs[i]; - if (i18n.hasResourceBundle(lng, "translation")) { - localStorage.language = lng; - i18n.changeLanguage(lng); - dayjs.locale(lng.toLocaleLowerCase()); - break; - } - } +chrome.i18n.getAcceptLanguages((lngs) => { + systemConfig.getLanguage().then((lng) => { + i18n.changeLanguage(lng); + dayjs.locale(lng.toLocaleLowerCase()); }); -} else { - dayjs.locale((localStorage.language as string).toLocaleLowerCase()); -} +}); + dayjs.extend(relativeTime); export function i18nName(script: { name: string; metadata: Metadata }) { diff --git a/src/pages/components/GMApiSetting/index.tsx b/src/pages/components/GMApiSetting/index.tsx index b2a7ecf..70c9657 100644 --- a/src/pages/components/GMApiSetting/index.tsx +++ b/src/pages/components/GMApiSetting/index.tsx @@ -3,7 +3,8 @@ import { Button, Card, Collapse, Link, Message, Space, Typography } from "@arco- import { useTranslation } from "react-i18next"; import FileSystemParams from "../FileSystemParams"; import { systemConfig } from "@App/pages/store/global"; -import { FileSystemType } from "@Packages/filesystem/factory"; +import FileSystemFactory, { FileSystemType } from "@Packages/filesystem/factory"; +import { set } from "node_modules/yaml/dist/schema/yaml-1.1/set"; const CollapseItem = Collapse.Item; @@ -52,13 +53,13 @@ const GMApiSetting: React.FC = () => { Message.error(`${t("account_validation_failed")}: ${e}`); return; } - const params = { ...systemConfig.catFileStorage.params }; + const params = { ...fileSystemParams }; params[fileSystemType] = fileSystemParams; - systemConfig.catFileStorage = { + systemConfig.setCatFileStorage({ status: "success", filesystem: fileSystemType, params, - }; + }); setStatus("success"); Message.success(t("save_success")!); }} @@ -68,10 +69,14 @@ const GMApiSetting: React.FC = () => { + +
+ + + + {likeNum > 2 && ( + + )} + + {likeNum > 2 && ( + + )} + +
+
+ + + + {likeNum > 2 && ( + + )} + + {likeNum > 2 && ( + + )} + +
+ + + ); +} + +export default App; diff --git a/src/pages/confirm/main.tsx b/src/pages/confirm/main.tsx new file mode 100644 index 0000000..4a85f4a --- /dev/null +++ b/src/pages/confirm/main.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; +import MainLayout from "../components/layout/MainLayout.tsx"; +import "@arco-design/web-react/dist/css/arco.css"; +import "@App/locales/locales"; +import "@App/index.css"; +import { Provider } from "react-redux"; +import { store } from "@App/pages/store/store.ts"; +import LoggerCore from "@App/app/logger/core.ts"; +import migrate from "@App/app/migrate.ts"; +import { LoggerDAO } from "@App/app/repo/logger.ts"; +import DBWriter from "@App/app/logger/db_writer.ts"; + +// 初始化数据库 +migrate(); +// 初始化日志组件 +const loggerCore = new LoggerCore({ + writer: new DBWriter(new LoggerDAO()), + labels: { env: "install" }, +}); + +loggerCore.logger().debug("page start"); + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + + + + + + + +); diff --git a/src/pages/options/routes/Setting.tsx b/src/pages/options/routes/Setting.tsx index aa6c468..82e67b3 100644 --- a/src/pages/options/routes/Setting.tsx +++ b/src/pages/options/routes/Setting.tsx @@ -10,9 +10,9 @@ import i18n from "@App/locales/locales"; import { useTranslation } from "react-i18next"; import dayjs from "dayjs"; import Logger from "@App/app/logger/logger"; -import { systemConfig } from "@App/pages/store/global"; -import { FileSystemType } from "@Packages/filesystem/factory"; +import FileSystemFactory, { FileSystemType } from "@Packages/filesystem/factory"; import FileSystemParams from "@App/pages/components/FileSystemParams"; +import { systemConfig } from "@App/pages/store/global"; function Setting() { const [syncDelete, setSyncDelete] = useState(); @@ -46,16 +46,25 @@ function Setting() { useEffect(() => { const loadConfigs = async () => { - const [cloudSync, menuExpandNum, checkCycle, updateDisabled, silenceUpdate, eslintConfig, enableEslint] = - await Promise.all([ - systemConfig.getCloudSync(), - systemConfig.getMenuExpandNum(), - systemConfig.getCheckScriptUpdateCycle(), - systemConfig.getUpdateDisableScript(), - systemConfig.getSilenceUpdateScript(), - systemConfig.getEslintConfig(), - systemConfig.getEnableEslint(), - ]); + const [ + cloudSync, + menuExpandNum, + checkCycle, + updateDisabled, + silenceUpdate, + eslintConfig, + enableEslint, + language, + ] = await Promise.all([ + systemConfig.getCloudSync(), + systemConfig.getMenuExpandNum(), + systemConfig.getCheckScriptUpdateCycle(), + systemConfig.getUpdateDisableScript(), + systemConfig.getSilenceUpdateScript(), + systemConfig.getEslintConfig(), + systemConfig.getEnableEslint(), + systemConfig.getLanguage(), + ]); setSyncDelete(cloudSync.syncDelete); setEnableCloudSync(cloudSync.enable); @@ -67,6 +76,7 @@ function Setting() { setSilenceUpdateScript(silenceUpdate); setEslintConfig(eslintConfig); setEnableEslint(enableEslint); + setLanguage(language); }; loadConfigs(); @@ -96,9 +106,7 @@ function Setting() { return; } setLanguage(value); - i18n.changeLanguage(value); - dayjs.locale(value.toLocaleLowerCase()); - localStorage.language = value; + systemConfig.setLanguage(value); Message.success(t("language_change_tip")!); }} > diff --git a/src/pkg/config/config.ts b/src/pkg/config/config.ts index 8da73e0..5ff293d 100644 --- a/src/pkg/config/config.ts +++ b/src/pkg/config/config.ts @@ -3,6 +3,8 @@ import ChromeStorage from "./chrome_storage"; import { defaultConfig } from "../../../packages/eslint/linter-config"; import { FileSystemType } from "@Packages/filesystem/factory"; import { MessageQueue } from "@Packages/message/message_queue"; +import i18n from "@App/locales/locales"; +import dayjs from "dayjs"; export const SystamConfigChange = "systemConfigChange"; @@ -214,4 +216,26 @@ export class SystemConfig { setMenuExpandNum(val: number) { this.set("menu_expand_num", val); } + + async getLanguage() { + const defaultLanguage = await new Promise((resolve) => { + chrome.i18n.getAcceptLanguages((lngs) => { + // 遍历数组寻找匹配语言 + for (let i = 0; i < lngs.length; i += 1) { + const lng = lngs[i]; + if (i18n.hasResourceBundle(lng, "translation")) { + resolve(lng); + break; + } + } + }); + }); + return this.get("language", defaultLanguage || chrome.i18n.getUILanguage()); + } + + setLanguage(value: any) { + this.set("language", value); + i18n.changeLanguage(value); + dayjs.locale(value.toLocaleLowerCase()); + } } diff --git a/src/service_worker.ts b/src/service_worker.ts index 5cdce06..1127401 100644 --- a/src/service_worker.ts +++ b/src/service_worker.ts @@ -58,7 +58,8 @@ async function main() { // 初始化管理器 const message = new ExtensionMessage(true); const server = new Server("serviceWorker", message); - const manager = new ServiceWorkerManager(server, new MessageQueue(), new ServiceWorkerMessageSend()); + const messageQueue = new MessageQueue(); + const manager = new ServiceWorkerManager(server, messageQueue, new ServiceWorkerMessageSend()); manager.initManager(); // 初始化沙盒环境 await setupOffscreenDocument();