优化
Some checks failed
test / Run tests (push) Failing after 15s
build / Build (push) Failing after 23s
Some checks failed
test / Run tests (push) Failing after 15s
build / Build (push) Failing after 23s
This commit is contained in:
parent
116f614852
commit
1e8b5e6453
@ -19,7 +19,7 @@ export function connect(msg: Message, action: string, data?: any): Promise<Messa
|
|||||||
|
|
||||||
export class Client {
|
export class Client {
|
||||||
constructor(
|
constructor(
|
||||||
private msg: ExtensionMessageSend,
|
private msg: MessageSend,
|
||||||
private prefix: string
|
private prefix: string
|
||||||
) {
|
) {
|
||||||
if (!this.prefix.endsWith("/")) {
|
if (!this.prefix.endsWith("/")) {
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { Message, MessageConnect, MessageSend } from "./server";
|
import { Message, MessageConnect, MessageSend } from "./server";
|
||||||
|
|
||||||
export class ExtensionMessageSend implements MessageSend {
|
export class ExtensionMessageSend implements MessageSend {
|
||||||
|
constructor(private serverEnv: string) {
|
||||||
|
// 由于service_worker和offscren同时监听消息的话,都会同时收到,用serverEnv于区分不同的环墨
|
||||||
|
}
|
||||||
|
|
||||||
connect(data: any): Promise<MessageConnect> {
|
connect(data: any): Promise<MessageConnect> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const con = chrome.runtime.connect();
|
const con = chrome.runtime.connect();
|
||||||
@ -12,9 +16,14 @@ export class ExtensionMessageSend implements MessageSend {
|
|||||||
// 发送消息 注意不进行回调的内存泄漏
|
// 发送消息 注意不进行回调的内存泄漏
|
||||||
sendMessage(data: any): Promise<any> {
|
sendMessage(data: any): Promise<any> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
chrome.runtime.sendMessage(data, (resp) => {
|
chrome.runtime.sendMessage(
|
||||||
resolve(resp);
|
Object.assign(data, {
|
||||||
});
|
serverEnv: this.serverEnv,
|
||||||
|
}),
|
||||||
|
(resp) => {
|
||||||
|
resolve(resp);
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ export type MessageSender = {
|
|||||||
tabId: number;
|
tabId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiFunction = (params: any, con: MessageConnect | null) => any;
|
export type ApiFunction = (params: any, con: MessageConnect | null) => Promise<any> | void;
|
||||||
|
|
||||||
export class Server {
|
export class Server {
|
||||||
private apiFunctionMap: Map<string, ApiFunction> = new Map();
|
private apiFunctionMap: Map<string, ApiFunction> = new Map();
|
||||||
@ -32,10 +32,17 @@ export class Server {
|
|||||||
message: Message
|
message: Message
|
||||||
) {
|
) {
|
||||||
message.onConnect((msg: any, con: MessageConnect) => {
|
message.onConnect((msg: any, con: MessageConnect) => {
|
||||||
|
if (msg.serverEnv !== this.env) {
|
||||||
|
con.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.connectHandle(msg.action, msg.data, con);
|
this.connectHandle(msg.action, msg.data, con);
|
||||||
});
|
});
|
||||||
|
|
||||||
message.onMessage((msg, sendResponse) => {
|
message.onMessage((msg, sendResponse) => {
|
||||||
|
if (msg.serverEnv !== this.env) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return this.messageHandle(msg.action, msg.data, sendResponse);
|
return this.messageHandle(msg.action, msg.data, sendResponse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -100,7 +107,7 @@ export class Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 转发消息
|
// 转发消息
|
||||||
export function forwardMessage(path: string, from: Server, to: ExtensionMessageSend) {
|
export function forwardMessage(path: string, from: Server, to: MessageSend) {
|
||||||
from.on(path, (params, fromCon) => {
|
from.on(path, (params, fromCon) => {
|
||||||
if (fromCon) {
|
if (fromCon) {
|
||||||
to.connect({ action: path, data: params }).then((toCon) => {
|
to.connect({ action: path, data: params }).then((toCon) => {
|
||||||
|
123
src/app/cache.ts
123
src/app/cache.ts
@ -1,22 +1,109 @@
|
|||||||
|
export interface CacheStorage {
|
||||||
|
get(key: string): Promise<any>;
|
||||||
|
set(key: string, value: any): Promise<void>;
|
||||||
|
has(key: string): Promise<boolean>;
|
||||||
|
del(key: string): Promise<void>;
|
||||||
|
list(): Promise<string[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExtCache implements CacheStorage {
|
||||||
|
get(key: string): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
chrome.storage.local.get(key, (value) => {
|
||||||
|
resolve(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: string, value: any): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
chrome.storage.local.set(
|
||||||
|
{
|
||||||
|
[key]: value,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key: string): Promise<boolean> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
chrome.storage.local.get(key, (value) => {
|
||||||
|
resolve(value[key] !== undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
del(key: string): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
chrome.storage.local.remove(key, () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
list(): Promise<string[]> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
chrome.storage.local.get(null, (value) => {
|
||||||
|
resolve(Object.keys(value));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MapCache {
|
||||||
|
private map: Map<string, any> = new Map();
|
||||||
|
|
||||||
|
get(key: string): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolve(this.map.get(key));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: string, value: any): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.map.set(key, value);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key: string): Promise<boolean> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolve(this.map.has(key));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
del(key: string): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.map.delete(key);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
list(): Promise<string[]> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolve(Array.from(this.map.keys()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class Cache {
|
export default class Cache {
|
||||||
static instance: Cache = new Cache();
|
static instance: Cache = new Cache(new ExtCache());
|
||||||
|
|
||||||
static getInstance(): Cache {
|
static getInstance(): Cache {
|
||||||
return Cache.instance;
|
return Cache.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
map: Map<string, unknown>;
|
private constructor(private storage: CacheStorage) {}
|
||||||
|
|
||||||
private constructor() {
|
public get(key: string): Promise<any> {
|
||||||
this.map = new Map<string, unknown>();
|
return this.storage.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(key: string): unknown {
|
public async getOrSet(key: string, set: () => Promise<any>): Promise<any> {
|
||||||
return this.map.get(key);
|
let ret = await this.get(key);
|
||||||
}
|
|
||||||
|
|
||||||
public async getOrSet(key: string, set: () => Promise<unknown>): Promise<unknown> {
|
|
||||||
let ret = this.get(key);
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = await set();
|
ret = await set();
|
||||||
this.set(key, ret);
|
this.set(key, ret);
|
||||||
@ -24,19 +111,19 @@ export default class Cache {
|
|||||||
return Promise.resolve(ret);
|
return Promise.resolve(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(key: string, value: unknown): void {
|
public set(key: string, value: any): Promise<void> {
|
||||||
this.map.set(key, value);
|
return this.storage.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public has(key: string): boolean {
|
public has(key: string): Promise<boolean> {
|
||||||
return this.map.has(key);
|
return this.storage.has(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public del(key: string): void {
|
public del(key: string): Promise<void> {
|
||||||
this.map.delete(key);
|
return this.storage.del(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public list(): string[] {
|
public list(): Promise<string[]> {
|
||||||
return Array.from(this.map.keys());
|
return this.storage.list();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
|
import { ConfirmParam } from "@App/runtime/service_worker/permission_verify";
|
||||||
|
|
||||||
export default class CacheKey {
|
export default class CacheKey {
|
||||||
// 加载脚本信息时的缓存
|
// 加载脚本信息时的缓存
|
||||||
static scriptInstallInfo(uuid: string): string {
|
static scriptInstallInfo(uuid: string): string {
|
||||||
return `scriptInfo:${uuid}`;
|
return `scriptInfo:${uuid}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static permissionConfirm(scriptUuid: string, confirm: ConfirmParam): string {
|
||||||
|
return `permission:${scriptUuid}:${confirm.permissionValue || ""}:${confirm.permission || ""}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { DAO, db } from "./dao";
|
import { Repo } from "./repo";
|
||||||
|
|
||||||
export interface Permission {
|
export interface Permission {
|
||||||
id: number;
|
uuid: string;
|
||||||
scriptId: number;
|
|
||||||
permission: string;
|
permission: string;
|
||||||
permissionValue: string;
|
permissionValue: string;
|
||||||
allow: boolean;
|
allow: boolean;
|
||||||
@ -10,11 +9,20 @@ export interface Permission {
|
|||||||
updatetime: number;
|
updatetime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PermissionDAO extends DAO<Permission> {
|
export class PermissionDAO extends Repo<Permission> {
|
||||||
public tableName = "permission";
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super("permission");
|
||||||
this.table = db.table(this.tableName);
|
}
|
||||||
|
|
||||||
|
key(model: Permission) {
|
||||||
|
return model.uuid + ":" + model.permission + ":" + model.permissionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
findByKey(uuid: string, permission: string, permissionValue: string) {
|
||||||
|
return this.get(uuid + ":" + permission + ":" + permissionValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(value: Permission) {
|
||||||
|
return super._save(this.key(value), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ export const SCRIPT_RUN_STATUS_RUNNING: SCRIPT_RUN_STATUS = "running";
|
|||||||
export const SCRIPT_RUN_STATUS_COMPLETE: SCRIPT_RUN_STATUS = "complete";
|
export const SCRIPT_RUN_STATUS_COMPLETE: SCRIPT_RUN_STATUS = "complete";
|
||||||
export const SCRIPT_RUN_STATUS_ERROR: SCRIPT_RUN_STATUS = "error";
|
export const SCRIPT_RUN_STATUS_ERROR: SCRIPT_RUN_STATUS = "error";
|
||||||
|
|
||||||
export type Metadata = { [key: string]: string[] };
|
export type Metadata = { [key: string]: string[] | undefined };
|
||||||
|
|
||||||
export type ConfigType = "text" | "checkbox" | "select" | "mult-select" | "number" | "textarea" | "time";
|
export type ConfigType = "text" | "checkbox" | "select" | "mult-select" | "number" | "textarea" | "time";
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ export class ScriptDAO extends Repo<Script> {
|
|||||||
return super._save(val.uuid, val);
|
return super._save(val.uuid, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAndCode(uuid: string): Promise<ScriptAndCode|undefined> {
|
getAndCode(uuid: string): Promise<ScriptAndCode | undefined> {
|
||||||
return Promise.all([this.get(uuid), this.scriptCodeDAO.get(uuid)]).then(([script, code]) => {
|
return Promise.all([this.get(uuid), this.scriptCodeDAO.get(uuid)]).then(([script, code]) => {
|
||||||
if (!script || !code) {
|
if (!script || !code) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
7
src/app/service/offscreen/gm_api.ts
Normal file
7
src/app/service/offscreen/gm_api.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Group } from "@Packages/message/server";
|
||||||
|
|
||||||
|
export class GMApi {
|
||||||
|
constructor(private group: Group) {}
|
||||||
|
|
||||||
|
init() {}
|
||||||
|
}
|
@ -1,15 +1,18 @@
|
|||||||
import { forwardMessage, Server } from "@Packages/message/server";
|
import { forwardMessage, Message, Server } from "@Packages/message/server";
|
||||||
import { ScriptService } from "./script";
|
import { ScriptService } from "./script";
|
||||||
import { Broker } from "@Packages/message/message_queue";
|
import { Broker } from "@Packages/message/message_queue";
|
||||||
import { Logger, LoggerDAO } from "@App/app/repo/logger";
|
import { Logger, LoggerDAO } from "@App/app/repo/logger";
|
||||||
import { WindowMessage } from "@Packages/message/window_message";
|
import { WindowMessage } from "@Packages/message/window_message";
|
||||||
import { ExtensionMessageSend } from "@Packages/message/extension_message";
|
import { ExtensionMessage } from "@Packages/message/extension_message";
|
||||||
import { ServiceWorkerClient } from "../service_worker/client";
|
import { ServiceWorkerClient } from "../service_worker/client";
|
||||||
import { sendMessage } from "@Packages/message/client";
|
import { sendMessage } from "@Packages/message/client";
|
||||||
|
import { GMApi } from "./gm_api";
|
||||||
|
|
||||||
// offscreen环境的管理器
|
// offscreen环境的管理器
|
||||||
export class OffscreenManager {
|
export class OffscreenManager {
|
||||||
private extensionMessage = new ExtensionMessageSend();
|
private extensionMessage: Message = new ExtensionMessage("service_worker");
|
||||||
|
|
||||||
|
private api: Server = new Server("offscreen", this.extensionMessage);
|
||||||
|
|
||||||
private windowMessage = new WindowMessage(window, sandbox);
|
private windowMessage = new WindowMessage(window, sandbox);
|
||||||
|
|
||||||
@ -42,6 +45,9 @@ export class OffscreenManager {
|
|||||||
script.init();
|
script.init();
|
||||||
// 转发gm api请求
|
// 转发gm api请求
|
||||||
forwardMessage("serviceWorker/runtime/gmApi", this.windowApi, this.extensionMessage);
|
forwardMessage("serviceWorker/runtime/gmApi", this.windowApi, this.extensionMessage);
|
||||||
|
// 处理gm请求
|
||||||
|
const gmApi = new GMApi(this.api.group("gmApi"));
|
||||||
|
gmApi.init();
|
||||||
|
|
||||||
// // 处理gm xhr请求
|
// // 处理gm xhr请求
|
||||||
// this.api.on("gmXhr", (data) => {
|
// this.api.on("gmXhr", (data) => {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
} from "../service_worker/client";
|
} from "../service_worker/client";
|
||||||
import { SCRIPT_STATUS_ENABLE, SCRIPT_TYPE_NORMAL } from "@App/app/repo/scripts";
|
import { SCRIPT_STATUS_ENABLE, SCRIPT_TYPE_NORMAL } from "@App/app/repo/scripts";
|
||||||
import { disableScript, enableScript } from "../sandbox/client";
|
import { disableScript, enableScript } from "../sandbox/client";
|
||||||
import { ExtensionMessageSend } from "@Packages/message/extension_message";
|
import { MessageSend } from "@Packages/message/server";
|
||||||
|
|
||||||
export class ScriptService {
|
export class ScriptService {
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
@ -21,7 +21,7 @@ export class ScriptService {
|
|||||||
valueClient: ValueClient = new ValueClient(this.extensionMessage);
|
valueClient: ValueClient = new ValueClient(this.extensionMessage);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private extensionMessage: ExtensionMessageSend,
|
private extensionMessage: MessageSend,
|
||||||
private windowMessage: WindowMessage,
|
private windowMessage: WindowMessage,
|
||||||
private broker: Broker
|
private broker: Broker
|
||||||
) {
|
) {
|
||||||
|
@ -3,10 +3,10 @@ import { Client } from "@Packages/message/client";
|
|||||||
import { InstallSource } from ".";
|
import { InstallSource } from ".";
|
||||||
import { Broker } from "@Packages/message/message_queue";
|
import { Broker } from "@Packages/message/message_queue";
|
||||||
import { Resource } from "@App/app/repo/resource";
|
import { Resource } from "@App/app/repo/resource";
|
||||||
import { ExtensionMessageSend } from "@Packages/message/extension_message";
|
import { MessageSend } from "@Packages/message/server";
|
||||||
|
|
||||||
export class ServiceWorkerClient extends Client {
|
export class ServiceWorkerClient extends Client {
|
||||||
constructor(msg: ExtensionMessageSend) {
|
constructor(msg: MessageSend) {
|
||||||
super(msg, "serviceWorker");
|
super(msg, "serviceWorker");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export class ServiceWorkerClient extends Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ScriptClient extends Client {
|
export class ScriptClient extends Client {
|
||||||
constructor(msg: ExtensionMessageSend) {
|
constructor(msg: MessageSend) {
|
||||||
super(msg, "serviceWorker/script");
|
super(msg, "serviceWorker/script");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export class ScriptClient extends Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ResourceClient extends Client {
|
export class ResourceClient extends Client {
|
||||||
constructor(msg: ExtensionMessageSend) {
|
constructor(msg: MessageSend) {
|
||||||
super(msg, "serviceWorker/resource");
|
super(msg, "serviceWorker/resource");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ export class ResourceClient extends Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ValueClient extends Client {
|
export class ValueClient extends Client {
|
||||||
constructor(msg: ExtensionMessageSend) {
|
constructor(msg: MessageSend) {
|
||||||
super(msg, "serviceWorker/value");
|
super(msg, "serviceWorker/value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
87
src/app/service/service_worker/gm_api.ts
Normal file
87
src/app/service/service_worker/gm_api.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import LoggerCore from "@App/app/logger/core";
|
||||||
|
import Logger from "@App/app/logger/logger";
|
||||||
|
import { Script, ScriptDAO } from "@App/app/repo/scripts";
|
||||||
|
import { Group, MessageConnect, MessageSender } from "@Packages/message/server";
|
||||||
|
import { ValueService } from "@App/app/service/service_worker/value";
|
||||||
|
import PermissionVerify from "./permission_verify";
|
||||||
|
|
||||||
|
// GMApi,处理脚本的GM API调用请求
|
||||||
|
|
||||||
|
export type MessageRequest = {
|
||||||
|
uuid: string; // 脚本id
|
||||||
|
api: string;
|
||||||
|
runFlag: string;
|
||||||
|
params: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Request = MessageRequest & {
|
||||||
|
script: Script;
|
||||||
|
sender: MessageSender;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Api = (request: Request, con: MessageConnect | null) => Promise<any>;
|
||||||
|
|
||||||
|
export default class GMApi {
|
||||||
|
logger: Logger;
|
||||||
|
|
||||||
|
scriptDAO: ScriptDAO = new ScriptDAO();
|
||||||
|
|
||||||
|
permissionVerify: PermissionVerify = new PermissionVerify();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private group: Group,
|
||||||
|
private value: ValueService
|
||||||
|
) {
|
||||||
|
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
|
||||||
|
}
|
||||||
|
|
||||||
|
async handlerRequest(data: MessageRequest, con: MessageConnect | null) {
|
||||||
|
this.logger.trace("GM API request", { api: data.api, uuid: data.uuid, param: data.params });
|
||||||
|
const api = PermissionVerify.apis.get(data.api);
|
||||||
|
if (!api) {
|
||||||
|
return Promise.reject(new Error("api is not found"));
|
||||||
|
}
|
||||||
|
const req = await this.parseRequest(data, { tabId: 0 });
|
||||||
|
try {
|
||||||
|
await this.permissionVerify.verify(req, api);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error("verify error", { api: data.api }, Logger.E(e));
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
return api.api.call(this, req, con);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求
|
||||||
|
async parseRequest(data: MessageRequest, sender: MessageSender): Promise<Request> {
|
||||||
|
const script = await this.scriptDAO.get(data.uuid);
|
||||||
|
if (!script) {
|
||||||
|
return Promise.reject(new Error("script is not found"));
|
||||||
|
}
|
||||||
|
const req: Request = <Request>data;
|
||||||
|
req.script = script;
|
||||||
|
req.sender = sender;
|
||||||
|
return Promise.resolve(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PermissionVerify.API()
|
||||||
|
GM_setValue(request: Request): Promise<any> {
|
||||||
|
if (!request.params || request.params.length !== 2) {
|
||||||
|
return Promise.reject(new Error("param is failed"));
|
||||||
|
}
|
||||||
|
const [key, value] = request.params;
|
||||||
|
const sender = <MessageSender & { runFlag: string }>request.sender;
|
||||||
|
sender.runFlag = request.runFlag;
|
||||||
|
return this.value.setValue(request.script.uuid, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PermissionVerify.API()
|
||||||
|
GM_xmlhttpRequest(request: Request, con: MessageConnect) {
|
||||||
|
console.log("xml", request, con);
|
||||||
|
// 先处理unsafe hearder
|
||||||
|
// 再发送到offscreen, 处理请求
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.group.on("gmApi", this.handlerRequest.bind(this));
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ export type InstallSource = "user" | "system" | "sync" | "subscribe" | "vscode";
|
|||||||
export default class ServiceWorkerManager {
|
export default class ServiceWorkerManager {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
private api: Server = new Server("service_worker", new ExtensionMessage());
|
private api: Server = new Server("service_worker", new ExtensionMessage("service_worker"));
|
||||||
|
|
||||||
private mq: MessageQueue = new MessageQueue(this.api);
|
private mq: MessageQueue = new MessageQueue(this.api);
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ export default class ServiceWorkerManager {
|
|||||||
// },
|
// },
|
||||||
// condition: {
|
// condition: {
|
||||||
// resourceTypes: [chrome.declarativeNetRequest.ResourceType.XMLHTTPREQUEST],
|
// resourceTypes: [chrome.declarativeNetRequest.ResourceType.XMLHTTPREQUEST],
|
||||||
// urlFilter: "https://scriptcat.org/zh-CN",
|
// urlFilter: "^https://scriptcat.org/zh-CN$",
|
||||||
// excludedTabIds: excludedTabIds,
|
// excludedTabIds: excludedTabIds,
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
// gm api 权限验证
|
// gm api 权限验证
|
||||||
import Cache from "@App/app/cache";
|
import Cache from "@App/app/cache";
|
||||||
import { Permission, PermissionDAO } from "@App/app/repo/permission";
|
|
||||||
import { Script } from "@App/app/repo/scripts";
|
import { Script } from "@App/app/repo/scripts";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { Api, Request } from "./gm_api";
|
import { Api, Request } from "./gm_api";
|
||||||
import Queue from "@App/pkg/utils/queue";
|
import Queue from "@App/pkg/utils/queue";
|
||||||
|
import CacheKey from "@App/app/cache_key";
|
||||||
|
import { Permission, PermissionDAO } from "@App/app/repo/permission";
|
||||||
|
|
||||||
export interface ConfirmParam {
|
export interface ConfirmParam {
|
||||||
// 权限名
|
// 权限名
|
||||||
@ -91,8 +92,6 @@ export default class PermissionVerify {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
permissionDAO: PermissionDAO;
|
|
||||||
|
|
||||||
// 确认队列
|
// 确认队列
|
||||||
confirmQueue: Queue<{
|
confirmQueue: Queue<{
|
||||||
request: Request;
|
request: Request;
|
||||||
@ -101,108 +100,19 @@ export default class PermissionVerify {
|
|||||||
reject: (reason: any) => void;
|
reject: (reason: any) => void;
|
||||||
}> = new Queue();
|
}> = new Queue();
|
||||||
|
|
||||||
removePermissionCache(scriptId: number) {
|
async removePermissionCache(scriptId: number) {
|
||||||
// 先删除缓存
|
// 先删除缓存
|
||||||
Cache.getInstance()
|
(await Cache.getInstance().list()).forEach((key) => {
|
||||||
.list()
|
if (key.startsWith(`permission:${scriptId.toString()}:`)) {
|
||||||
.forEach((key) => {
|
Cache.getInstance().del(key);
|
||||||
if (key.startsWith(`permission:${scriptId.toString()}:`)) {
|
}
|
||||||
Cache.getInstance().del(key);
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private permissionDAO: PermissionDAO;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.permissionDAO = new PermissionDAO();
|
this.permissionDAO = new PermissionDAO();
|
||||||
// 监听用户确认消息
|
|
||||||
const message = <MessageHander>IoC.instance(MessageHander);
|
|
||||||
message.setHandler("permissionConfirm", (_action, data: { uuid: string; userConfirm: UserConfirm }) => {
|
|
||||||
const confirm = this.confirmMap.get(data.uuid);
|
|
||||||
if (!confirm) {
|
|
||||||
if (data.userConfirm.type === 0) {
|
|
||||||
// 忽略
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
return Promise.reject(new Error("confirm not found"));
|
|
||||||
}
|
|
||||||
this.confirmMap.delete(data.uuid);
|
|
||||||
confirm.resolve(data.userConfirm);
|
|
||||||
return Promise.resolve(true);
|
|
||||||
});
|
|
||||||
// 监听获取用户确认消息
|
|
||||||
message.setHandler("getConfirm", (_action, uuid: string) => {
|
|
||||||
const data = this.confirmMap.get(uuid);
|
|
||||||
if (!data) {
|
|
||||||
return Promise.reject(new Error("uuid not found"));
|
|
||||||
}
|
|
||||||
// 查询允许统配的有多少个相同等待确认权限
|
|
||||||
let likeNum = 0;
|
|
||||||
if (data.confirm.wildcard) {
|
|
||||||
this.confirmQueue.list.forEach((value) => {
|
|
||||||
const confirm = value.confirm as ConfirmParam;
|
|
||||||
if (
|
|
||||||
confirm.wildcard &&
|
|
||||||
value.request.scriptId === data.script.id &&
|
|
||||||
confirm.permission === data.confirm.permission
|
|
||||||
) {
|
|
||||||
likeNum += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Promise.resolve({
|
|
||||||
script: data.script,
|
|
||||||
confirm: data.confirm,
|
|
||||||
likeNum,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// 监听删除权限
|
|
||||||
message.setHandler("deletePermission", async (_action, data: { scriptId: number; confirm: ConfirmParam }) => {
|
|
||||||
// 先删除缓存
|
|
||||||
this.removePermissionCache(data.scriptId);
|
|
||||||
// 再删除数据库
|
|
||||||
const m = await this.permissionDAO.findOne({
|
|
||||||
scriptId: data.scriptId,
|
|
||||||
permission: data.confirm.permission,
|
|
||||||
permissionValue: data.confirm.permissionValue || "",
|
|
||||||
});
|
|
||||||
if (!m) {
|
|
||||||
return Promise.resolve(true);
|
|
||||||
}
|
|
||||||
await this.permissionDAO.delete(m.id);
|
|
||||||
return Promise.resolve(true);
|
|
||||||
});
|
|
||||||
// 监听添加权限
|
|
||||||
message.setHandler("addPermission", async (_action, data: { scriptId: number; permission: Permission }) => {
|
|
||||||
// 先删除缓存
|
|
||||||
this.removePermissionCache(data.scriptId);
|
|
||||||
// 从数据库中查询是否有此权限
|
|
||||||
const m = await this.permissionDAO.findOne({
|
|
||||||
scriptId: data.scriptId,
|
|
||||||
permission: data.permission.permission,
|
|
||||||
permissionValue: data.permission.permissionValue || "",
|
|
||||||
});
|
|
||||||
if (!m) {
|
|
||||||
// 没有添加
|
|
||||||
await this.permissionDAO.save(data.permission);
|
|
||||||
return Promise.resolve(true);
|
|
||||||
}
|
|
||||||
// 有则更新
|
|
||||||
data.permission.id = m.id;
|
|
||||||
data.permission.createtime = m.createtime;
|
|
||||||
data.permission.updatetime = new Date().getTime();
|
|
||||||
this.permissionDAO.update(m.id, data.permission);
|
|
||||||
return Promise.resolve(true);
|
|
||||||
});
|
|
||||||
// 监听重置权限
|
|
||||||
message.setHandler("resetPermission", async (_action, data: { scriptId: number }) => {
|
|
||||||
// 先删除缓存
|
|
||||||
this.removePermissionCache(data.scriptId);
|
|
||||||
// 从数据库中查询是否有此权限
|
|
||||||
await this.permissionDAO.delete({
|
|
||||||
scriptId: data.scriptId,
|
|
||||||
});
|
|
||||||
return Promise.resolve(true);
|
|
||||||
});
|
|
||||||
this.dealConfirmQueue();
|
this.dealConfirmQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,22 +176,14 @@ export default class PermissionVerify {
|
|||||||
if (typeof confirm === "boolean") {
|
if (typeof confirm === "boolean") {
|
||||||
return confirm;
|
return confirm;
|
||||||
}
|
}
|
||||||
const cacheKey = CacheKey.permissionConfirm(request.script.id, confirm);
|
const cacheKey = CacheKey.permissionConfirm(request.script.uuid, confirm);
|
||||||
// 从数据库中查询是否有此权限
|
// 从数据库中查询是否有此权限
|
||||||
const ret = await Cache.getInstance().getOrSet(cacheKey, async () => {
|
const ret = await Cache.getInstance().getOrSet(cacheKey, async () => {
|
||||||
let model = await this.permissionDAO.findOne({
|
let model = await this.permissionDAO.findByKey(request.uuid, confirm.permission, confirm.permissionValue || "");
|
||||||
scriptId: request.scriptId,
|
|
||||||
permission: confirm.permission,
|
|
||||||
permissionValue: confirm.permissionValue || "",
|
|
||||||
});
|
|
||||||
if (!model) {
|
if (!model) {
|
||||||
// 允许通配
|
// 允许通配
|
||||||
if (confirm.wildcard) {
|
if (confirm.wildcard) {
|
||||||
model = await this.permissionDAO.findOne({
|
model = await this.permissionDAO.findByKey(request.uuid, confirm.permission, confirm.permissionValue || "");
|
||||||
scriptId: request.scriptId,
|
|
||||||
permission: confirm.permission,
|
|
||||||
permissionValue: "*",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve(model);
|
return Promise.resolve(model);
|
||||||
@ -297,9 +199,8 @@ export default class PermissionVerify {
|
|||||||
// 没有权限,则弹出页面让用户进行确认
|
// 没有权限,则弹出页面让用户进行确认
|
||||||
const userConfirm = await this.confirmWindow(request.script, confirm);
|
const userConfirm = await this.confirmWindow(request.script, confirm);
|
||||||
// 成功存入数据库
|
// 成功存入数据库
|
||||||
const model = {
|
const model: Permission = {
|
||||||
id: 0,
|
uuid: request.uuid,
|
||||||
scriptId: request.scriptId,
|
|
||||||
permission: confirm.permission,
|
permission: confirm.permission,
|
||||||
permissionValue: "",
|
permissionValue: "",
|
||||||
allow: userConfirm.allow,
|
allow: userConfirm.allow,
|
||||||
@ -327,15 +228,11 @@ export default class PermissionVerify {
|
|||||||
}
|
}
|
||||||
// 总是 放入数据库
|
// 总是 放入数据库
|
||||||
if (userConfirm.type >= 4) {
|
if (userConfirm.type >= 4) {
|
||||||
const oldConfirm = await this.permissionDAO.findOne({
|
const oldConfirm = await this.permissionDAO.findByKey(request.uuid, model.permission, model.permissionValue);
|
||||||
scriptId: request.scriptId,
|
|
||||||
permission: model.permission,
|
|
||||||
permissionValue: model.permissionValue,
|
|
||||||
});
|
|
||||||
if (!oldConfirm) {
|
if (!oldConfirm) {
|
||||||
await this.permissionDAO.save(model);
|
await this.permissionDAO.save(model);
|
||||||
} else {
|
} else {
|
||||||
await this.permissionDAO.update(oldConfirm.id, model);
|
await this.permissionDAO.update(this.permissionDAO.key(model), model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (userConfirm.allow) {
|
if (userConfirm.allow) {
|
||||||
@ -357,27 +254,9 @@ export default class PermissionVerify {
|
|||||||
|
|
||||||
// 弹出窗口让用户进行确认
|
// 弹出窗口让用户进行确认
|
||||||
async confirmWindow(script: Script, confirm: ConfirmParam): Promise<UserConfirm> {
|
async confirmWindow(script: Script, confirm: ConfirmParam): Promise<UserConfirm> {
|
||||||
return new Promise((resolve, reject) => {
|
return Promise.resolve({
|
||||||
const uuid = uuidv4();
|
allow: true,
|
||||||
// 超时处理
|
type: 1,
|
||||||
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}`),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -52,11 +52,9 @@ export class RuntimeService {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 初始化gm api
|
// 启动gm api
|
||||||
const gmApi = new GMApi(this.value);
|
const gmApi = new GMApi(this.group, this.value);
|
||||||
gmApi.start();
|
gmApi.start();
|
||||||
// 处理请求
|
|
||||||
this.group.on("gmApi", gmApi.handlerRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registryPageScript(script: ScriptAndCode) {
|
registryPageScript(script: ScriptAndCode) {
|
||||||
|
@ -157,7 +157,7 @@ export class ScriptService {
|
|||||||
const logger = this.logger.with({
|
const logger = this.logger.with({
|
||||||
name: script.name,
|
name: script.name,
|
||||||
uuid: script.uuid,
|
uuid: script.uuid,
|
||||||
version: script.metadata.version[0],
|
version: script.metadata.version![0],
|
||||||
upsertBy,
|
upsertBy,
|
||||||
});
|
});
|
||||||
let update = false;
|
let update = false;
|
||||||
|
@ -99,7 +99,7 @@ const PermissionManager: React.FC<{
|
|||||||
if (permissionValue) {
|
if (permissionValue) {
|
||||||
permission.push({
|
permission.push({
|
||||||
id: 0,
|
id: 0,
|
||||||
scriptId: script.id,
|
uuid: script.id,
|
||||||
permission: permissionValue.permission,
|
permission: permissionValue.permission,
|
||||||
permissionValue: permissionValue.permissionValue,
|
permissionValue: permissionValue.permissionValue,
|
||||||
allow: permissionValue.allow,
|
allow: permissionValue.allow,
|
||||||
@ -157,7 +157,7 @@ const PermissionManager: React.FC<{
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPermissionValue({
|
setPermissionValue({
|
||||||
id: 0,
|
id: 0,
|
||||||
scriptId: script.id,
|
uuid: script.id,
|
||||||
permission: "cors",
|
permission: "cors",
|
||||||
permissionValue: "",
|
permissionValue: "",
|
||||||
allow: true,
|
allow: true,
|
||||||
|
@ -44,7 +44,7 @@ export default class ExecScript {
|
|||||||
// 构建脚本资源
|
// 构建脚本资源
|
||||||
this.scriptFunc = compileScript(this.scriptRes.code);
|
this.scriptFunc = compileScript(this.scriptRes.code);
|
||||||
const grantMap: { [key: string]: boolean } = {};
|
const grantMap: { [key: string]: boolean } = {};
|
||||||
scriptRes.metadata.grant.forEach((key) => {
|
scriptRes.metadata.grant?.forEach((key) => {
|
||||||
grantMap[key] = true;
|
grantMap[key] = true;
|
||||||
});
|
});
|
||||||
if (grantMap.none) {
|
if (grantMap.none) {
|
||||||
|
@ -65,6 +65,7 @@ export default class GMApi {
|
|||||||
return this.message.sendMessage({
|
return this.message.sendMessage({
|
||||||
action: "serviceWorker/runtime/gmApi",
|
action: "serviceWorker/runtime/gmApi",
|
||||||
data: {
|
data: {
|
||||||
|
uuid: this.scriptRes.uuid,
|
||||||
api,
|
api,
|
||||||
params,
|
params,
|
||||||
},
|
},
|
||||||
@ -76,6 +77,7 @@ export default class GMApi {
|
|||||||
return this.message.connect({
|
return this.message.connect({
|
||||||
action: "serviceWorker/runtime/gmApi",
|
action: "serviceWorker/runtime/gmApi",
|
||||||
data: {
|
data: {
|
||||||
|
uuid: this.scriptRes.uuid,
|
||||||
api,
|
api,
|
||||||
params,
|
params,
|
||||||
},
|
},
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import LoggerCore from "@App/app/logger/core";
|
|
||||||
import Logger from "@App/app/logger/logger";
|
|
||||||
import { Script } from "@App/app/repo/scripts";
|
|
||||||
import PermissionVerify from "./permission_verify";
|
|
||||||
import { MessageSender } from "@Packages/message/server";
|
|
||||||
import { ValueService } from "@App/app/service/service_worker/value";
|
|
||||||
|
|
||||||
// GMApi,处理脚本的GM API调用请求
|
|
||||||
|
|
||||||
export type MessageRequest = {
|
|
||||||
scriptId: number; // 脚本id
|
|
||||||
api: string;
|
|
||||||
runFlag: string;
|
|
||||||
params: any[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Request = MessageRequest & {
|
|
||||||
script: Script;
|
|
||||||
sender: MessageSender;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Api = (request: Request) => Promise<any>;
|
|
||||||
|
|
||||||
export default class GMApi {
|
|
||||||
logger: Logger;
|
|
||||||
|
|
||||||
constructor(private value: ValueService) {
|
|
||||||
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
|
|
||||||
}
|
|
||||||
|
|
||||||
handlerRequest(params: Request) {
|
|
||||||
console.log(params, arguments);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PermissionVerify.API()
|
|
||||||
GM_setValue(request: Request): Promise<any> {
|
|
||||||
if (!request.params || request.params.length !== 2) {
|
|
||||||
return Promise.reject(new Error("param is failed"));
|
|
||||||
}
|
|
||||||
const [key, value] = request.params;
|
|
||||||
const sender = <MessageSender & { runFlag: string }>request.sender;
|
|
||||||
sender.runFlag = request.runFlag;
|
|
||||||
return this.value.setValue(request.script.uuid, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PermissionVerify.API()
|
|
||||||
GM_xmlhttpRequest(request: Request) {
|
|
||||||
// 发送到offscreen, 处理请求
|
|
||||||
console.log(request, arguments);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {}
|
|
||||||
}
|
|
@ -184,10 +184,10 @@ export default class Runtime extends Manager {
|
|||||||
tabMap = new Map();
|
tabMap = new Map();
|
||||||
scriptMenu.set(senderId, tabMap);
|
scriptMenu.set(senderId, tabMap);
|
||||||
}
|
}
|
||||||
let menuArr = tabMap.get(request.scriptId);
|
let menuArr = tabMap.get(request.uuid);
|
||||||
if (!menuArr) {
|
if (!menuArr) {
|
||||||
menuArr = [];
|
menuArr = [];
|
||||||
tabMap.set(request.scriptId, menuArr);
|
tabMap.set(request.uuid, menuArr);
|
||||||
}
|
}
|
||||||
// 查询菜单是否已经存在
|
// 查询菜单是否已经存在
|
||||||
for (let i = 0; i < menuArr.length; i += 1) {
|
for (let i = 0; i < menuArr.length; i += 1) {
|
||||||
@ -212,7 +212,7 @@ export default class Runtime extends Manager {
|
|||||||
}
|
}
|
||||||
const tabMap = scriptMenu.get(senderId);
|
const tabMap = scriptMenu.get(senderId);
|
||||||
if (tabMap) {
|
if (tabMap) {
|
||||||
const menuArr = tabMap.get(request.scriptId);
|
const menuArr = tabMap.get(request.uuid);
|
||||||
if (menuArr) {
|
if (menuArr) {
|
||||||
// 从菜单数组中遍历删除
|
// 从菜单数组中遍历删除
|
||||||
for (let i = 0; i < menuArr.length; i += 1) {
|
for (let i = 0; i < menuArr.length; i += 1) {
|
||||||
@ -222,7 +222,7 @@ export default class Runtime extends Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (menuArr.length === 0) {
|
if (menuArr.length === 0) {
|
||||||
tabMap.delete(request.scriptId);
|
tabMap.delete(request.uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!tabMap.size) {
|
if (!tabMap.size) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user