Skip to content

Commit a3c2581

Browse files
cyfung1031CodFrm
andauthored
🐛 修正订阅脚本的静默更新与 connect 权限逻辑 (#1201)
* 修正 Subscribe 的 Script 的 静默更新 * lint * 🐛 修正订阅脚本的静默更新与 connect 权限逻辑 根据文档设计,订阅脚本应始终静默更新,不受「非重要变更静默更新脚本」 开关控制;订阅下脚本的 connect 权限完全由订阅的 connect 覆盖。 - subscribe.ts: 订阅本身更新去掉 toggle 依赖,始终静默(除非 connect 变化) - script.ts: 订阅下脚本更新始终静默,不检查 toggle 和 connect - gm_api.ts: 运行时 GM_xmlhttpRequest/GM_cookie 的 connect 权限使用订阅声明的覆盖 - utils.ts: 简化 checkSilenceUpdate,移除不再需要的 subscribeMetadata 参数 --------- Co-authored-by: 王一之 <yz@ggnb.top>
1 parent 9845603 commit a3c2581

6 files changed

Lines changed: 160 additions & 30 deletions

File tree

src/app/service/service_worker/gm_api/gm_api.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import LoggerCore from "@App/app/logger/core";
22
import Logger from "@App/app/logger/logger";
33
import { ScriptDAO } from "@App/app/repo/scripts";
4+
import { SubscribeDAO } from "@App/app/repo/subscribe";
45
import { type IGetSender, type Group, GetSenderType } from "@Packages/message/server";
56
import type { ExtMessageSender, MessageSend, TMessageCommAction } from "@Packages/message/types";
67
import { connect, sendMessage } from "@Packages/message/client";
@@ -236,6 +237,7 @@ export default class GMApi {
236237
logger: Logger;
237238

238239
scriptDAO: ScriptDAO = new ScriptDAO();
240+
subscribeDAO: SubscribeDAO = new SubscribeDAO();
239241

240242
constructor(
241243
private systemConfig: SystemConfig,
@@ -273,6 +275,13 @@ export default class GMApi {
273275
if (!script) {
274276
throw new Error("script is not found");
275277
}
278+
// 订阅脚本的 connect 使用订阅声明的 connect 覆盖脚本自身的
279+
if (script.subscribeUrl) {
280+
const subscribe = await this.subscribeDAO.get(script.subscribeUrl);
281+
if (subscribe?.metadata?.connect) {
282+
script.metadata = { ...script.metadata, connect: subscribe.metadata.connect };
283+
}
284+
}
276285
return { ...data, script } as GMApiRequest<T>;
277286
}
278287

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { describe, it, expect, beforeEach } from "vitest";
2+
import { ScriptDAO, SCRIPT_TYPE_NORMAL, SCRIPT_STATUS_ENABLE, SCRIPT_RUN_STATUS_COMPLETE } from "@App/app/repo/scripts";
3+
import type { Script } from "@App/app/repo/scripts";
4+
import { SubscribeDAO, SUBSCRIBE_STATUS_ENABLE } from "@App/app/repo/subscribe";
5+
import type { Subscribe } from "@App/app/repo/subscribe";
6+
import type { SCMetadata } from "@App/app/repo/metadata";
7+
import GMApi, { MockGMExternalDependencies } from "./gm_api";
8+
import { initTestEnv } from "@Tests/utils";
9+
import { MockMessage } from "@Packages/message/mock_message";
10+
import { Server } from "@Packages/message/server";
11+
import EventEmitter from "eventemitter3";
12+
import { MessageQueue } from "@Packages/message/message_queue";
13+
import { SystemConfig } from "@App/pkg/config/config";
14+
import PermissionVerify from "../permission_verify";
15+
16+
initTestEnv();
17+
18+
function makeScript(uuid: string, connect?: string[], subscribeUrl?: string): Script {
19+
const metadata: SCMetadata = {};
20+
if (connect) metadata.connect = connect;
21+
return {
22+
uuid,
23+
name: "test-script",
24+
namespace: "test",
25+
metadata,
26+
type: SCRIPT_TYPE_NORMAL,
27+
status: SCRIPT_STATUS_ENABLE,
28+
sort: 0,
29+
runStatus: SCRIPT_RUN_STATUS_COMPLETE,
30+
createtime: Date.now(),
31+
checktime: Date.now(),
32+
subscribeUrl,
33+
};
34+
}
35+
36+
function makeSubscribe(url: string, connect?: string[]): Subscribe {
37+
const metadata: SCMetadata = {};
38+
if (connect) metadata.connect = connect;
39+
return {
40+
url,
41+
name: "test-subscribe",
42+
code: "",
43+
author: "test",
44+
scripts: {},
45+
metadata,
46+
status: SUBSCRIBE_STATUS_ENABLE,
47+
createtime: Date.now(),
48+
checktime: Date.now(),
49+
};
50+
}
51+
52+
function createGMApi(): GMApi {
53+
const ee = new EventEmitter<string, any>();
54+
const message = new MockMessage(ee);
55+
const messageQueue = new MessageQueue();
56+
const systemConfig = new SystemConfig(messageQueue);
57+
const server = new Server("serviceWorker", message);
58+
const permissionVerify = new PermissionVerify(server.group("permissionVerify"), messageQueue);
59+
return new GMApi(
60+
systemConfig,
61+
permissionVerify,
62+
server.group("runtime"),
63+
message,
64+
messageQueue,
65+
{} as any,
66+
new MockGMExternalDependencies()
67+
);
68+
}
69+
70+
describe("parseRequest 订阅脚本 connect 覆盖", () => {
71+
let scriptDAO: ScriptDAO;
72+
let subscribeDAO: SubscribeDAO;
73+
let gmApi: GMApi;
74+
75+
beforeEach(() => {
76+
scriptDAO = new ScriptDAO();
77+
subscribeDAO = new SubscribeDAO();
78+
gmApi = createGMApi();
79+
});
80+
81+
it("订阅脚本的 connect 应被订阅的 connect 覆盖", async () => {
82+
const subscribeUrl = "https://example.com/test.sub.js";
83+
const script = makeScript("uuid-1", ["script-domain.com"], subscribeUrl);
84+
const subscribe = makeSubscribe(subscribeUrl, ["subscribe-domain.com", "api.example.com"]);
85+
86+
await scriptDAO.save(script);
87+
await subscribeDAO.save(subscribe);
88+
89+
const req = await gmApi.parseRequest({ uuid: "uuid-1", api: "test", runFlag: "", params: [] });
90+
// connect 应该被订阅的覆盖,而不是脚本自身的
91+
expect(req.script.metadata.connect).toEqual(["subscribe-domain.com", "api.example.com"]);
92+
});
93+
94+
it("普通脚本(无 subscribeUrl)的 connect 保持不变", async () => {
95+
const script = makeScript("uuid-2", ["my-domain.com"]);
96+
await scriptDAO.save(script);
97+
98+
const req = await gmApi.parseRequest({ uuid: "uuid-2", api: "test", runFlag: "", params: [] });
99+
expect(req.script.metadata.connect).toEqual(["my-domain.com"]);
100+
});
101+
102+
it("订阅不存在时脚本 connect 保持不变", async () => {
103+
const script = makeScript("uuid-3", ["my-domain.com"], "https://gone.com/deleted.sub.js");
104+
await scriptDAO.save(script);
105+
106+
const req = await gmApi.parseRequest({ uuid: "uuid-3", api: "test", runFlag: "", params: [] });
107+
expect(req.script.metadata.connect).toEqual(["my-domain.com"]);
108+
});
109+
110+
it("订阅没有声明 connect 时脚本 connect 保持不变", async () => {
111+
const subscribeUrl = "https://example.com/no-connect.sub.js";
112+
const script = makeScript("uuid-4", ["my-domain.com"], subscribeUrl);
113+
const subscribe = makeSubscribe(subscribeUrl); // 无 connect
114+
115+
await scriptDAO.save(script);
116+
await subscribeDAO.save(subscribe);
117+
118+
const req = await gmApi.parseRequest({ uuid: "uuid-4", api: "test", runFlag: "", params: [] });
119+
expect(req.script.metadata.connect).toEqual(["my-domain.com"]);
120+
});
121+
});

src/app/service/service_worker/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export default class ServiceWorkerManager {
9090
scriptDAO
9191
);
9292
synchronize.init();
93-
const subscribe = new SubscribeService(systemConfig, this.api.group("subscribe"), this.mq, script);
93+
const subscribe = new SubscribeService(this.api.group("subscribe"), this.mq, script);
9494
subscribe.init();
9595
const system = new SystemService(
9696
systemConfig,

src/app/service/service_worker/script.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -884,20 +884,24 @@ export class ScriptService {
884884
) {
885885
const upsertBy = options.source;
886886
const code = await fetchScriptBody(url);
887-
if (update && (await this.systemConfig.getSilenceUpdateScript())) {
887+
if (update) {
888888
try {
889889
const { oldScript, script } = await prepareScriptByCode(code, url, uuid);
890-
if (checkSilenceUpdate(oldScript!.metadata, script.metadata)) {
891-
logger?.info("silence update script");
892-
await this.installScript({
893-
script,
894-
code,
895-
upsertBy,
896-
});
890+
// 订阅脚本始终静默更新,信任关系由订阅建立
891+
if (oldScript?.subscribeUrl) {
892+
logger?.info("silence update subscribe script");
893+
await this.installScript({ script, code, upsertBy });
897894
return 2;
898895
}
899-
// 如果不符合静默更新规则,走后面的流程
900-
logger?.info("not silence update script, open install page");
896+
// 普通脚本:检查静默更新开关和 connect 变化
897+
if (await this.systemConfig.getSilenceUpdateScript()) {
898+
if (checkSilenceUpdate(oldScript!.metadata, script.metadata)) {
899+
logger?.info("silence update script");
900+
await this.installScript({ script, code, upsertBy });
901+
return 2;
902+
}
903+
logger?.info("not silence update script, open install page");
904+
}
901905
} catch (e) {
902906
logger?.error("prepare script failed", Logger.E(e));
903907
}

src/app/service/service_worker/subscribe.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import Logger from "@App/app/logger/logger";
33
import { ScriptDAO } from "@App/app/repo/scripts";
44
import type { SCMetadata, Subscribe, SubscribeScript } from "@App/app/repo/subscribe";
55
import { SUBSCRIBE_STATUS_DISABLE, SUBSCRIBE_STATUS_ENABLE, SubscribeDAO } from "@App/app/repo/subscribe";
6-
import { type SystemConfig } from "@App/pkg/config/config";
76
import { type IMessageQueue } from "@Packages/message/message_queue";
87
import { type Group } from "@Packages/message/server";
98
import { type ScriptService } from "./script";
@@ -24,7 +23,6 @@ export class SubscribeService {
2423
scriptDAO = new ScriptDAO();
2524

2625
constructor(
27-
private systemConfig: SystemConfig,
2826
private group: Group,
2927
private mq: IMessageQueue,
3028
private scriptService: ScriptService
@@ -239,25 +237,23 @@ export class SubscribeService {
239237
}
240238
}
241239

240+
// 订阅始终尝试静默更新,不受「非重要变更静默更新脚本」开关控制
241+
// 仅当订阅的 @connect 新增了域时才需要用户确认
242242
async trySilenceUpdate(code: string, url: string) {
243243
const logger = this.logger.with({
244244
url,
245245
});
246-
// 是否静默更新
247-
const silenceUpdate = await this.systemConfig.getSilenceUpdateScript();
248-
if (silenceUpdate) {
249-
try {
250-
const newSubscribe = await prepareSubscribeByCode(code, url);
251-
if (checkSilenceUpdate(newSubscribe.oldSubscribe!.metadata, newSubscribe.subscribe.metadata)) {
252-
logger.info("silence update subscribe");
253-
this.install({
254-
subscribe: newSubscribe.subscribe,
255-
});
256-
return true;
257-
}
258-
} catch (e) {
259-
logger.error("prepare script failed", Logger.E(e));
246+
try {
247+
const newSubscribe = await prepareSubscribeByCode(code, url);
248+
if (checkSilenceUpdate(newSubscribe.oldSubscribe!.metadata, newSubscribe.subscribe.metadata)) {
249+
logger.info("silence update subscribe");
250+
this.install({
251+
subscribe: newSubscribe.subscribe,
252+
});
253+
return true;
260254
}
255+
} catch (e) {
256+
logger.error("prepare script failed", Logger.E(e));
261257
}
262258
}
263259

src/pkg/utils/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ export async function openInCurrentTab(url: string, tabId?: number) {
144144
}
145145
}
146146

147-
// 检查订阅规则是否改变,是否能够静默更新
147+
// 检查 connect 是否改变,是否能够静默更新
148+
// 用于订阅本身的更新检查和普通脚本的静默更新检查
148149
export function checkSilenceUpdate(oldMeta: SCMetadata, newMeta: SCMetadata): boolean {
149-
// 判断connect是否改变
150150
const oldConnect = new Set<string>(oldMeta.connect || []);
151151
const newConnect = new Set<string>(newMeta.connect || []);
152-
// 老的里面没有新的就需要用户确认了
152+
// 新的 connect 中有老的没有的域,则需要用户确认
153153
for (const key of newConnect) {
154154
if (!oldConnect.has(key)) {
155155
return false;

0 commit comments

Comments
 (0)