优化细节
This commit is contained in:
parent
e1a890a400
commit
a26f1c5014
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "scriptcat",
|
||||
"version": "0.17.0-alpha.2",
|
||||
"version": "0.17.0-alpha.4",
|
||||
"description": "脚本猫,一个可以执行用户脚本的浏览器扩展,万物皆可脚本化,让你的浏览器可以做更多的事情!",
|
||||
"author": "CodFrm",
|
||||
"license": "GPLv3",
|
||||
|
@ -113,7 +113,6 @@ function renameField() {
|
||||
if (subscribe.length) {
|
||||
await Promise.all(
|
||||
subscribe.map((s: Subscribe) => {
|
||||
console.log("1234", s);
|
||||
const { url, name, code, author, scripts, metadata, status, createtime, updatetime, checktime } = s;
|
||||
return subscribeDAO.save({
|
||||
url,
|
||||
|
@ -23,6 +23,7 @@ export default class ContentRuntime {
|
||||
// 转发给inject
|
||||
return sendMessage(this.msg, "inject/runtime/valueUpdate", data);
|
||||
});
|
||||
forwardMessage("serviceWorker", "script/isInstalled", this.server, this.extSend);
|
||||
forwardMessage(
|
||||
"serviceWorker",
|
||||
"runtime/gmApi",
|
||||
|
@ -4,6 +4,8 @@ import ExecScript, { ValueUpdateData } from "./exec_script";
|
||||
import { addStyle, ScriptFunc } from "./utils";
|
||||
import { getStorageName } from "@App/pkg/utils/utils";
|
||||
import { EmitEventRequest } from "../service_worker/runtime";
|
||||
import { ExternalWhitelist } from "@App/app/const";
|
||||
import { sendMessage } from "@Packages/message/client";
|
||||
|
||||
export class InjectRuntime {
|
||||
execList: ExecScript[] = [];
|
||||
@ -44,6 +46,40 @@ export class InjectRuntime {
|
||||
val.valueUpdate(data);
|
||||
});
|
||||
});
|
||||
// 注入允许外部调用
|
||||
this.externalMessage();
|
||||
}
|
||||
|
||||
externalMessage() {
|
||||
// 对外接口白名单
|
||||
let msg = this.msg;
|
||||
for (let i = 0; i < ExternalWhitelist.length; i += 1) {
|
||||
if (window.location.host.endsWith(ExternalWhitelist[i])) {
|
||||
// 注入
|
||||
(<{ external: any }>(<unknown>window)).external = window.external || {};
|
||||
(<
|
||||
{
|
||||
external: {
|
||||
Scriptcat: {
|
||||
isInstalled: (name: string, namespace: string, callback: any) => void;
|
||||
};
|
||||
};
|
||||
}
|
||||
>(<unknown>window)).external.Scriptcat = {
|
||||
async isInstalled(name: string, namespace: string, callback: any) {
|
||||
const resp = await sendMessage(msg, "content/script/isInstalled", {
|
||||
name,
|
||||
namespace,
|
||||
});
|
||||
callback(resp);
|
||||
},
|
||||
};
|
||||
(<{ external: { Tampermonkey: any } }>(<unknown>window)).external.Tampermonkey = (<
|
||||
{ external: { Scriptcat: any } }
|
||||
>(<unknown>window)).external.Scriptcat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execScript(script: ScriptRunResouce, scriptFunc: ScriptFunc) {
|
||||
|
@ -9,6 +9,8 @@ import { PopupService } from "./popup";
|
||||
import { SystemConfig } from "@App/pkg/config/config";
|
||||
import { SynchronizeService } from "./synchronize";
|
||||
import { SubscribeService } from "./subscribe";
|
||||
import { ExtServer, ExtVersion } from "@App/app/const";
|
||||
import { systemConfig } from "@App/pages/store/global";
|
||||
|
||||
export type InstallSource = "user" | "system" | "sync" | "subscribe" | "vscode";
|
||||
|
||||
@ -78,8 +80,17 @@ export default class ServiceWorkerManager {
|
||||
case "checkSubscribeUpdate":
|
||||
subscribe.checkSubscribeUpdate();
|
||||
break;
|
||||
case "checkUpdate":
|
||||
// 检查扩展更新
|
||||
this.checkUpdate();
|
||||
break;
|
||||
}
|
||||
});
|
||||
// 8小时检查一次扩展更新
|
||||
chrome.alarms.create("checkUpdate", {
|
||||
delayInMinutes: 0,
|
||||
periodInMinutes: 8 * 60,
|
||||
});
|
||||
|
||||
// 监听配置变化
|
||||
this.mq.subscribe("systemConfigChange", (msg) => {
|
||||
@ -96,4 +107,18 @@ export default class ServiceWorkerManager {
|
||||
synchronize.cloudSyncConfigChange(config);
|
||||
});
|
||||
}
|
||||
|
||||
checkUpdate() {
|
||||
fetch(`${ExtServer}api/v1/system/version?version=${ExtVersion}`)
|
||||
.then((resp) => resp.json())
|
||||
.then((resp: { data: { notice: string; version: string } }) => {
|
||||
systemConfig.getCheckUpdate().then((items) => {
|
||||
if (items.notice !== resp.data.notice) {
|
||||
systemConfig.setCheckUpdate(Object.assign(resp.data, { isRead: false }));
|
||||
} else {
|
||||
systemConfig.setCheckUpdate(Object.assign(resp.data, { isRead: items.isRead }));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import { subscribeScriptDelete, subscribeScriptEnable, subscribeScriptInstall }
|
||||
import { ScriptService } from "./script";
|
||||
import { runScript, stopScript } from "../offscreen/client";
|
||||
import { getRunAt } from "./utils";
|
||||
import { randomString } from "@App/pkg/utils/utils";
|
||||
import { isUserScriptsAvailable, randomString } from "@App/pkg/utils/utils";
|
||||
import Cache from "@App/app/cache";
|
||||
import { dealPatternMatches, UrlMatch } from "@App/pkg/utils/match";
|
||||
import { ExtensionContentMessageSend } from "@Packages/message/extension_message";
|
||||
@ -70,6 +70,11 @@ export class RuntimeService {
|
||||
this.group.on("runScript", this.runScript.bind(this));
|
||||
this.group.on("pageLoad", this.pageLoad.bind(this));
|
||||
|
||||
// 检查是否开启了开发者模式
|
||||
if(!isUserScriptsAvailable()){
|
||||
// 未开启加上警告引导
|
||||
|
||||
}
|
||||
// 读取inject.js注入页面
|
||||
this.registerInjectScript();
|
||||
// 监听脚本开启
|
||||
|
@ -21,6 +21,7 @@ import { ResourceService } from "./resource";
|
||||
import { ValueService } from "./value";
|
||||
import { compileScriptCode } from "../content/utils";
|
||||
import { SystemConfig } from "@App/pkg/config/config";
|
||||
import i18n from "@App/locales/locales";
|
||||
|
||||
export class ScriptService {
|
||||
logger: Logger;
|
||||
@ -59,16 +60,10 @@ export class ScriptService {
|
||||
// 读取脚本url内容, 进行安装
|
||||
const logger = this.logger.with({ url: targetUrl });
|
||||
logger.debug("install script");
|
||||
this.openInstallPageByUrl(targetUrl, "user").catch((e) => {
|
||||
this.openInstallPageByUrl(targetUrl, "user")
|
||||
.catch((e) => {
|
||||
logger.error("install script error", Logger.E(e));
|
||||
// 如果打开失败, 则重定向到安装页
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: req.tabId },
|
||||
func: function () {
|
||||
history.back();
|
||||
},
|
||||
});
|
||||
// 并不再重定向当前url
|
||||
// 不再重定向当前url
|
||||
chrome.declarativeNetRequest.updateDynamicRules(
|
||||
{
|
||||
removeRuleIds: [2],
|
||||
@ -93,16 +88,31 @@ export class ScriptService {
|
||||
}
|
||||
}
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
// 回退到到安装页
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: req.tabId },
|
||||
func: function () {
|
||||
history.back();
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
{
|
||||
urls: [
|
||||
"https://docs.scriptcat.org/docs/script_installation",
|
||||
"https://docs.scriptcat.org/docs/script_installation/",
|
||||
"https://docs.scriptcat.org/en/docs/script_installation/",
|
||||
"https://www.tampermonkey.net/script_installation.php",
|
||||
],
|
||||
types: ["main_frame"],
|
||||
}
|
||||
);
|
||||
// 获取i18n
|
||||
let localePath = "";
|
||||
if (i18n.language !== "zh-CN") {
|
||||
localePath = `/en`;
|
||||
}
|
||||
// 重定向到脚本安装页
|
||||
chrome.declarativeNetRequest.updateDynamicRules(
|
||||
{
|
||||
@ -114,7 +124,7 @@ export class ScriptService {
|
||||
action: {
|
||||
type: chrome.declarativeNetRequest.RuleActionType.REDIRECT,
|
||||
redirect: {
|
||||
regexSubstitution: "https://docs.scriptcat.org/docs/script_installation#url=\\0",
|
||||
regexSubstitution: `https://docs.scriptcat.org${localePath}/docs/script_installation/#url=\\0`,
|
||||
},
|
||||
},
|
||||
condition: {
|
||||
@ -479,6 +489,15 @@ export class ScriptService {
|
||||
return this.checkUpdate(uuid, "user");
|
||||
}
|
||||
|
||||
isInstalled({ name, namespace }: { name: string; namespace: string }) {
|
||||
return this.scriptDAO.findByNameAndNamespace(name, namespace).then((script) => {
|
||||
if (script) {
|
||||
return { installed: true, version: script.metadata.version && script.metadata.version[0] };
|
||||
}
|
||||
return { installed: false };
|
||||
});
|
||||
}
|
||||
|
||||
init() {
|
||||
this.listenerScriptInstall();
|
||||
|
||||
@ -494,6 +513,7 @@ export class ScriptService {
|
||||
this.group.on("resetMatch", this.resetMatch.bind(this));
|
||||
this.group.on("resetExclude", this.resetExclude.bind(this));
|
||||
this.group.on("requestCheckUpdate", this.requestCheckUpdate.bind(this));
|
||||
this.group.on("isInstalled", this.isInstalled.bind(this));
|
||||
|
||||
// 定时检查更新, 每10分钟检查一次
|
||||
chrome.alarms.create("checkScriptUpdate", {
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_scriptcat__",
|
||||
"version": "0.17.0.1003",
|
||||
"version": "0.17.0.1005",
|
||||
"author": "CodFrm",
|
||||
"description": "__MSG_scriptcat_description__",
|
||||
"options_ui": {
|
||||
|
@ -39,11 +39,9 @@ const CodeEditor: React.ForwardRefRenderFunction<{ editor: editor.IStandaloneCod
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("1231", code);
|
||||
if (diffCode === undefined || code === undefined || !div.current) {
|
||||
return () => {};
|
||||
}
|
||||
console.log("1232");
|
||||
let edit: editor.IStandaloneDiffEditor | editor.IStandaloneCodeEditor;
|
||||
const inlineDiv = document.getElementById(id) as HTMLDivElement;
|
||||
// @ts-ignore
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Script, ScriptAndCode, ScriptCodeDAO, ScriptDAO } from "@App/app/repo/scripts";
|
||||
import { Script, SCRIPT_TYPE_NORMAL, ScriptAndCode, ScriptCodeDAO, ScriptDAO } from "@App/app/repo/scripts";
|
||||
import CodeEditor from "@App/pages/components/CodeEditor";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
@ -16,7 +16,7 @@ import { prepareScriptByCode } from "@App/pkg/utils/script";
|
||||
import ScriptStorage from "@App/pages/components/ScriptStorage";
|
||||
import ScriptResource from "@App/pages/components/ScriptResource";
|
||||
import ScriptSetting from "@App/pages/components/ScriptSetting";
|
||||
import { scriptClient } from "@App/pages/store/features/script";
|
||||
import { runtimeClient, scriptClient } from "@App/pages/store/features/script";
|
||||
import { i18nName } from "@App/locales/locales";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@ -188,16 +188,16 @@ function ScriptEditor() {
|
||||
|
||||
const save = (script: Script, e: editor.IStandaloneCodeEditor): Promise<Script> => {
|
||||
// 解析code生成新的script并更新
|
||||
return new Promise(() => {
|
||||
prepareScriptByCode(e.getValue(), script.origin || "", script.uuid)
|
||||
return prepareScriptByCode(e.getValue(), script.origin || "", script.uuid)
|
||||
.then((prepareScript) => {
|
||||
const newScript = prepareScript.script;
|
||||
if (!newScript.name) {
|
||||
Message.warning(t("script_name_cannot_be_set_to_empty"));
|
||||
return;
|
||||
return Promise.reject(new Error("script name cannot be empty"));
|
||||
}
|
||||
scriptClient.install(newScript, e.getValue()).then(
|
||||
(update) => {
|
||||
return scriptClient
|
||||
.install(newScript, e.getValue())
|
||||
.then((update): Script => {
|
||||
if (!update) {
|
||||
Message.success("新建成功,请注意后台脚本不会默认开启");
|
||||
// 保存的时候如何左侧没有脚本即新建
|
||||
@ -207,7 +207,6 @@ function ScriptEditor() {
|
||||
});
|
||||
} else {
|
||||
setScriptList((prev) => {
|
||||
// eslint-disable-next-line no-shadow, array-callback-return
|
||||
prev.map((script: Script) => {
|
||||
if (script.uuid === newScript.uuid) {
|
||||
script.name = newScript.name;
|
||||
@ -228,17 +227,19 @@ function ScriptEditor() {
|
||||
}
|
||||
return [...prev];
|
||||
});
|
||||
},
|
||||
(err: any) => {
|
||||
return newScript;
|
||||
})
|
||||
.catch((err: any) => {
|
||||
Message.error(`保存失败: ${err}`);
|
||||
}
|
||||
);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
Message.error(`错误的脚本代码: ${err}`);
|
||||
});
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
|
||||
const saveAs = (script: Script, e: editor.IStandaloneCodeEditor) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
chrome.downloads.download(
|
||||
@ -289,30 +290,35 @@ function ScriptEditor() {
|
||||
title: t("run"),
|
||||
items: [
|
||||
{
|
||||
id: "debug",
|
||||
title: t("debug"),
|
||||
id: "run",
|
||||
title: t("run"),
|
||||
hotKey: KeyMod.CtrlCmd | KeyCode.F5,
|
||||
hotKeyString: "Ctrl+F5",
|
||||
tooltip: "只有后台脚本/定时脚本才能调试, 且调试模式下不对进行权限校验(例如@connect)",
|
||||
tooltip: "只有后台脚本/定时脚本才能运行",
|
||||
action: async (script, e) => {
|
||||
// 保存更新代码之后再调试
|
||||
const newScript = await save(script, e);
|
||||
// 判断脚本类型
|
||||
if (newScript.type === SCRIPT_TYPE_NORMAL) {
|
||||
Message.error("只有后台脚本/定时脚本才能运行");
|
||||
return;
|
||||
}
|
||||
Message.loading({
|
||||
id: "debug_script",
|
||||
content: "正在准备脚本资源...",
|
||||
duration: 3000,
|
||||
});
|
||||
runtimeCtrl
|
||||
.debugScript(newScript)
|
||||
runtimeClient
|
||||
.runScript(newScript.uuid)
|
||||
.then(() => {
|
||||
Message.success({
|
||||
id: "debug_script",
|
||||
content: "构建成功, 可以打开开发者工具在控制台中查看输出",
|
||||
content: "构建成功, 可以在扩展页打开开发者工具在控制台中查看输出",
|
||||
duration: 3000,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
LoggerCore.logger(Logger.E(err)).debug("debug script error");
|
||||
LoggerCore.logger(Logger.E(err)).debug("run script error");
|
||||
Message.error({
|
||||
id: "debug_script",
|
||||
content: `构建失败: ${err}`,
|
||||
|
@ -17,6 +17,8 @@ import { useTranslation } from "react-i18next";
|
||||
import ScriptMenuList from "../components/ScriptMenuList";
|
||||
import { popupClient } from "../store/features/script";
|
||||
import { ScriptMenu } from "@App/app/service/service_worker/popup";
|
||||
import { systemConfig } from "../store/global";
|
||||
import { SystemConfig } from "@App/pkg/config/config";
|
||||
|
||||
const CollapseItem = Collapse.Item;
|
||||
|
||||
@ -30,9 +32,11 @@ function App() {
|
||||
const [scriptList, setScriptList] = useState<ScriptMenu[]>([]);
|
||||
const [backScriptList, setBackScriptList] = useState<ScriptMenu[]>([]);
|
||||
const [showAlert, setShowAlert] = useState(false);
|
||||
const [notice, setNotice] = useState("");
|
||||
const [isRead, setIsRead] = useState(true);
|
||||
const [version, setVersion] = useState(ExtVersion);
|
||||
const [checkUpdate, setCheckUpdate] = useState<Parameters<typeof systemConfig.setCheckUpdate>[0]>({
|
||||
version: ExtVersion,
|
||||
notice: "",
|
||||
isRead: false,
|
||||
});
|
||||
const [currentUrl, setCurrentUrl] = useState("");
|
||||
const [isEnableScript, setIsEnableScript] = useState(localStorage.enable_script !== "false");
|
||||
const { t } = useTranslation();
|
||||
@ -45,22 +49,15 @@ function App() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// systemManage.getNotice().then((res) => {
|
||||
// if (res) {
|
||||
// setNotice(res.notice);
|
||||
// setIsRead(res.isRead);
|
||||
// }
|
||||
// });
|
||||
// systemManage.getVersion().then((res) => {
|
||||
// res && setVersion(res);
|
||||
// });
|
||||
systemConfig.getCheckUpdate().then((res) => {
|
||||
setCheckUpdate(res);
|
||||
});
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (!tabs.length) {
|
||||
return;
|
||||
}
|
||||
setCurrentUrl(tabs[0].url || "");
|
||||
popupClient.getPopupData({ url: tabs[0].url!, tabId: tabs[0].id! }).then((resp) => {
|
||||
console.log(resp);
|
||||
// 按照开启状态和更新时间排序
|
||||
const list = resp.scriptList;
|
||||
list.sort((a, b) => {
|
||||
@ -109,15 +106,17 @@ function App() {
|
||||
window.open("/src/options.html", "_blank");
|
||||
}}
|
||||
/>
|
||||
<Badge count={isRead ? 0 : 1} dot offset={[-8, 6]}>
|
||||
<Badge count={checkUpdate.isRead ? 0 : 1} dot offset={[-8, 6]}>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<IconNotification />}
|
||||
iconOnly
|
||||
onClick={() => {
|
||||
setShowAlert(!showAlert);
|
||||
setIsRead(true);
|
||||
systemManage.setRead(true);
|
||||
console.log(checkUpdate);
|
||||
checkUpdate.isRead = true;
|
||||
setCheckUpdate(checkUpdate);
|
||||
systemConfig.setCheckUpdate(checkUpdate);
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
@ -179,9 +178,9 @@ function App() {
|
||||
bodyStyle={{ padding: 0 }}
|
||||
>
|
||||
<Alert
|
||||
style={{ marginBottom: 20, display: showAlert ? "flex" : "none" }}
|
||||
style={{ display: showAlert ? "flex" : "none" }}
|
||||
type="info"
|
||||
content={<div dangerouslySetInnerHTML={{ __html: notice }} />}
|
||||
content={<div dangerouslySetInnerHTML={{ __html: checkUpdate.notice || "" }} />}
|
||||
/>
|
||||
<Collapse bordered={false} defaultActiveKey={["script", "background"]} style={{ maxWidth: 640 }}>
|
||||
<CollapseItem
|
||||
@ -204,7 +203,7 @@ function App() {
|
||||
</Collapse>
|
||||
<div className="flex flex-row arco-card-header !h-6">
|
||||
<span className="text-[12px] font-500">{`v${ExtVersion}`}</span>
|
||||
{semver.lt(ExtVersion, version) && (
|
||||
{semver.lt(ExtVersion, checkUpdate.version) && (
|
||||
<span
|
||||
onClick={() => {
|
||||
window.open(`https://github.com/scriptscat/scriptcat/releases/tag/v${version}`);
|
||||
|
@ -5,6 +5,7 @@ import { FileSystemType } from "@Packages/filesystem/factory";
|
||||
import { MessageQueue } from "@Packages/message/message_queue";
|
||||
import i18n from "@App/locales/locales";
|
||||
import dayjs from "dayjs";
|
||||
import { ExtVersion } from "@App/app/const";
|
||||
|
||||
export const SystamConfigChange = "systemConfigChange";
|
||||
|
||||
@ -65,9 +66,7 @@ export class SystemConfig {
|
||||
|
||||
public set(key: string, val: any) {
|
||||
this.cache.set(key, val);
|
||||
this.storage.set(key, val).then(() => {
|
||||
console.log(chrome.runtime.lastError, val);
|
||||
});
|
||||
this.storage.set(key, val);
|
||||
// 发送消息通知更新
|
||||
this.mq.publish(SystamConfigChange, {
|
||||
key,
|
||||
@ -247,4 +246,20 @@ export class SystemConfig {
|
||||
i18n.changeLanguage(value);
|
||||
dayjs.locale(value.toLocaleLowerCase());
|
||||
}
|
||||
|
||||
setCheckUpdate(data: { notice: string; version: string; isRead: boolean }) {
|
||||
this.set("check_update", {
|
||||
notice: data.notice,
|
||||
version: data.version,
|
||||
isRead: data.isRead,
|
||||
});
|
||||
}
|
||||
|
||||
getCheckUpdate(): Promise<Parameters<typeof this.setCheckUpdate>[0]> {
|
||||
return this.get("check_update", {
|
||||
notice: "",
|
||||
isRead: false,
|
||||
version: ExtVersion,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -264,3 +264,15 @@ export function errorMsg(e: any): string {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function isUserScriptsAvailable() {
|
||||
return false;
|
||||
try {
|
||||
// Property access which throws if developer mode is not enabled.
|
||||
chrome.userScripts;
|
||||
return true;
|
||||
} catch {
|
||||
// Not available.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user