mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-22 03:10:43 +08:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b8d8c81b9 | ||
|
|
dd1b2a9513 | ||
|
|
cb65d57cef | ||
|
|
68ae019c7d | ||
|
|
65faff9e57 | ||
|
|
01f04e3e56 | ||
|
|
ed1005936b | ||
|
|
fffbb2a0a0 | ||
|
|
95bb29d216 | ||
|
|
7e1e9e2c79 | ||
|
|
f881ad6ce2 | ||
|
|
0090c60141 | ||
|
|
f2d360fae0 | ||
|
|
514006fef5 | ||
|
|
eaecc8eacd | ||
|
|
4daf96f637 | ||
|
|
98e585680a | ||
|
|
5ab87eec74 | ||
|
|
c6234ae79c | ||
|
|
fbe662c056 | ||
|
|
5424078d38 | ||
|
|
611d9543fe | ||
|
|
c763e28a34 | ||
|
|
9b0c89f1b1 | ||
|
|
3ab46d55c0 | ||
|
|
19015fae87 | ||
|
|
4cba932506 |
@@ -310,6 +310,9 @@ module.exports = function (webpackEnv) {
|
||||
.map(ext => `.${ext}`)
|
||||
.filter(ext => useTypeScript || !ext.includes('ts')),
|
||||
alias: {
|
||||
'@assets': path.resolve(__dirname, '../src/assets'),
|
||||
'@components': path.resolve(__dirname, '../src/components'),
|
||||
'@network': path.resolve(__dirname, '../src/network'),
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
|
||||
Generated
+135
-14137
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -9,7 +9,7 @@
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"ahooks": "^3.7.10",
|
||||
"antd": "^5.15.3",
|
||||
"antd": "^5.17.2",
|
||||
"babel-jest": "^27.4.2",
|
||||
"babel-plugin-named-asset-import": "^0.3.8",
|
||||
"babel-preset-react-app": "^10.0.1",
|
||||
@@ -17,6 +17,7 @@
|
||||
"browserslist": "^4.18.1",
|
||||
"camelcase": "^6.2.1",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||
"classnames": "^2.5.1",
|
||||
"css-minimizer-webpack-plugin": "^3.2.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
@@ -137,6 +138,7 @@
|
||||
"@babel/core": "^7.24.1",
|
||||
"@babel/preset-env": "^7.24.1",
|
||||
"@babel/preset-react": "^7.24.1",
|
||||
"@types/chrome": "^0.0.268",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
+78
-75
@@ -1,85 +1,88 @@
|
||||
let socket;
|
||||
const connectWebsocket = url => {
|
||||
disconnectWebsocket()
|
||||
socket = new WebSocket(url);
|
||||
socket.onopen = () => {
|
||||
chrome.runtime.sendMessage({status: "connected"})
|
||||
}
|
||||
socket.onclose = () => {
|
||||
chrome.runtime.sendMessage({status: "disconnected"})
|
||||
}
|
||||
}
|
||||
import {ActionType, WebSocketManager} from './socket.js';
|
||||
|
||||
const disconnectWebsocket = () => {
|
||||
if (socket) {
|
||||
try {
|
||||
socket.close()
|
||||
socket = null;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
heartbeat = () => {
|
||||
if (socket) {
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
try {
|
||||
socket.send(JSON.stringify({
|
||||
"type": "heartbeat",
|
||||
}))
|
||||
} catch (e) {
|
||||
console.error("Error sending heartbeat:", e);
|
||||
}
|
||||
} else {
|
||||
disconnectWebsocket()
|
||||
}
|
||||
}
|
||||
}
|
||||
heartbeat()
|
||||
setInterval(heartbeat, 3000)
|
||||
|
||||
console.info("Chrome Extenstion Background is loaded")
|
||||
|
||||
let proxyHost = "";
|
||||
const websocketManager = new WebSocketManager();
|
||||
|
||||
|
||||
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
|
||||
if (msg.action === "connect") {
|
||||
console.info("Start to connect websocket")
|
||||
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({connected: true})
|
||||
} else {
|
||||
chrome.runtime.sendMessage({connected: false})
|
||||
}
|
||||
} else if (msg.action === 'setproxy') {
|
||||
chrome.proxy.settings.set({
|
||||
value: {
|
||||
mode: "fixed_servers",
|
||||
rules: {
|
||||
singleProxy: {
|
||||
scheme: msg.scheme,
|
||||
host: msg.host,
|
||||
port: parseInt(`${msg.port}`)
|
||||
console.log("msg", msg)
|
||||
switch (msg.action) {
|
||||
case ActionType.CONNECT:
|
||||
console.info("Start to connect websocket")
|
||||
const host = msg['host'] || "127.0.0.1"
|
||||
const port = msg['port'] || 11212
|
||||
websocketManager.connectWebsocket(`ws://${host}:${port}/?token=${"a"}`, port)
|
||||
break;
|
||||
case ActionType.DISCONNECT:
|
||||
websocketManager.disconnectWebsocket();
|
||||
break;
|
||||
case ActionType.SET_PROXY:
|
||||
chrome.proxy.settings.set({
|
||||
value: {
|
||||
mode: "fixed_servers",
|
||||
rules: {
|
||||
singleProxy: {
|
||||
scheme: msg.scheme,
|
||||
host: msg.host,
|
||||
port: parseInt(`${msg.port}`)
|
||||
}
|
||||
}
|
||||
},
|
||||
scope: 'regular',
|
||||
});
|
||||
break;
|
||||
case ActionType.CLEAR_PROXY:
|
||||
chrome.proxy.settings.clear({})
|
||||
break;
|
||||
case ActionType.PROXY_STATUS:
|
||||
chrome.proxy.settings.get({}, function (details) {
|
||||
if (details.value && details.value.mode === "fixed_servers") {
|
||||
let proxyConfig = details.value.rules.singleProxy;
|
||||
chrome.runtime.sendMessage({
|
||||
enable: true,
|
||||
proxy: `${proxyConfig.scheme}://${proxyConfig.host}:${proxyConfig.port}`
|
||||
})
|
||||
} else {
|
||||
chrome.runtime.sendMessage({enable: false, proxy: ""})
|
||||
}
|
||||
},
|
||||
scope: 'regular',
|
||||
})
|
||||
proxyHost = `${msg.scheme}://${msg.host}:${msg.port}`
|
||||
} else if (msg.action === 'clearproxy') {
|
||||
chrome.proxy.settings.clear({})
|
||||
proxyHost = ""
|
||||
} else if (msg.action === 'proxystatus') {
|
||||
if (proxyHost !== '') {
|
||||
chrome.runtime.sendMessage({enable: !!proxyHost, proxy: proxyHost})
|
||||
} else {
|
||||
chrome.runtime.sendMessage({enable: false, proxy: ""})
|
||||
}
|
||||
});
|
||||
break;
|
||||
case ActionType.INJECT_SCRIPT:
|
||||
(async () => {
|
||||
try {
|
||||
// 注入 JS 脚本
|
||||
await chrome.scripting.executeScript({
|
||||
target: {tabId: msg.tabId},
|
||||
files: ['content.js']
|
||||
});
|
||||
|
||||
// 发送消息
|
||||
const response = await chrome.tabs.sendMessage(msg.tabId, {
|
||||
type: ActionType.INJECT_SCRIPT,
|
||||
value: msg.value
|
||||
});
|
||||
|
||||
console.log("response", response);
|
||||
if (response && response.action === ActionType.TO_EXTENSION_PAGE) {
|
||||
await chrome.runtime.sendMessage(response);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Script or CSS injection failed:', err);
|
||||
}
|
||||
})();
|
||||
break
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
const pageFunction = (code) => {
|
||||
chrome.runtime.sendMessage({code}, response => {
|
||||
if (response && response.success) {
|
||||
console.log('Result:', response.result);
|
||||
} else {
|
||||
console.error('Error:', response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
(() => {
|
||||
if (window.contentScriptInjected) {
|
||||
return;
|
||||
}
|
||||
window.contentScriptInjected = true;
|
||||
// 检查并插入 CSS 样式
|
||||
const styleId = 'injected-css-style';
|
||||
if (!document.getElementById(styleId)) {
|
||||
const style = document.createElement('style');
|
||||
style.id = styleId;
|
||||
style.textContent = `
|
||||
body {
|
||||
border: 3px solid red;
|
||||
position: relative; /* Ensure the body is positioned to allow the pseudo-element */
|
||||
}
|
||||
body::after {
|
||||
content: "Injection successful";
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: green;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
font-size: 16px;
|
||||
z-index: 1000;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.type === 'yakit_inject_script') {
|
||||
const injectedScriptURL = chrome.runtime.getURL('inject.js');
|
||||
const script = document.createElement('script');
|
||||
script.src = injectedScriptURL;
|
||||
script.onload = () => {
|
||||
window.postMessage({type: request.value.mode, value: request.value}, '*');
|
||||
script.remove();
|
||||
};
|
||||
(document.head || document.documentElement).appendChild(script);
|
||||
window.addEventListener('message', function onMessage(event) {
|
||||
if (event.source !== window || event.data.type !== 'FROM_INJECT_JS') {
|
||||
return;
|
||||
}
|
||||
window.removeEventListener('message', onMessage);
|
||||
// Send the result to the background script
|
||||
// chrome.runtime.sendMessage({ action: 'yakit_to_extension_page', result: event.data.result });
|
||||
// 直接向向发送端返回结果
|
||||
sendResponse({action: 'yakit_to_extension_page', result: event.data.result});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
})()
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
(() => {
|
||||
if (window.injectedMessageListener) {
|
||||
return;
|
||||
}
|
||||
window.injectedMessageListener = true;
|
||||
|
||||
window.addEventListener('message', function onMessage(event) {
|
||||
if (event.source !== window) {
|
||||
return;
|
||||
}
|
||||
let result
|
||||
switch (event.data.type) {
|
||||
case 'CONTENT_CALL_FUNCTION':
|
||||
const fn_name = event.data.value.fn_name;
|
||||
const args = event.data.value.args;
|
||||
result = window[fn_name](args);
|
||||
window.postMessage({type: 'FROM_INJECT_JS', result: result}, '*');
|
||||
break;
|
||||
case 'CONTENT_EVAL_CODE':
|
||||
const code = event.data.value.code;
|
||||
result = (() => {
|
||||
try {
|
||||
return eval(code);
|
||||
} catch (e) {
|
||||
// console.error("Error evaluating code:", e);
|
||||
return e.toString();
|
||||
}
|
||||
})();
|
||||
// console.log("CONTENT_EVAL_CODE result: ", result);
|
||||
window.postMessage({type: 'FROM_INJECT_JS', result: result}, '*');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
+11
-8
@@ -12,9 +12,11 @@
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
},
|
||||
"permissions": [
|
||||
"webNavigation",
|
||||
"activeTab",
|
||||
"scripting",
|
||||
"tabs",
|
||||
@@ -22,14 +24,15 @@
|
||||
"storage",
|
||||
"webRequest"
|
||||
],
|
||||
"content_scripts": [
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
],
|
||||
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"matches": [
|
||||
"<all_urls>"
|
||||
],
|
||||
"js": [
|
||||
"content.js"
|
||||
]
|
||||
"resources": ["inject.js"],
|
||||
"matches": ["<all_urls>"],
|
||||
"use_dynamic_url": true
|
||||
}
|
||||
],
|
||||
"icons": {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
export const ActionType = {
|
||||
CONNECT: 'connect',
|
||||
DISCONNECT: 'disconnect',
|
||||
STATUS: 'status',
|
||||
PROXY_STATUS: 'proxy_status',
|
||||
SET_PROXY: 'set_proxy',
|
||||
CLEAR_PROXY: 'clear_proxy',
|
||||
INJECT_SCRIPT: 'yakit_inject_script',
|
||||
TO_EXTENSION_PAGE: "yakit_to_extension_page",
|
||||
}
|
||||
|
||||
export class WebSocketManager {
|
||||
constructor() {
|
||||
this.socket = null;
|
||||
this.intervalId = null;
|
||||
}
|
||||
|
||||
connectWebsocket(url, port) {
|
||||
this.disconnectWebsocket();
|
||||
this.socket = new WebSocket(url);
|
||||
|
||||
this.socket.onopen = () => {
|
||||
chrome.runtime.sendMessage({action: ActionType.STATUS, connected: true, port: port});
|
||||
this.startHeartbeat();
|
||||
};
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
this.handleMessage(event.data);
|
||||
};
|
||||
|
||||
this.socket.onclose = () => {
|
||||
chrome.runtime.sendMessage({action: ActionType.STATUS, connected: false});
|
||||
};
|
||||
|
||||
this.socket.onerror = (error) => {
|
||||
console.error("WebSocket Error:", error);
|
||||
};
|
||||
}
|
||||
|
||||
disconnectWebsocket() {
|
||||
if (this.socket) {
|
||||
try {
|
||||
this.socket.close();
|
||||
chrome.runtime.sendMessage({action: ActionType.STATUS, connected: false});
|
||||
} catch (e) {
|
||||
console.error("Error closing websocket:", e);
|
||||
}
|
||||
this.socket = null;
|
||||
this.stopHeartbeat();
|
||||
}
|
||||
}
|
||||
|
||||
startHeartbeat() {
|
||||
this.intervalId = setInterval(() => this.heartbeat(), 3000);
|
||||
}
|
||||
|
||||
stopHeartbeat() {
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
}
|
||||
}
|
||||
|
||||
heartbeat() {
|
||||
if (this.isConnected()) {
|
||||
try {
|
||||
this.socket.send(JSON.stringify({"type": "heartbeat"}));
|
||||
} catch (e) {
|
||||
console.error("Error sending heartbeat:", e);
|
||||
}
|
||||
} else {
|
||||
this.disconnectWebsocket();
|
||||
}
|
||||
}
|
||||
|
||||
isConnected() {
|
||||
return this.socket && this.socket.readyState === WebSocket.OPEN;
|
||||
}
|
||||
|
||||
handleMessage(message) {
|
||||
console.log("message", message)
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -1,5 +1,10 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
width: 420px;
|
||||
border-radius: 0px 0px 4px 4px;
|
||||
border-right: 1px solid #EAECF3;
|
||||
border-bottom: 1px solid #EAECF3;
|
||||
border-left: 1px solid #EAECF3;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
@@ -32,7 +37,8 @@
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-15
@@ -1,21 +1,26 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import {Layout, Row} from 'antd';
|
||||
import {Controller} from "./components/Controller";
|
||||
import {Proxifier} from "./components/Proxifier";
|
||||
import React from "react";
|
||||
import "./App.css";
|
||||
import {ConfigProvider} from "antd";
|
||||
import {Contro} from "@components/Contro";
|
||||
import {Proxifier} from "@components/Proxifier";
|
||||
import {EvalInTab} from "@components/EvalInTab";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App" style={{width: 300, backgroundColor: "#eee", padding: 8}}>
|
||||
<Layout>
|
||||
<Row>
|
||||
<Controller/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Proxifier/>
|
||||
</Row>
|
||||
</Layout>
|
||||
</div>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
colorPrimary: "#F28B44",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="App">
|
||||
{/*<Contro/>*/}
|
||||
<Proxifier/>
|
||||
|
||||
<EvalInTab/>
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import Icon from "@ant-design/icons";
|
||||
import { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon";
|
||||
import React from "react";
|
||||
|
||||
interface IconProps extends CustomIconComponentProps {
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
ref?: any;
|
||||
}
|
||||
|
||||
const X = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M4 12L12 4M4 4L12 12"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
/**
|
||||
* @description Icon/Outline/x
|
||||
*/
|
||||
export const XIcon = (props: Partial<IconProps>) => {
|
||||
return <Icon component={X} {...props} />;
|
||||
};
|
||||
|
||||
const Check = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M3.33337 8.66669L6.00004 11.3334L12.6667 4.66669"
|
||||
stroke="#56C991"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
/**
|
||||
* @description Icon/Outline/check
|
||||
*/
|
||||
export const CheckIcon = (props: Partial<IconProps>) => {
|
||||
return <Icon component={Check} {...props} />;
|
||||
};
|
||||
|
||||
const PencilAlt = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M7.33329 3.33334H3.99996C3.26358 3.33334 2.66663 3.93029 2.66663 4.66667V12C2.66663 12.7364 3.26358 13.3333 3.99996 13.3333H11.3333C12.0697 13.3333 12.6666 12.7364 12.6666 12V8.66667M11.7238 2.39052C12.2445 1.86983 13.0887 1.86983 13.6094 2.39052C14.1301 2.91122 14.1301 3.75544 13.6094 4.27614L7.88557 10H5.99996L5.99996 8.11438L11.7238 2.39052Z"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
/**
|
||||
* @description Icon/Outline/pencil-alt
|
||||
*/
|
||||
export const PencilAltIcon = (props: Partial<IconProps>) => {
|
||||
return <Icon component={PencilAlt} {...props} />;
|
||||
};
|
||||
|
||||
const Refresh = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M2.66663 2.66669V6.00002H3.0543M13.292 7.33335C12.964 4.70248 10.7197 2.66669 7.99996 2.66669C5.76171 2.66669 3.84549 4.04547 3.0543 6.00002M3.0543 6.00002H5.99996M13.3333 13.3334V10H12.9456M12.9456 10C12.1544 11.9546 10.2382 13.3334 7.99996 13.3334C5.28021 13.3334 3.03595 11.2976 2.70789 8.66669M12.9456 10H9.99996"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
/**
|
||||
* @description Icon/Outline/refresh
|
||||
*/
|
||||
export const RefreshIcon = (props: Partial<IconProps>) => {
|
||||
return <Icon component={Refresh} {...props} />;
|
||||
};
|
||||
|
||||
const Exit = () => (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.33333 6.66668C9.10226 6.78107 8.87965 6.90988 8.66667 7.0519C7.05869 8.12418 6 9.95027 6 12.0227C6 15.3239 8.68629 18 12 18C15.3137 18 18 15.3239 18 12.0227C18 9.95027 16.9413 8.12418 15.3333 7.0519C15.1204 6.90988 14.8977 6.78107 14.6667 6.66668M12 5.33334V10.6667"
|
||||
stroke="#F7544A"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
/**
|
||||
* @description 退出
|
||||
*/
|
||||
export const ExitIcon = (props: Partial<IconProps>) => {
|
||||
return <Icon component={Exit} {...props} />;
|
||||
};
|
||||
|
||||
const PlusSm = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M8 4V8M8 8V12M8 8H12M8 8L4 8"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
/**
|
||||
* @description Icon/Outline/plus-sm
|
||||
*/
|
||||
export const PlusSmIcon = (props: Partial<IconProps>) => {
|
||||
return <Icon component={PlusSm} {...props} />;
|
||||
};
|
||||
|
||||
const Trash = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M12.6666 4.66667L12.0884 12.7617C12.0386 13.4594 11.458 14 10.7585 14H5.24145C4.54193 14 3.96135 13.4594 3.91151 12.7617L3.33329 4.66667M6.66663 7.33333V11.3333M9.33329 7.33333V11.3333M9.99996 4.66667V2.66667C9.99996 2.29848 9.70148 2 9.33329 2H6.66663C6.29844 2 5.99996 2.29848 5.99996 2.66667V4.66667M2.66663 4.66667H13.3333"
|
||||
stroke="#F7544A"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
/**
|
||||
* @description Icon/Outline/trash
|
||||
*/
|
||||
export const TrashIcon = (props: Partial<IconProps>) => {
|
||||
return <Icon component={Trash} {...props} />;
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
.Contro {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
padding: 8px 16px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.Contro-error-bg {
|
||||
background-color: rgba(244, 115, 107, .10);
|
||||
}
|
||||
|
||||
.Contro-success-bg {
|
||||
background-color: rgba(86, 201, 145, .10);
|
||||
}
|
||||
|
||||
.Contro-cont-input {
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.Contro-lable {
|
||||
color: #85899E;
|
||||
}
|
||||
|
||||
.Contro-cont {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.Contro-cont-text-error {
|
||||
color: #F6544A;
|
||||
}
|
||||
|
||||
.Contro-cont-text-success {
|
||||
color: #56C991;
|
||||
}
|
||||
|
||||
.Contro-handle-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Contro-handle-icon svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contro-handle-icon-check {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.icon-p {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.icon-p svg {
|
||||
color: #85899E;
|
||||
}
|
||||
|
||||
.Contro-cont span.anticon:hover {
|
||||
background: #F0F1F3;
|
||||
}
|
||||
|
||||
.grey-icon svg {
|
||||
color: #85899E;
|
||||
}
|
||||
|
||||
.icon-active:active svg {
|
||||
color: #F28B44;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import React, {useEffect, useMemo, useRef, useState} from "react";
|
||||
import {Divider, Tooltip, Input} from "antd";
|
||||
import classNames from "classnames";
|
||||
import {
|
||||
CheckIcon,
|
||||
ExitIcon,
|
||||
PencilAltIcon,
|
||||
RefreshIcon,
|
||||
XIcon,
|
||||
} from "@assets/icon/icon";
|
||||
import {wsc} from "@network/chrome";
|
||||
import "./Contro.css";
|
||||
|
||||
interface ControProps {
|
||||
}
|
||||
|
||||
export const Contro: React.FC<ControProps> = () => {
|
||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||
const [connected, setConnected] = useState(false);
|
||||
const [autoFindFailedReason, setAutoFindFailedReason] = useState<string>("");
|
||||
const [enginePort, setEnginePort] = useState<string>("");
|
||||
const [enginePortTemp, setEnginePortTemp] = useState<string>(enginePort);
|
||||
const enginePortRef = useRef<string>(enginePort);
|
||||
|
||||
useEffect(() => {
|
||||
enginePortRef.current = enginePort;
|
||||
}, [enginePort]);
|
||||
|
||||
useEffect(() => {
|
||||
const yakitConnectInfo = localStorage.getItem("yakit-connect");
|
||||
if (!yakitConnectInfo) {
|
||||
findPort(11212, 11222);
|
||||
} else {
|
||||
const {port, connected} = JSON.parse(yakitConnectInfo);
|
||||
setConnected(connected);
|
||||
setEnginePort(port + "");
|
||||
setAutoFindFailedReason("");
|
||||
}
|
||||
|
||||
wsc.onWSCMessage((message) => {
|
||||
if (message.action === wsc.ActionType.STATUS) {
|
||||
if (message.connected === false) {
|
||||
handleConnectFail();
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"yakit-connect",
|
||||
JSON.stringify({connected: true, port: message.port})
|
||||
);
|
||||
setEnginePort(message.port + "");
|
||||
setConnected(true);
|
||||
setAutoFindFailedReason("");
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const findPort = (port: number, max: number) => {
|
||||
const ws = new WebSocket(`ws://127.0.0.1:${port}`);
|
||||
ws.onclose = (e: CloseEvent) => {
|
||||
if (enginePortRef.current) {
|
||||
handleConnectFail();
|
||||
return;
|
||||
}
|
||||
if (e.reason !== `FoundYakitWebSocketController` && port + 1 <= max) {
|
||||
setTimeout(() => findPort(port + 1, max), 200);
|
||||
}
|
||||
|
||||
if (port + 1 > max) {
|
||||
setConnected(false);
|
||||
setEnginePort("");
|
||||
setAutoFindFailedReason("Cannot found Yakit");
|
||||
localStorage.setItem("yakit-connect", "");
|
||||
}
|
||||
};
|
||||
ws.onopen = () => {
|
||||
setConnected(true);
|
||||
setEnginePort(port + "");
|
||||
ws.close(1000, "FoundYakitWebSocketController");
|
||||
connectPort(port);
|
||||
};
|
||||
};
|
||||
|
||||
const handleConnectFail = () => {
|
||||
setConnected(false);
|
||||
setAutoFindFailedReason("Yakit WebSocket Controller Connect Fail");
|
||||
localStorage.setItem("yakit-connect", "");
|
||||
};
|
||||
|
||||
const connectPort = (port: number) => {
|
||||
setAutoFindFailedReason("");
|
||||
wsc.connect(port);
|
||||
};
|
||||
|
||||
const safeConnected = useMemo(() => {
|
||||
return connected && enginePort;
|
||||
}, [connected, enginePort]);
|
||||
|
||||
const failConnected = useMemo(() => {
|
||||
return !connected && autoFindFailedReason;
|
||||
}, [connected, autoFindFailedReason]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{safeConnected || failConnected ? (
|
||||
<div
|
||||
className={classNames("Contro", {
|
||||
["Contro-success-bg"]: safeConnected,
|
||||
["Contro-error-bg"]: failConnected,
|
||||
})}
|
||||
>
|
||||
<div className="Contro-lable">Yakit 引擎连接状态:</div>
|
||||
<div className="Contro-cont">
|
||||
{isEdit ? (
|
||||
<>
|
||||
<Input
|
||||
rootClassName="Contro-cont-input"
|
||||
value={enginePortTemp}
|
||||
placeholder="输入范围 11212 - 11222"
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setEnginePortTemp(value);
|
||||
}}
|
||||
/>
|
||||
<div className="Contro-handle-icon">
|
||||
<XIcon
|
||||
className="grey-icon icon-p icon-active"
|
||||
onClick={() => {
|
||||
setIsEdit(false);
|
||||
}}
|
||||
/>
|
||||
<CheckIcon
|
||||
className="contro-handle-icon-check icon-p"
|
||||
onClick={() => {
|
||||
if (enginePortTemp) {
|
||||
setIsEdit(false);
|
||||
setEnginePort(enginePortTemp);
|
||||
connectPort(Number(enginePortTemp));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className={classNames("Contro-cont-text", {
|
||||
["Contro-cont-text-success"]: safeConnected,
|
||||
["Contro-cont-text-error"]: failConnected,
|
||||
})}
|
||||
>
|
||||
{safeConnected && "已连接(" + enginePort + ")"}
|
||||
{failConnected &&
|
||||
(enginePort
|
||||
? autoFindFailedReason + "(" + enginePort + ")"
|
||||
: autoFindFailedReason)}
|
||||
</div>
|
||||
<div className="Contro-handle-icon">
|
||||
<Tooltip title="修改监听端口">
|
||||
<PencilAltIcon
|
||||
className="grey-icon icon-p icon-active"
|
||||
onClick={() => {
|
||||
setIsEdit(true);
|
||||
setEnginePortTemp(enginePort);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Divider type="vertical" style={{height: 16}}/>
|
||||
{safeConnected && (
|
||||
<ExitIcon onClick={() => wsc.disconnect()}/>
|
||||
)}
|
||||
{failConnected && (
|
||||
<RefreshIcon
|
||||
className="grey-icon icon-active"
|
||||
onClick={() => {
|
||||
if (enginePort) {
|
||||
connectPort(Number(enginePort));
|
||||
} else {
|
||||
findPort(11212, 11222);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
import React, {useEffect} from "react";
|
||||
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<ControllerProp> = (props) => {
|
||||
const [findingPort, setFindingPort] = React.useState<boolean>(false)
|
||||
const [autoFindFailedReason, setAutoFindFailedReason] = React.useState<string>("")
|
||||
const [port, setPort] = React.useState<number>()
|
||||
|
||||
const [connected, setConnected, getConnected] = useGetState(false);
|
||||
const [init, setInit] = React.useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
// control status
|
||||
const updateStatus = () => {
|
||||
wsc.updateWSCStatus()
|
||||
}
|
||||
updateStatus()
|
||||
const id = setInterval(updateStatus, 500)
|
||||
|
||||
wsc.onWSCMessage((req: { connected: boolean }) => {
|
||||
if (req['connected'] === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
setConnected(req.connected)
|
||||
setTimeout(() => {
|
||||
setInit(true)
|
||||
}, 500)
|
||||
})
|
||||
return () => {
|
||||
clearInterval(id)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!init || connected) {
|
||||
return
|
||||
}
|
||||
|
||||
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), 300)
|
||||
}
|
||||
|
||||
if (port + 1 > max) {
|
||||
setFindingPort(false)
|
||||
setAutoFindFailedReason("Cannot found Yakit or Yakit WebSocket Controller Port is not right")
|
||||
}
|
||||
}
|
||||
ws.onopen = () => {
|
||||
setFindingPort(false)
|
||||
setPort(port)
|
||||
ws.close(1000, "FoundYakitWebSocketController")
|
||||
}
|
||||
}
|
||||
findPort(11212, 11222)
|
||||
}, [connected, init])
|
||||
|
||||
const freeze = findingPort || (!init);
|
||||
const safeConnected = connected && init;
|
||||
|
||||
return <Card size={"small"} extra={safeConnected ? <Button size={"small"} danger={true} onClick={() => {
|
||||
wsc.disconnect()
|
||||
}}>
|
||||
Disconnect
|
||||
</Button> : undefined}>
|
||||
{connected && init ?
|
||||
<Alert type={"success"} message={"Yakit Connected"}/> :
|
||||
<Form
|
||||
onSubmitCapture={e => {
|
||||
e.preventDefault()
|
||||
|
||||
wsc.connect(port)
|
||||
}}
|
||||
size={"small"}
|
||||
>
|
||||
{autoFindFailedReason !== '' ? <Alert type={"error"} message={autoFindFailedReason}/> : undefined}
|
||||
<Form.Item label={"Controller Port"}>
|
||||
<InputNumber disabled={freeze} value={port} onChange={e => setPort(e)}/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button size={"small"} type={"primary"} loading={freeze} htmlType={"submit"}>Connect to
|
||||
Yakit</Button>
|
||||
</Form.Item>
|
||||
</Form>}
|
||||
</Card>
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {Button, Input} from "antd";
|
||||
import TextArea from "antd/lib/input/TextArea";
|
||||
import {wsc} from "@network/chrome";
|
||||
|
||||
interface EvalInTabProps {
|
||||
}
|
||||
|
||||
export const EvalInTab: React.FC<EvalInTabProps> = () => {
|
||||
const [funcName, setFuncName] = useState("");
|
||||
const [inputArgsData, setInputArgsData] = useState("");
|
||||
const [code, setCode] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
wsc.onWSCMessage((message) => {
|
||||
if (message.action === wsc.ActionType.TO_EXTENSION_PAGE) {
|
||||
console.log("res:", message.result);
|
||||
alert("from content script: " + JSON.stringify(message.result));
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleClick = async () => {
|
||||
try {
|
||||
const [tab] = await wsc.getTab();
|
||||
await chrome.runtime.sendMessage({
|
||||
action: wsc.ActionType.INJECT_SCRIPT,
|
||||
tabId: tab.id,
|
||||
value: {mode: "CONTENT_CALL_FUNCTION", fn_name: funcName, args: inputArgsData},
|
||||
});
|
||||
} catch (error) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setInputArgsData(event.target.value);
|
||||
};
|
||||
|
||||
|
||||
const handleEvalCodeClick = async () => {
|
||||
try {
|
||||
const [tab] = await wsc.getTab();
|
||||
await chrome.runtime.sendMessage({
|
||||
action: wsc.ActionType.INJECT_SCRIPT,
|
||||
tabId: tab.id,
|
||||
value: {
|
||||
mode: "CONTENT_EVAL_CODE",
|
||||
code: code,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input
|
||||
value={funcName}
|
||||
onChange={(e) => setFuncName(e.target.value)}
|
||||
placeholder="Enter function name"
|
||||
></Input>
|
||||
<TextArea
|
||||
value={inputArgsData}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Enter function args"
|
||||
/>
|
||||
<Button onClick={handleClick}>eval func in tab</Button>
|
||||
|
||||
<TextArea
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
placeholder="Enter your expression (e.g., 22 * 33)"
|
||||
/>
|
||||
<Button onClick={handleEvalCodeClick}>eval code in tab</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
.Prox-title-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 24px 8px;
|
||||
}
|
||||
|
||||
.Prox-title-wrap-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Prox-title-wrap-left .prox-title {
|
||||
margin-right: 4px;
|
||||
color: #31343F;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.Prox-title-wrap-left .prox-number {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 16px;
|
||||
text-align: center;
|
||||
color: #85899E;
|
||||
border-radius: 8px;
|
||||
background: #F0F1F3;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.Prox-title-wrap-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Prox-title-wrap-right .Prox-add-text {
|
||||
margin-right: 4px;
|
||||
color: var(--yakit-primary);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.Prox-title-wrap-right .Prox-add-icon svg {
|
||||
color: var(--yakit-primary);
|
||||
}
|
||||
|
||||
.Prox-list-wrap {
|
||||
overflow-y: auto;
|
||||
max-height: 305px;
|
||||
padding: 16px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.add-list {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #EAECF3;
|
||||
border-radius: 4px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.add-list:hover {
|
||||
border: 1px solid var(--yakit-primary);
|
||||
}
|
||||
|
||||
.add-list .add-list-icon svg {
|
||||
color: #85899E;
|
||||
}
|
||||
|
||||
.Prox-list-wrap .add-list .add-list-text {
|
||||
margin-left: 4px;
|
||||
color: #31343F;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.Prox-list-wrap .Prox-list-item-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.Prox-list-wrap .Prox-list-item-wrap:hover {
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
|
||||
.Prox-list-item-space .ant-space-item .ant-space-compact {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.Prox-list-item-space .ant-space-item .ant-space-compact .ant-select-single {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.Prox-list-wrap .Prox-list-item-wrap:hover .proxy-list-del-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.proxy-list-del-icon {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.proxy-list-del-icon:hover {
|
||||
background: #F0F1F3;
|
||||
}
|
||||
+300
-83
@@ -1,93 +1,310 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {Button, Card, Form, Input, InputNumber, Select, Space} from "antd";
|
||||
import {wsc} from "../network/chrome";
|
||||
|
||||
export interface ProxifierProp {
|
||||
|
||||
}
|
||||
|
||||
const {Compact} = Space;
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Space, Select, Input, Switch } from "antd";
|
||||
import { PlusSmIcon, TrashIcon } from "@assets/icon/icon";
|
||||
import { wsc } from "@network/chrome";
|
||||
import "./Proxifier.css";
|
||||
|
||||
type Scheme = "http" | "socks5";
|
||||
interface ProxyConfig {
|
||||
scheme: "http" | "socks5";
|
||||
host: string;
|
||||
port: number;
|
||||
id: string;
|
||||
scheme: Scheme;
|
||||
host: string;
|
||||
port: string;
|
||||
hostStatus: "error" | "";
|
||||
portStatus: "error" | "";
|
||||
open: boolean;
|
||||
proxy: string;
|
||||
}
|
||||
|
||||
export const Proxifier: React.FC<ProxifierProp> = (props) => {
|
||||
const [config, setConfig] = useState<ProxyConfig>({
|
||||
scheme: "http",
|
||||
host: "127.0.0.1",
|
||||
port: 8083
|
||||
});
|
||||
const [init, setInit] = useState(false);
|
||||
const [proxyEnable, setProxyEnable] = useState(false);
|
||||
const [currentProxy, setCurrentProxy] = useState("");
|
||||
export interface ProxifierProps {}
|
||||
export const Proxifier: React.FC<ProxifierProps> = () => {
|
||||
const [proxyList, setProxyList] = useState<ProxyConfig[]>(() => {
|
||||
const storageProxyList = localStorage.getItem("yakit-proxy-list") || "[]";
|
||||
return JSON.parse(storageProxyList);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (init) {
|
||||
return
|
||||
useEffect(() => {
|
||||
localStorage.setItem("yakit-proxy-list", JSON.stringify(proxyList));
|
||||
}, [proxyList]);
|
||||
|
||||
const addNewProxyListItem = (
|
||||
scheme: Scheme,
|
||||
host: string,
|
||||
port: string,
|
||||
open: boolean,
|
||||
proxy: string
|
||||
) => {
|
||||
const proxyItem: ProxyConfig = {
|
||||
id: Math.random() + "",
|
||||
scheme: scheme as Scheme,
|
||||
host: host,
|
||||
port: port,
|
||||
hostStatus: "",
|
||||
portStatus: "",
|
||||
open: open,
|
||||
proxy: proxy,
|
||||
};
|
||||
return proxyItem;
|
||||
};
|
||||
|
||||
const parseUrl = (url: string) => {
|
||||
const regex = /^(.*?):\/\/(.*?):(\d+)/;
|
||||
const match = url.match(regex);
|
||||
if (match) {
|
||||
const scheme = match[1];
|
||||
const host = match[2];
|
||||
const port = match[3];
|
||||
return {
|
||||
scheme,
|
||||
host,
|
||||
port,
|
||||
};
|
||||
} else {
|
||||
return null; // 不匹配格式
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
wsc.updateProxyStatus();
|
||||
|
||||
wsc.onProxyStatusMessage((msg) => {
|
||||
if (msg.proxy === undefined || msg.enable === undefined) {
|
||||
return;
|
||||
}
|
||||
if (msg.proxy === "" || msg.enable === false) {
|
||||
if (proxyList.some((i) => i.open)) {
|
||||
const copyProxyList = [...proxyList];
|
||||
copyProxyList.forEach((i) => {
|
||||
i.open = false;
|
||||
});
|
||||
setProxyList(copyProxyList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
wsc.onProxyStatusMessage(msg => {
|
||||
if (msg['proxy'] === undefined || msg['enable'] === undefined) {
|
||||
return
|
||||
if (msg.proxy && msg.enable) {
|
||||
const copyProxyList = [...proxyList];
|
||||
let newProxyItem: ProxyConfig = undefined;
|
||||
if (!copyProxyList.length) {
|
||||
const urlObj = parseUrl(msg.proxy);
|
||||
if (urlObj) {
|
||||
newProxyItem = addNewProxyListItem(
|
||||
urlObj.scheme as Scheme,
|
||||
urlObj.host,
|
||||
urlObj.port,
|
||||
true,
|
||||
msg.proxy
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const proxyExist = copyProxyList.some((i) => i.proxy === msg.proxy);
|
||||
if (proxyExist) {
|
||||
const proxyOpen = copyProxyList.some(
|
||||
(i) => i.open && i.proxy === msg.proxy
|
||||
);
|
||||
if (!proxyOpen) {
|
||||
copyProxyList.forEach((i) => {
|
||||
i.open = false;
|
||||
});
|
||||
for (let i = 0; i < copyProxyList.length; i++) {
|
||||
if (copyProxyList[i].proxy === msg.proxy) {
|
||||
copyProxyList[i].open = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setInit(true)
|
||||
setProxyEnable(msg.enable)
|
||||
setCurrentProxy(msg.proxy)
|
||||
})
|
||||
}, [init])
|
||||
|
||||
// proxy
|
||||
useEffect(() => {
|
||||
const update = () => {
|
||||
wsc.updateProxyStatus()
|
||||
} else {
|
||||
copyProxyList.forEach((i) => {
|
||||
i.open = false;
|
||||
});
|
||||
const urlObj = parseUrl(msg.proxy);
|
||||
if (urlObj) {
|
||||
newProxyItem = addNewProxyListItem(
|
||||
urlObj.scheme as Scheme,
|
||||
urlObj.host,
|
||||
urlObj.port,
|
||||
true,
|
||||
msg.proxy
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
update()
|
||||
const id = setInterval(update, 500)
|
||||
return () => {
|
||||
clearInterval(id)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <Card title={`Proxy Set: ${proxyEnable ? currentProxy : "Non-Config"}`} size={"small"} extra={<>{
|
||||
proxyEnable ? <Button size={"small"} onClick={() => {
|
||||
wsc.clearproxy()
|
||||
}}>停用</Button> : undefined
|
||||
}</>}>
|
||||
<Form size={"small"} onSubmitCapture={e => {
|
||||
e.preventDefault()
|
||||
setInit(false)
|
||||
wsc.setproxy(config.scheme, config.host, config.port)
|
||||
}} disabled={!init || proxyEnable}>
|
||||
<Form.Item label={"Proxy Setting"}>
|
||||
<Compact>
|
||||
<Select
|
||||
style={{width: 86}}
|
||||
value={config.scheme}
|
||||
onChange={e => (setConfig({...config, scheme: e}))}
|
||||
>
|
||||
<Select.Option value={"http"}>HTTP</Select.Option>
|
||||
<Select.Option value={"socks5"}>Socks5</Select.Option>
|
||||
</Select>
|
||||
<Input
|
||||
style={{width: 100}}
|
||||
placeholder={"ProxyHost"}
|
||||
value={config.host}
|
||||
onChange={e => (setConfig({...config, host: e.target.value}))}/>
|
||||
<InputNumber
|
||||
style={{width: 65}}
|
||||
placeholder={"Port"}
|
||||
value={config.port}
|
||||
onChange={e => (setConfig({...config, port: e}))}
|
||||
/>
|
||||
</Compact>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type={"primary"} htmlType={"submit"} loading={!init}>Enable Proxy</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
};
|
||||
if (newProxyItem) {
|
||||
copyProxyList.unshift(newProxyItem);
|
||||
}
|
||||
setProxyList(copyProxyList);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const hostOnchange = (value: string, id: string) => {
|
||||
const ipPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
||||
const domainPattern = /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
const copyProxyList = structuredClone(proxyList);
|
||||
if (value === "" || ipPattern.test(value) || domainPattern.test(value)) {
|
||||
copyProxyList.forEach((i) => {
|
||||
if (i.id === id) {
|
||||
i.hostStatus = "";
|
||||
i.host = value;
|
||||
i.proxy = i.scheme + "://" + value + ":" + i.port;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
copyProxyList.forEach((i) => {
|
||||
if (i.id === id) {
|
||||
i.hostStatus = "error";
|
||||
i.host = value;
|
||||
i.proxy = i.scheme + "://" + value + ":" + i.port;
|
||||
}
|
||||
});
|
||||
}
|
||||
setProxyList(copyProxyList);
|
||||
};
|
||||
|
||||
const portOnchange = (value: string, id: string) => {
|
||||
const portNumber = parseInt(value, 10);
|
||||
const copyProxyList = structuredClone(proxyList);
|
||||
if (
|
||||
value === "" ||
|
||||
(/^\d+$/.test(value) && portNumber >= 0 && portNumber <= 65535)
|
||||
) {
|
||||
copyProxyList.forEach((i) => {
|
||||
if (i.id === id) {
|
||||
i.portStatus = "";
|
||||
const port = value === "" ? "" : portNumber + "";
|
||||
i.port = port;
|
||||
i.proxy = i.scheme + "://" + i.host + ":" + port;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
copyProxyList.forEach((i) => {
|
||||
if (i.id === id) {
|
||||
i.portStatus = "error";
|
||||
i.port = value;
|
||||
i.proxy = i.scheme + "://" + i.host + ":" + value;
|
||||
}
|
||||
});
|
||||
}
|
||||
setProxyList(copyProxyList);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="Prox">
|
||||
<div className="Prox-title-wrap">
|
||||
<div className="Prox-title-wrap-left">
|
||||
<span className="prox-title">设置代理</span>
|
||||
<span className="prox-number">{proxyList.length}</span>
|
||||
</div>
|
||||
<div
|
||||
className="Prox-title-wrap-right"
|
||||
onClick={() => {
|
||||
setProxyList([
|
||||
...proxyList,
|
||||
addNewProxyListItem("http", "", "", false, "http://"),
|
||||
]);
|
||||
}}
|
||||
>
|
||||
<span className="Prox-add-text">添加</span>
|
||||
<PlusSmIcon className="Prox-add-icon" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="Prox-list-wrap">
|
||||
{proxyList.length ? (
|
||||
proxyList.map((item) => (
|
||||
<div className="Prox-list-item-wrap" key={item.id}>
|
||||
<Space className="Prox-list-item-space">
|
||||
<Space.Compact>
|
||||
<Select
|
||||
value={item.scheme}
|
||||
style={{ width: 88 }}
|
||||
disabled={item.open}
|
||||
onChange={(value, option) => {
|
||||
const copyProxyList = structuredClone(proxyList);
|
||||
copyProxyList.forEach((i) => {
|
||||
if (i.id === item.id) {
|
||||
i.scheme = value;
|
||||
i.proxy = value + "://" + i.host + ":" + i.port;
|
||||
}
|
||||
});
|
||||
setProxyList(copyProxyList);
|
||||
}}
|
||||
>
|
||||
<Select.Option value="http">HTTP</Select.Option>
|
||||
<Select.Option value="socks5">Socks5</Select.Option>
|
||||
</Select>
|
||||
<Input
|
||||
value={item.host}
|
||||
style={{ width: 136 }}
|
||||
disabled={item.open}
|
||||
status={item.hostStatus}
|
||||
onChange={(e) => hostOnchange(e.target.value, item.id)}
|
||||
/>
|
||||
<Input
|
||||
value={item.port}
|
||||
style={{ width: 64 }}
|
||||
disabled={item.open}
|
||||
status={item.portStatus}
|
||||
onChange={(e) => portOnchange(e.target.value, item.id)}
|
||||
/>
|
||||
</Space.Compact>
|
||||
</Space>
|
||||
{!item.open && (
|
||||
<TrashIcon
|
||||
className="proxy-list-del-icon"
|
||||
onClick={() => {
|
||||
setProxyList(proxyList.filter((i) => i.id !== item.id));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Switch
|
||||
checkedChildren="启"
|
||||
unCheckedChildren="停"
|
||||
value={item.open}
|
||||
disabled={
|
||||
item.hostStatus === "error" ||
|
||||
item.portStatus === "error" ||
|
||||
item.host === "" ||
|
||||
item.port === ""
|
||||
}
|
||||
onChange={(checked: boolean) => {
|
||||
wsc.clearProxy();
|
||||
const copyProxyList = structuredClone(proxyList);
|
||||
copyProxyList.forEach((i) => {
|
||||
if (i.id === item.id) {
|
||||
i.open = checked;
|
||||
if (checked) {
|
||||
wsc.setProxy(item.scheme, item.host, Number(item.port));
|
||||
}
|
||||
} else {
|
||||
i.open = false;
|
||||
}
|
||||
});
|
||||
setProxyList(copyProxyList);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div
|
||||
className="add-list"
|
||||
onClick={() => {
|
||||
setProxyList([
|
||||
addNewProxyListItem(
|
||||
"http",
|
||||
"127.0.0.1",
|
||||
"8083",
|
||||
false,
|
||||
"http://127.0.0.1:8083"
|
||||
),
|
||||
]);
|
||||
}}
|
||||
>
|
||||
<PlusSmIcon className="add-list-icon" />
|
||||
<span className="add-list-text">添加</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+29
-3
@@ -1,3 +1,7 @@
|
||||
html:root {
|
||||
--yakit-primary: #F28B44
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
@@ -7,7 +11,29 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
/*滚动条整体样式*/
|
||||
width: 8px;
|
||||
/*高宽分别对应横竖滚动条的尺寸*/
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
/*滚动条里面小方块*/
|
||||
border-radius: 10px;
|
||||
box-shadow: inset 0 0 5px rgba(193, 193, 193);
|
||||
background: #c1c1c1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
/*滚动条里面轨道*/
|
||||
box-shadow: inset 0 0 5px rgba(193, 193, 193);
|
||||
border-radius: 10px;
|
||||
background: #ededed;
|
||||
}
|
||||
|
||||
+24
-11
@@ -1,9 +1,19 @@
|
||||
import {chrome} from "./chromeapi";
|
||||
|
||||
export namespace wsc {
|
||||
export enum ActionType {
|
||||
CONNECT = 'connect',
|
||||
DISCONNECT = 'disconnect',
|
||||
STATUS = 'status',
|
||||
PROXY_STATUS = 'proxy_status',
|
||||
SET_PROXY = 'set_proxy',
|
||||
CLEAR_PROXY = 'clear_proxy',
|
||||
INJECT_SCRIPT = 'yakit_inject_script',
|
||||
// 用于接收来自content script的消息
|
||||
TO_EXTENSION_PAGE = "yakit_to_extension_page",
|
||||
}
|
||||
|
||||
export function connect(port: number, host?: string) {
|
||||
chrome.runtime.sendMessage({
|
||||
action: 'connect',
|
||||
action: ActionType.CONNECT,
|
||||
host: host || '127.0.0.1',
|
||||
port: port,
|
||||
});
|
||||
@@ -11,19 +21,19 @@ export namespace wsc {
|
||||
|
||||
export function disconnect() {
|
||||
chrome.runtime.sendMessage({
|
||||
action: 'disconnect',
|
||||
action: ActionType.DISCONNECT,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateWSCStatus() {
|
||||
chrome.runtime.sendMessage({
|
||||
action: 'status',
|
||||
action: ActionType.STATUS,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateProxyStatus() {
|
||||
chrome.runtime.sendMessage({
|
||||
action: 'proxystatus',
|
||||
action: ActionType.PROXY_STATUS,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,13 +45,16 @@ export namespace wsc {
|
||||
chrome.runtime.onMessage.addListener(onMessage)
|
||||
}
|
||||
|
||||
export function setproxy(scheme: string, host: string, port: number) {
|
||||
chrome.runtime.sendMessage({action: "setproxy", scheme, host, port})
|
||||
export function setProxy(scheme: string, host: string, port: number) {
|
||||
chrome.runtime.sendMessage({action: ActionType.SET_PROXY, scheme, host, port})
|
||||
}
|
||||
|
||||
export function clearproxy() {
|
||||
chrome.runtime.sendMessage({action: 'clearproxy'})
|
||||
export function clearProxy() {
|
||||
chrome.runtime.sendMessage({action: ActionType.CLEAR_PROXY})
|
||||
}
|
||||
|
||||
|
||||
// 获取当前tab
|
||||
export async function getTab() {
|
||||
return chrome.tabs.query({active: true, currentWindow: true})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
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});
|
||||
}
|
||||
}
|
||||
};
|
||||
+10
-4
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"noImplicitAny": true,
|
||||
"module": "es6",
|
||||
"target": "es5",
|
||||
@@ -8,9 +7,16 @@
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"moduleResolution": "node"
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
"./src/**/*",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+62
-57
@@ -5,60 +5,65 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development', // 设置模式为开发模式
|
||||
entry: './src/index.jsx', // 指定入口文件
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'build'), // 输出目录
|
||||
filename: 'bundle.js', // 输出文件名
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
|
||||
'process.env.BABEL_ENV': JSON.stringify(process.env.BABEL_ENV || 'development'),
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: './public/index.html',
|
||||
filename: 'index.html'
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
// copy public assets exclude index.html
|
||||
{
|
||||
from: path.resolve(__dirname, 'public'),
|
||||
to: path.resolve(__dirname, 'build'),
|
||||
globOptions: {
|
||||
ignore: ['**/index.html']
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
watch: true, // 开启实时监控
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/, // 匹配所有的 css 文件
|
||||
use: ['style-loader', 'css-loader'] // 对匹配到的文件使用这两个 loader
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/, // 匹配TS和TSX文件
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/, // 匹配JS和JSX文件
|
||||
exclude: /node_modules/, // 排除node_modules目录
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react'] // 使用的babel预设
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js', '.jsx'] // 解析扩展(确保能够解析JS和JSX文件)
|
||||
},
|
||||
devtool: 'inline-source-map', // 生成内联源映射,便于调试
|
||||
};
|
||||
mode: 'development', // 设置模式为开发模式
|
||||
entry: './src/index.jsx', // 指定入口文件
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'build'), // 输出目录
|
||||
filename: 'bundle.js', // 输出文件名
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
|
||||
'process.env.BABEL_ENV': JSON.stringify(process.env.BABEL_ENV || 'development'),
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: './public/index.html',
|
||||
filename: 'index.html'
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
// copy public assets exclude index.html
|
||||
{
|
||||
from: path.resolve(__dirname, 'public'),
|
||||
to: path.resolve(__dirname, 'build'),
|
||||
globOptions: {
|
||||
ignore: ['**/index.html']
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
watch: true, // 开启实时监控
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/, // 匹配所有的 css 文件
|
||||
use: ['style-loader', 'css-loader'] // 对匹配到的文件使用这两个 loader
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/, // 匹配TS和TSX文件
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/, // 匹配JS和JSX文件
|
||||
exclude: /node_modules/, // 排除node_modules目录
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react'] // 使用的babel预设
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js', '.jsx'], // 解析扩展(确保能够解析JS和JSX文件)
|
||||
alias: {
|
||||
'@assets': path.resolve(__dirname, './src/assets'),
|
||||
'@components': path.resolve(__dirname, './src/components'),
|
||||
'@network': path.resolve(__dirname, './src/network'),
|
||||
}
|
||||
},
|
||||
devtool: 'inline-source-map', // 生成内联源映射,便于调试
|
||||
};
|
||||
Reference in New Issue
Block a user