From 96141b9d30caa78a8247432790e64f8f2c2b7331 Mon Sep 17 00:00:00 2001 From: VillanCh <188228725@qq.com> Date: Thu, 21 Mar 2024 09:23:52 +0800 Subject: [PATCH] add basic dev boostrap --- package-lock.json | 73 +++++++++++++++++++++++++++++ package.json | 1 + public/background.js | 15 ++++-- src/components/Controller.tsx | 86 +++++++++++++++++++++++++++++------ src/network/chrome.ts | 27 +++++++++++ src/network/chromeapi.ts | 74 ++++++++++++++++++++++++++++++ 6 files changed, 257 insertions(+), 19 deletions(-) create mode 100644 src/network/chrome.ts create mode 100644 src/network/chromeapi.ts diff --git a/package-lock.json b/package-lock.json index 1eee93a..fd30182 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "ahooks": "^3.7.10", "antd": "^5.15.3", "babel-jest": "^27.4.2", "babel-plugin-named-asset-import": "^0.3.8", @@ -5477,6 +5478,27 @@ "node": ">= 6.0.0" } }, + "node_modules/ahooks": { + "version": "3.7.10", + "resolved": "https://registry.npmjs.org/ahooks/-/ahooks-3.7.10.tgz", + "integrity": "sha512-/HLYif7sFA/5qSuWKrwvjDbf3bq+sdaMrUWS7XGCDRWdC2FrG/i+u5LZdakMYc6UIgJTMQ7tGiJCV7sdU4kSIw==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "dayjs": "^1.9.1", + "intersection-observer": "^0.12.0", + "js-cookie": "^2.x.x", + "lodash": "^4.17.21", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -10401,6 +10423,11 @@ "node": ">=10.13.0" } }, + "node_modules/intersection-observer": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz", + "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==" + }, "node_modules/ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -12998,6 +13025,11 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -17027,6 +17059,17 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/scroll-into-view-if-needed": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", @@ -23696,6 +23739,21 @@ "debug": "4" } }, + "ahooks": { + "version": "3.7.10", + "resolved": "https://registry.npmjs.org/ahooks/-/ahooks-3.7.10.tgz", + "integrity": "sha512-/HLYif7sFA/5qSuWKrwvjDbf3bq+sdaMrUWS7XGCDRWdC2FrG/i+u5LZdakMYc6UIgJTMQ7tGiJCV7sdU4kSIw==", + "requires": { + "@babel/runtime": "^7.21.0", + "dayjs": "^1.9.1", + "intersection-observer": "^0.12.0", + "js-cookie": "^2.x.x", + "lodash": "^4.17.21", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "tslib": "^2.4.1" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -27225,6 +27283,11 @@ "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true }, + "intersection-observer": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz", + "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==" + }, "ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -29064,6 +29127,11 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==" }, + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -31759,6 +31827,11 @@ "ajv-keywords": "^3.5.2" } }, + "screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==" + }, "scroll-into-view-if-needed": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", diff --git a/package.json b/package.json index 032b851..a0f001c 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "ahooks": "^3.7.10", "antd": "^5.15.3", "babel-jest": "^27.4.2", "babel-plugin-named-asset-import": "^0.3.8", diff --git a/public/background.js b/public/background.js index d57c301..d9fcbf8 100644 --- a/public/background.js +++ b/public/background.js @@ -40,15 +40,20 @@ heartbeat() setInterval(heartbeat, 3000) console.info("Chrome Extenstion Background is loaded") + chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) { - if (msg.action === "connectWebsocket") { + if (msg.action === "connect") { console.info("Start to connect websocket") - connectWebsocket(msg.url) - } else if (msg.action === "init") { + const host = msg['host'] || "127.0.0.1" + const port = msg['port'] || 11212 + connectWebsocket(`ws://${host}:${port}/?token=${"a"}`) + } else if (msg.action === 'disconnect') { + disconnectWebsocket() + } else if (msg.action === "status") { if (socket) { - chrome.runtime.sendMessage({status: "connected"}) + chrome.runtime.sendMessage({connected: true}) } else { - chrome.runtime.sendMessage({status: "initialized"}) + chrome.runtime.sendMessage({connected: false}) } } }) diff --git a/src/components/Controller.tsx b/src/components/Controller.tsx index 46ba233..1ed86ad 100644 --- a/src/components/Controller.tsx +++ b/src/components/Controller.tsx @@ -1,29 +1,87 @@ import React, {useEffect} from "react"; -import {Card} from "antd"; +import {Alert, Button, Card, Form, InputNumber} from "antd"; +import {wsc} from "../network/chrome"; +import {useGetState} from "ahooks"; export interface ControllerProp { } export const Controller: React.FC = (props) => { - const [connected, setConnected] = React.useState(false) - const ref = React.useRef(null) + const [findingPort, setFindingPort] = React.useState(false) + const [autoFindFailedReason, setAutoFindFailedReason] = React.useState("") + const [port, setPort] = React.useState() + + const [connected, setConnected, getConnected] = useGetState(false); + const [init, setInit] = React.useState(false) useEffect(() => { - if (!ref) { + // control status + const updateStatus = () => { + wsc.getStatus() + } + updateStatus() + const id = setInterval(updateStatus, 1000) + + wsc.listen((req: { connected: boolean }) => { + setConnected(req.connected) + setTimeout(() => { + setInit(true) + }, 500) + }) + return () => { + clearInterval(id) + } + }, []) + + useEffect(() => { + if (!init) { return } - // const update = () => { - // ref.current.click() - // } - // const id = setInterval(update, 10000) - // return () => { - // clearInterval(id) - // } - }, [ref]) + if (connected) { + return + } - return - Yakit 启动 + const findPort = (port: number, max: number) => { + const ws = new WebSocket(`ws://127.0.0.1:${port}`) + ws.onclose = (e: CloseEvent) => { + if (e.reason !== `FoundYakitWebSocketController` && (port + 1 <= max)) { + setTimeout(() => findPort(port + 1, max), 1000) + } + + if (port + 1 > max) { + setFindingPort(false) + setAutoFindFailedReason("Yakit 未启动或无法监测到 WebSocket Controller 端口") + } + } + ws.onopen = () => { + setFindingPort(false) + setPort(port) + ws.close(1000, "FoundYakitWebSocketController") + } + } + findPort(11212, 11222) + }, [connected, init]) + + const freeze = findingPort || (!init); + const safeConnected = connected && init; + + return + 断开链接 + : }> + {connected && init ? :
{ + e.preventDefault() + + wsc.connect(port) + }}> + {autoFindFailedReason !== '' ? : undefined} + + setPort(e)}/> + + + + + }
}; \ No newline at end of file diff --git a/src/network/chrome.ts b/src/network/chrome.ts new file mode 100644 index 0000000..0baf0ef --- /dev/null +++ b/src/network/chrome.ts @@ -0,0 +1,27 @@ +import {chrome} from "./chromeapi"; + +export namespace wsc { + export function connect(port: number, host?: string) { + chrome.runtime.sendMessage({ + action: 'connect', + host: host || '127.0.0.1', + port: port, + }); + } + + export function disconnect() { + chrome.runtime.sendMessage({ + action: 'disconnect', + }); + } + + export function getStatus() { + chrome.runtime.sendMessage({ + action: 'status', + }); + } + + export function listen(onMessage: (message: any) => void) { + chrome.runtime.onMessage.addListener(onMessage); + } +} diff --git a/src/network/chromeapi.ts b/src/network/chromeapi.ts new file mode 100644 index 0000000..1520102 --- /dev/null +++ b/src/network/chromeapi.ts @@ -0,0 +1,74 @@ +export interface MockMessageSender { + id?: string; +} + +export interface MockChromeRuntime { + sendMessage: (message: any, responseCallback?: (response: any) => void) => void; + onMessage: { + addListener: (callback: (message: any, sender: MockMessageSender, sendResponse: (response?: any) => void) => void) => void; + }; +} + +export interface MockChromeProxy { + settings: { + set: (value: { value: any, scope: string }) => void; + get: (details: { incognito?: boolean }, callback: (config: any) => void) => void; + }; +} + +export interface MockChromeTabs { + query: (queryInfo: any, callback: (result: any) => void) => void; + create: (createProperties: any, callback?: (tab: any) => void) => void; +} + +export interface MockChrome { + runtime: MockChromeRuntime; + proxy: MockChromeProxy; + tabs: MockChromeTabs; +} + +declare global { + interface Window { + chrome?: any; + } +} + +export const chrome: MockChrome = typeof window.chrome !== 'undefined' && window.chrome.runtime ? window.chrome : { + runtime: { + sendMessage: (message, responseCallback) => { + console.log('Mock sendMessage called with:', message); + if (responseCallback) responseCallback('response from mock'); + }, + onMessage: { + addListener: (callback) => { + console.log('Mock onMessage.addListener called'); + callback({connected: true}, undefined, undefined) + // You can simulate incoming messages here if needed + } + } + }, + proxy: { + settings: { + set: (value) => { + console.log('Mock proxy settings set with:', value); + }, + get: (details, callback) => { + console.log('Mock proxy settings get called'); + // Simulate proxy settings response + callback({mode: 'direct'}); + } + } + }, + tabs: { + query: (queryInfo, callback) => { + console.log('Mock tabs.query called with:', queryInfo); + // Simulate a tab query response + callback([{id: 1, url: 'http://example.com', title: 'Example'}]); + }, + create: (createProperties, callback) => { + console.log('Mock tabs.create called with:', createProperties); + // Simulate creating a tab + if (callback) callback({id: 2, url: createProperties.url}); + } + } +}; \ No newline at end of file