mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-22 03:10:43 +08:00
Remove ord directory from tracking
This commit is contained in:
@@ -1,84 +0,0 @@
|
||||
import {ActionType, injectScriptAndSendMessage, WebSocketManager} from './socket.js';
|
||||
import { setupProxyHandlers } from './proxy.js';
|
||||
|
||||
console.info("Chrome Extension Background is loaded");
|
||||
|
||||
const websocketManager = new WebSocketManager();
|
||||
|
||||
// 设置代理处理器
|
||||
setupProxyHandlers();
|
||||
|
||||
// 添加点击事件处理
|
||||
chrome.action.onClicked.addListener((tab) => {
|
||||
// 打开侧边栏
|
||||
chrome.sidePanel.open({windowId: tab.windowId}).catch(error => {
|
||||
console.error('Error opening side panel:', error);
|
||||
});
|
||||
});
|
||||
|
||||
// 设置默认打开状态
|
||||
chrome.sidePanel.setOptions({
|
||||
enabled: true,
|
||||
path: 'index.html'
|
||||
}).catch(error => {
|
||||
console.error('Error setting side panel options:', error);
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||
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=chrome`, port)
|
||||
break;
|
||||
case ActionType.SEND_MESSAGE:
|
||||
websocketManager.sendMessage(msg.message);
|
||||
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}`)
|
||||
},
|
||||
// enable 127.0.0.1 && localhost to mitmproxy
|
||||
bypassList: ["<-loopback>"]
|
||||
}
|
||||
},
|
||||
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: ""})
|
||||
}
|
||||
});
|
||||
break;
|
||||
case ActionType.INJECT_SCRIPT:
|
||||
(async () => {
|
||||
await injectScriptAndSendMessage(msg.tabId, {
|
||||
type: ActionType.INJECT_SCRIPT,
|
||||
value: msg.value
|
||||
});
|
||||
})();
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
(() => {
|
||||
if (window.contentScriptInjected) {
|
||||
return;
|
||||
}
|
||||
window.contentScriptInjected = true;
|
||||
window.badgeCount = 0;
|
||||
// 检查并插入 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', async function onMessage(event) {
|
||||
if (event.source !== window || event.data.type !== 'FROM_INJECT_JS') {
|
||||
return;
|
||||
}
|
||||
window.removeEventListener('message', onMessage);
|
||||
window.badgeCount += 1;
|
||||
// 直接向向发送端返回结果
|
||||
sendResponse({action: 'yakit_to_extension_page', result: event.data.result});
|
||||
// Send updated badge count to background script
|
||||
await chrome.runtime.sendMessage({action: 'yakit_badge', data: window.badgeCount.toString()});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})()
|
||||
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
class Database {
|
||||
constructor() {
|
||||
this.DB_NAME = 'yaklang_extension';
|
||||
this.DB_VERSION = 1;
|
||||
this.stores = {
|
||||
// 代理日志存储
|
||||
PROXY_LOGS: 'proxy_logs',
|
||||
// 代理配置列表存储
|
||||
PROXY_CONFIGS: 'proxy_configs',
|
||||
// 当前代理配置存储
|
||||
CURRENT_PROXY: 'current_proxy',
|
||||
// 代理认证信息存储
|
||||
PROXY_AUTH: 'proxy_auth'
|
||||
};
|
||||
}
|
||||
|
||||
async initDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(this.DB_NAME, this.DB_VERSION);
|
||||
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
|
||||
// 代理日志存储
|
||||
if (!db.objectStoreNames.contains(this.stores.PROXY_LOGS)) {
|
||||
const logsStore = db.createObjectStore(this.stores.PROXY_LOGS, { keyPath: 'id' });
|
||||
logsStore.createIndex('timestamp', 'timestamp');
|
||||
logsStore.createIndex('resourceType', 'resourceType');
|
||||
logsStore.createIndex('status', 'status');
|
||||
}
|
||||
|
||||
// 代理配置列表存储
|
||||
if (!db.objectStoreNames.contains(this.stores.PROXY_CONFIGS)) {
|
||||
const configsStore = db.createObjectStore(this.stores.PROXY_CONFIGS, { keyPath: 'id' });
|
||||
configsStore.createIndex('name', 'name');
|
||||
configsStore.createIndex('enabled', 'enabled');
|
||||
}
|
||||
|
||||
// 当前代理配置存储
|
||||
if (!db.objectStoreNames.contains(this.stores.CURRENT_PROXY)) {
|
||||
db.createObjectStore(this.stores.CURRENT_PROXY);
|
||||
}
|
||||
|
||||
// 代理认证信息存储
|
||||
if (!db.objectStoreNames.contains(this.stores.PROXY_AUTH)) {
|
||||
const authStore = db.createObjectStore(this.stores.PROXY_AUTH, { keyPath: 'id' });
|
||||
authStore.createIndex('host', 'host');
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async getStore(storeName, mode = 'readonly') {
|
||||
const db = await this.initDB();
|
||||
const tx = db.transaction(storeName, mode);
|
||||
return tx.objectStore(storeName);
|
||||
}
|
||||
|
||||
// CRUD 操作
|
||||
async get(storeName, key) {
|
||||
const store = await this.getStore(storeName);
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.get(key);
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
async getAll(storeName) {
|
||||
const store = await this.getStore(storeName);
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.getAll();
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
async put(storeName, value, key = undefined) {
|
||||
try {
|
||||
const store = await this.getStore(storeName, 'readwrite');
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = key ? store.put(value, key) : store.put(value);
|
||||
request.onsuccess = () => {
|
||||
console.log(`Successfully put data in ${storeName}:`, value);
|
||||
resolve(request.result);
|
||||
};
|
||||
request.onerror = () => {
|
||||
console.error(`Error putting data in ${storeName}:`, request.error);
|
||||
reject(request.error);
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error in put operation for ${storeName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async delete(storeName, key) {
|
||||
const store = await this.getStore(storeName, 'readwrite');
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.delete(key);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
async clear(storeName) {
|
||||
const store = await this.getStore(storeName, 'readwrite');
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.clear();
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const db = new Database();
|
||||
@@ -1,175 +0,0 @@
|
||||
import { db } from './db.js';
|
||||
|
||||
class ProxyStore {
|
||||
constructor() {
|
||||
this.MAX_LOGS = 1000;
|
||||
}
|
||||
|
||||
// 代理配置相关操作
|
||||
async getProxyConfigs() {
|
||||
return await db.getAll(db.stores.PROXY_CONFIGS);
|
||||
}
|
||||
|
||||
async saveProxyConfigs(configs) {
|
||||
try {
|
||||
console.log('Saving proxy configs:', configs);
|
||||
|
||||
// 确保配置数组有效
|
||||
if (!Array.isArray(configs)) {
|
||||
throw new Error('配置必须是数组');
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
const store = await db.getStore(db.stores.PROXY_CONFIGS, 'readwrite');
|
||||
|
||||
// 清除现有配置
|
||||
await store.clear();
|
||||
|
||||
// 保存新配置
|
||||
for (const config of configs) {
|
||||
await store.put(config);
|
||||
}
|
||||
|
||||
console.log('Proxy configs saved successfully');
|
||||
this.notifyConfigUpdate();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error saving proxy configs:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getCurrentProxy() {
|
||||
return await db.get(db.stores.CURRENT_PROXY, 'current');
|
||||
}
|
||||
|
||||
async setCurrentProxy(proxy) {
|
||||
await db.put(db.stores.CURRENT_PROXY, proxy, 'current');
|
||||
}
|
||||
|
||||
async clearCurrentProxy() {
|
||||
await db.delete(db.stores.CURRENT_PROXY, 'current');
|
||||
}
|
||||
|
||||
// 代理日志相关操作
|
||||
async getLogs() {
|
||||
const logs = await db.getAll(db.stores.PROXY_LOGS);
|
||||
return logs.sort((a, b) => b.timestamp - a.timestamp);
|
||||
}
|
||||
|
||||
async addLog(log) {
|
||||
await db.put(db.stores.PROXY_LOGS, log);
|
||||
await this.cleanOldLogs();
|
||||
}
|
||||
|
||||
async clearLogs() {
|
||||
await db.clear(db.stores.PROXY_LOGS);
|
||||
}
|
||||
|
||||
async cleanOldLogs() {
|
||||
const store = await db.getStore(db.stores.PROXY_LOGS, 'readwrite');
|
||||
const countRequest = store.count();
|
||||
|
||||
countRequest.onsuccess = () => {
|
||||
if (countRequest.result > this.MAX_LOGS) {
|
||||
const excess = countRequest.result - this.MAX_LOGS;
|
||||
const cursorRequest = store.index('timestamp').openCursor();
|
||||
let deleted = 0;
|
||||
|
||||
cursorRequest.onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (cursor && deleted < excess) {
|
||||
cursor.delete();
|
||||
deleted++;
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 代理认证相关操作
|
||||
async getAuthHandlers() {
|
||||
return await db.getAll(db.stores.PROXY_AUTH);
|
||||
}
|
||||
|
||||
async saveAuthHandler(handler) {
|
||||
await db.put(db.stores.PROXY_AUTH, handler);
|
||||
}
|
||||
|
||||
async deleteAuthHandler(id) {
|
||||
await db.delete(db.stores.PROXY_AUTH, id);
|
||||
}
|
||||
|
||||
async clearAuthHandlers() {
|
||||
await db.clear(db.stores.PROXY_AUTH);
|
||||
}
|
||||
|
||||
async getErrors() {
|
||||
return await db.get(db.stores.PROXY_AUTH, 'errors') || [];
|
||||
}
|
||||
|
||||
async saveErrors(errors) {
|
||||
await db.put(db.stores.PROXY_AUTH, errors, 'errors');
|
||||
}
|
||||
|
||||
async getAuth() {
|
||||
return await db.get(db.stores.PROXY_AUTH, 'auth');
|
||||
}
|
||||
|
||||
async saveAuth(auth) {
|
||||
await db.put(db.stores.PROXY_AUTH, auth, 'auth');
|
||||
}
|
||||
|
||||
async clearAuth() {
|
||||
await db.delete(db.stores.PROXY_AUTH, 'auth');
|
||||
}
|
||||
|
||||
notifyConfigUpdate() {
|
||||
chrome.runtime.sendMessage({
|
||||
action: 'PROXY_CONFIGS_UPDATED'
|
||||
}).catch(() => {
|
||||
// 忽略接收者不存在的错误
|
||||
});
|
||||
}
|
||||
|
||||
async addAndEnableProxy(config) {
|
||||
try {
|
||||
// 先禁用所有其他代理
|
||||
const existingConfigs = await this.getProxyConfigs();
|
||||
for (const existingConfig of existingConfigs) {
|
||||
if (existingConfig.enabled) {
|
||||
await this.saveProxyConfigs([{
|
||||
...existingConfig,
|
||||
enabled: false
|
||||
}]);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加并启用新代理
|
||||
await this.saveProxyConfigs([config]);
|
||||
|
||||
// 应用新代理
|
||||
await chrome.proxy.settings.set({
|
||||
value: {
|
||||
mode: config.proxyType,
|
||||
rules: {
|
||||
singleProxy: {
|
||||
scheme: config.scheme,
|
||||
host: config.host,
|
||||
port: config.port
|
||||
}
|
||||
}
|
||||
},
|
||||
scope: 'regular'
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error in addAndEnableProxy:', error);
|
||||
return { success: false, error };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const proxyStore = new ProxyStore();
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<meta name="theme-color" content="#000000"/>
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,38 +0,0 @@
|
||||
(() => {
|
||||
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;
|
||||
console.log(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;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Yakit Chrome Endpoint",
|
||||
"version": "0.0.7",
|
||||
"description": "A Endpoint for Yakit MITM or more",
|
||||
"options_ui": {
|
||||
"page": "proxy/options.html",
|
||||
"open_in_tab": true
|
||||
},
|
||||
"action": {
|
||||
"default_popup": "index.html",
|
||||
"default_icon": {
|
||||
"16": "/images/icon16.png",
|
||||
"48": "/images/icon48.png",
|
||||
"128": "/images/icon128.png"
|
||||
}
|
||||
},
|
||||
"side_panel": {
|
||||
"default_path": "index.html"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>","http://mitm/"],
|
||||
"run_at": "document_start",
|
||||
"js": [
|
||||
"proxy/links_finder.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"matches": ["<all_urls>","http://mitm/"],
|
||||
"run_at": "document_end",
|
||||
"js": [
|
||||
"proxy/content.js"
|
||||
]
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
"proxy",
|
||||
"storage",
|
||||
"sidePanel",
|
||||
"webRequest",
|
||||
"declarativeNetRequest",
|
||||
"webNavigation",
|
||||
"tabs"
|
||||
],
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
],
|
||||
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"images/*",
|
||||
"proxy/*"
|
||||
],
|
||||
"matches": ["<all_urls>"]
|
||||
},
|
||||
{
|
||||
"resources": [
|
||||
"/images/yak.svg"
|
||||
],
|
||||
"matches": ["<all_urls>"]
|
||||
}
|
||||
],
|
||||
"icons": {
|
||||
"16": "/images/icon16.png",
|
||||
"48": "/images/icon48.png",
|
||||
"128": "/images/icon128.png"
|
||||
}
|
||||
}
|
||||
-559
@@ -1,559 +0,0 @@
|
||||
import {ProxySettings} from './proxy/proxy-settings.js';
|
||||
import {ProxyAuth} from './proxy/proxy-auth.js';
|
||||
import {ProxyActionType} from './types/action.js';
|
||||
import {proxyLogs} from './proxy/proxy-logs.js';
|
||||
import {proxyStore} from './db/proxy-store.js';
|
||||
|
||||
// 修改代理状态获取函数为 Promise 形式
|
||||
function getProxySettings() {
|
||||
return new Promise((resolve) => {
|
||||
chrome.proxy.settings.get({}, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSetProxyConfig(config, sendResponse) {
|
||||
try {
|
||||
// 处理代理服务器的情况
|
||||
if (config.proxyType === 'fixed_servers') {
|
||||
// 固定代理服务器模式需要验证 host 和 port
|
||||
if (!config || !config.host || !config.port) {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: '无效的代理配置:缺少主机或端口'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const proxyConfig = {
|
||||
mode: "fixed_servers",
|
||||
rules: {
|
||||
singleProxy: {
|
||||
scheme: config.scheme || 'http',
|
||||
host: config.host,
|
||||
port: parseInt(config.port)
|
||||
},
|
||||
bypassList: config.bypassList || []
|
||||
}
|
||||
};
|
||||
|
||||
await new Promise((resolve) => {
|
||||
chrome.proxy.settings.set({
|
||||
value: proxyConfig,
|
||||
scope: 'regular'
|
||||
}, resolve);
|
||||
});
|
||||
|
||||
const settings = await getProxySettings();
|
||||
const isSuccess = settings.value.mode === "fixed_servers" &&
|
||||
settings.value.rules.singleProxy.host === config.host &&
|
||||
settings.value.rules.singleProxy.port === parseInt(config.port);
|
||||
|
||||
if (isSuccess) {
|
||||
await proxyStore.setCurrentProxy({
|
||||
...config,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
const configs = await proxyStore.getProxyConfigs();
|
||||
const updatedConfigs = configs.map(c => ({
|
||||
...c,
|
||||
enabled: c.id === config.id
|
||||
}));
|
||||
await proxyStore.saveProxyConfigs(updatedConfigs);
|
||||
|
||||
console.log('Proxy successfully set:', settings.value);
|
||||
sendResponse({success: true});
|
||||
|
||||
// 通知所有 content scripts 更新
|
||||
await notifyProxyStatusChanged();
|
||||
} else {
|
||||
console.error('Proxy settings verification failed');
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: '代理设置验证失败'
|
||||
});
|
||||
}
|
||||
return;
|
||||
} else if (config.proxyType === 'pac_script') {
|
||||
// PAC 脚本模式需要验证 pacScript
|
||||
if (!config || !config.pacScript || !config.pacScript.data) {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: '无效的 PAC 脚本配置'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const proxyConfig = {
|
||||
mode: "pac_script",
|
||||
pacScript: config.pacScript
|
||||
};
|
||||
|
||||
await new Promise((resolve) => {
|
||||
chrome.proxy.settings.set({
|
||||
value: proxyConfig,
|
||||
scope: 'regular'
|
||||
}, resolve);
|
||||
});
|
||||
|
||||
const settings = await getProxySettings();
|
||||
const isSuccess = settings.value.mode === "pac_script" &&
|
||||
settings.value.pacScript &&
|
||||
settings.value.pacScript.data;
|
||||
|
||||
if (isSuccess) {
|
||||
await proxyStore.setCurrentProxy({
|
||||
...config,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
const configs = await proxyStore.getProxyConfigs();
|
||||
const updatedConfigs = configs.map(c => ({
|
||||
...c,
|
||||
enabled: c.id === config.id
|
||||
}));
|
||||
await proxyStore.saveProxyConfigs(updatedConfigs);
|
||||
|
||||
console.log('PAC script proxy successfully set:', settings.value);
|
||||
sendResponse({success: true});
|
||||
|
||||
// 通知所有 content scripts 更新
|
||||
await notifyProxyStatusChanged();
|
||||
} else {
|
||||
console.error('PAC script settings verification failed', {
|
||||
expected: config,
|
||||
actual: settings.value
|
||||
});
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: '代理设置验证失败'
|
||||
});
|
||||
}
|
||||
return;
|
||||
} else if (config.proxyType === 'direct' || config.proxyType === 'system') {
|
||||
// 直接连接或系统代理模式
|
||||
const proxyConfig = {
|
||||
mode: config.proxyType
|
||||
};
|
||||
|
||||
await new Promise((resolve) => {
|
||||
chrome.proxy.settings.set({
|
||||
value: proxyConfig,
|
||||
scope: 'regular'
|
||||
}, resolve);
|
||||
});
|
||||
|
||||
const settings = await getProxySettings();
|
||||
const isSuccess = settings.value.mode === config.proxyType;
|
||||
|
||||
if (isSuccess) {
|
||||
await proxyStore.setCurrentProxy({
|
||||
...config,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
const configs = await proxyStore.getProxyConfigs();
|
||||
const updatedConfigs = configs.map(c => ({
|
||||
...c,
|
||||
enabled: c.id === config.id
|
||||
}));
|
||||
await proxyStore.saveProxyConfigs(updatedConfigs);
|
||||
|
||||
console.log('Proxy successfully set:', settings.value);
|
||||
sendResponse({success: true});
|
||||
|
||||
// 通知所有 content scripts 更新
|
||||
await notifyProxyStatusChanged();
|
||||
} else {
|
||||
console.error('Proxy settings verification failed');
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: '代理设置验证失败'
|
||||
});
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: '不支持的代理类型'
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error setting proxy:', error);
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error.message || '设置代理时发生错误'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function handleClearProxyConfig(sendResponse) {
|
||||
try {
|
||||
await chrome.proxy.settings.clear({
|
||||
scope: 'regular'
|
||||
});
|
||||
// 获取所有配置并禁用
|
||||
const configs = await proxyStore.getProxyConfigs();
|
||||
const updatedConfigs = configs.map(config => ({
|
||||
...config,
|
||||
enabled: false
|
||||
}));
|
||||
await proxyStore.saveProxyConfigs(updatedConfigs);
|
||||
|
||||
sendResponse({ success: true });
|
||||
|
||||
// 通知所有 content scripts 更新
|
||||
await notifyProxyStatusChanged();
|
||||
} catch (error) {
|
||||
console.error('Error clearing proxy config:', error);
|
||||
sendResponse({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGetProxyStatus(sendResponse) {
|
||||
try {
|
||||
const settings = await getProxySettings();
|
||||
const currentProxy = await proxyStore.getCurrentProxy();
|
||||
|
||||
const status = {
|
||||
enabled: settings.value.mode === "fixed_servers",
|
||||
config: currentProxy || null,
|
||||
mode: settings.value.mode
|
||||
};
|
||||
|
||||
console.log('Current proxy status:', status);
|
||||
sendResponse({
|
||||
success: true,
|
||||
data: status
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error getting proxy status:', error);
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error.message || '获取代理状态时发生错误'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 添加代理请求监听器
|
||||
function setupProxyRequestListener() {
|
||||
// 监听请求发送
|
||||
chrome.webRequest.onBeforeRequest.addListener(
|
||||
(details) => {
|
||||
// 使用非阻塞方式处理请求
|
||||
queueProxyLog(details).catch(error => {
|
||||
console.error('Error in proxy request listener:', error);
|
||||
});
|
||||
// 不需要返回值
|
||||
},
|
||||
{urls: ["<all_urls>"]}
|
||||
);
|
||||
|
||||
// 监听请求错误
|
||||
chrome.webRequest.onErrorOccurred.addListener(
|
||||
(details) => {
|
||||
// 使用非阻塞方式处理错误
|
||||
queueProxyLog(details, new Error(details.error)).catch(error => {
|
||||
console.error('Error in proxy error listener:', error);
|
||||
});
|
||||
},
|
||||
{urls: ["<all_urls>"]}
|
||||
);
|
||||
}
|
||||
|
||||
// 使用队列处理日志
|
||||
async function queueProxyLog(details, error = null) {
|
||||
try {
|
||||
// 检查代理状态
|
||||
const settings = await getProxySettings();
|
||||
if (settings.value.mode !== "fixed_servers") {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前代理配置
|
||||
const currentProxy = await proxyStore.getCurrentProxy();
|
||||
if (!currentProxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
await proxyLogs.logRequest(details, currentProxy, error);
|
||||
} catch (error) {
|
||||
console.error('Error in queueProxyLog:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加检查和设置初始代理的函数
|
||||
async function checkAndSetInitialProxy() {
|
||||
try {
|
||||
// 确保默认配置存在
|
||||
await ProxySettings.setDefaultConfigs();
|
||||
|
||||
// 获取上次保存的代理配置
|
||||
const lastProxy = await proxyStore.getCurrentProxy();
|
||||
|
||||
if (lastProxy) {
|
||||
// 如果有上次的配置,恢复它
|
||||
console.log('Restoring last proxy configuration:', lastProxy);
|
||||
await handleSetProxyConfig(lastProxy, () => {});
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前的代理设置
|
||||
const settings = await getProxySettings();
|
||||
console.log('Current proxy settings:', settings);
|
||||
|
||||
// 检查是否存在固定代理服务器设置
|
||||
if (settings.value.mode === "fixed_servers" &&
|
||||
settings.value.rules &&
|
||||
settings.value.rules.singleProxy) {
|
||||
|
||||
const proxy = settings.value.rules.singleProxy;
|
||||
|
||||
// 获取现有配置
|
||||
const existingConfigs = await proxyStore.getProxyConfigs();
|
||||
|
||||
// 检查是否已存在相同的 MITM 配置
|
||||
const existingMitm = existingConfigs.find(config =>
|
||||
config.host === proxy.host &&
|
||||
config.port === proxy.port &&
|
||||
config.scheme === proxy.scheme
|
||||
);
|
||||
|
||||
if (!existingMitm) {
|
||||
// 创建新的 MITM 配置
|
||||
const newConfig = {
|
||||
id: Date.now().toString(),
|
||||
name: "Yakit MITM",
|
||||
proxyType: 'fixed_servers',
|
||||
scheme: proxy.scheme || 'http',
|
||||
host: proxy.host,
|
||||
port: proxy.port,
|
||||
enabled: true,
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=899126#c17
|
||||
bypassList: ["<-loopback>"],
|
||||
matchList: []
|
||||
};
|
||||
|
||||
// 添加到现有配置中
|
||||
const updatedConfigs = [...existingConfigs, newConfig];
|
||||
await proxyStore.saveProxyConfigs(updatedConfigs);
|
||||
|
||||
// 启用新配置
|
||||
await handleSetProxyConfig(newConfig, () => {});
|
||||
|
||||
console.log('Added and enabled Yakit MITM config from existing proxy settings');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有之前的配置也没有检测到代理,才设置为系统代理
|
||||
await handleSetProxyConfig({
|
||||
id: 'system',
|
||||
name: '[系统代理]',
|
||||
proxyType: 'system',
|
||||
enabled: true
|
||||
}, () => {});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during initialization:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 修改 setupProxyHandlers 函数
|
||||
export function setupProxyHandlers() {
|
||||
// 设置代理错误处理
|
||||
ProxyAuth.setupErrorHandler();
|
||||
// 设置认证监听
|
||||
ProxyAuth.setupAuthListener();
|
||||
// 设置代理请求监听器
|
||||
setupProxyRequestListener();
|
||||
|
||||
// 消息监听器
|
||||
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||
console.log("Proxy message:", msg);
|
||||
|
||||
switch (msg.action) {
|
||||
case ProxyActionType.SET_PROXY_CONFIG:
|
||||
handleSetProxyConfig(msg.config, sendResponse);
|
||||
return true;
|
||||
|
||||
case ProxyActionType.CLEAR_PROXY_CONFIG:
|
||||
(async () => {
|
||||
await handleClearProxyConfig(sendResponse);
|
||||
})();
|
||||
return true;
|
||||
|
||||
case ProxyActionType.GET_PROXY_STATUS:
|
||||
handleGetProxyStatus(sendResponse);
|
||||
return true;
|
||||
|
||||
case ProxyActionType.GET_PROXY_LOGS:
|
||||
proxyLogs.getLogs().then(logs => {
|
||||
sendResponse({
|
||||
success: true,
|
||||
data: logs
|
||||
});
|
||||
}).catch(error => {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
case ProxyActionType.CLEAR_PROXY_LOGS:
|
||||
proxyLogs.clearLogs().then(() => {
|
||||
sendResponse({success: true});
|
||||
}).catch(error => {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
case ProxyActionType.GET_PROXY_CONFIGS:
|
||||
(async () => {
|
||||
try {
|
||||
const configs = await proxyStore.getProxyConfigs();
|
||||
sendResponse({ success: true, data: configs });
|
||||
} catch (error) {
|
||||
console.error('Error getting proxy configs:', error);
|
||||
sendResponse({ success: false, error: error.message });
|
||||
}
|
||||
})();
|
||||
return true;
|
||||
|
||||
case ProxyActionType.ADD_PROXY_CONFIG:
|
||||
proxyStore.getProxyConfigs().then(async configs => {
|
||||
const newConfigs = [...configs, msg.config];
|
||||
ProxyActionType
|
||||
proxyStore.saveProxyConfigs(newConfigs);
|
||||
sendResponse({success: true});
|
||||
}).catch(error => {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
case ProxyActionType.UPDATE_PROXY_CONFIG:
|
||||
(async () => {
|
||||
try {
|
||||
if (!msg.configs || !Array.isArray(msg.configs)) {
|
||||
throw new Error('无效的配置数据');
|
||||
}
|
||||
|
||||
console.log('Updating proxy configs:', msg.configs);
|
||||
await proxyStore.saveProxyConfigs(msg.configs);
|
||||
|
||||
// 获取最新的配置
|
||||
const updatedConfigs = await proxyStore.getProxyConfigs();
|
||||
console.log('Configs updated successfully:', updatedConfigs);
|
||||
|
||||
// 发送响应
|
||||
sendResponse({
|
||||
success: true,
|
||||
data: updatedConfigs
|
||||
});
|
||||
|
||||
// 通知所有 content scripts 更新
|
||||
await notifyProxyStatusChanged();
|
||||
} catch (error) {
|
||||
console.error('Error updating proxy configs:', error);
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error.message || '更新代理配置失败'
|
||||
});
|
||||
}
|
||||
})();
|
||||
return true;
|
||||
|
||||
case ProxyActionType.OPEN_OPTIONS_PAGE:
|
||||
// 打开选项页
|
||||
chrome.tabs.create({
|
||||
url: chrome.runtime.getURL('/proxy/options.html')
|
||||
}).then(tab => {
|
||||
if (msg.triggerAdd) {
|
||||
// 如果需要触发添加代理,等待页面加载完成
|
||||
const listener = (tabId, changeInfo) => {
|
||||
if (tabId === tab.id && changeInfo.status === 'complete') {
|
||||
chrome.tabs.onUpdated.removeListener(listener);
|
||||
// 向选项页发送消息触发添加代理
|
||||
chrome.tabs.sendMessage(tab.id, {
|
||||
action: 'TRIGGER_ADD_PROXY'
|
||||
});
|
||||
}
|
||||
};
|
||||
chrome.tabs.onUpdated.addListener(listener);
|
||||
}
|
||||
});
|
||||
sendResponse({ success: true });
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 在扩展启动时初始化
|
||||
chrome.runtime.onInstalled.addListener(async () => {
|
||||
await checkAndSetInitialProxy();
|
||||
});
|
||||
|
||||
// 浏览器启动时初始化
|
||||
chrome.runtime.onStartup.addListener(async () => {
|
||||
await checkAndSetInitialProxy();
|
||||
});
|
||||
}
|
||||
|
||||
// 当代理状态改变时通知所有内容脚本
|
||||
async function notifyProxyStatusChanged() {
|
||||
const tabs = await chrome.tabs.query({});
|
||||
for (const tab of tabs) {
|
||||
try {
|
||||
chrome.tabs.sendMessage(tab.id, { action: 'PROXY_STATUS_CHANGED' });
|
||||
} catch (error) {
|
||||
// 忽略不支持的标签页
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setProxyConfig(config) {
|
||||
try {
|
||||
let chromeProxyConfig;
|
||||
|
||||
if (config.proxyType === 'pac_script') {
|
||||
chromeProxyConfig = {
|
||||
mode: "pac_script",
|
||||
pacScript: config.pacScript
|
||||
};
|
||||
} else if (config.proxyType === 'fixed_servers') {
|
||||
chromeProxyConfig = {
|
||||
mode: "fixed_servers",
|
||||
rules: {
|
||||
singleProxy: {
|
||||
scheme: config.scheme,
|
||||
host: config.host,
|
||||
port: config.port
|
||||
},
|
||||
bypassList: config.bypassList || []
|
||||
}
|
||||
};
|
||||
} else {
|
||||
chromeProxyConfig = {
|
||||
mode: config.proxyType // direct, system, auto_detect
|
||||
};
|
||||
}
|
||||
|
||||
await chrome.proxy.settings.set({
|
||||
value: chromeProxyConfig,
|
||||
scope: 'regular'
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Failed to set proxy config:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,213 +0,0 @@
|
||||
// Links Finder Module
|
||||
class LinksFinder {
|
||||
constructor() {
|
||||
this.links = [];
|
||||
this.lastUpdate = null;
|
||||
}
|
||||
|
||||
// 获取页面所有链接
|
||||
getAllLinks() {
|
||||
const links = [];
|
||||
const seen = new Set();
|
||||
|
||||
// 获取所有 a 标签
|
||||
document.querySelectorAll('a').forEach(a => {
|
||||
const href = a.href;
|
||||
if (href && !seen.has(href) && href.startsWith('http')) {
|
||||
seen.add(href);
|
||||
links.push({
|
||||
type: 'anchor',
|
||||
url: href,
|
||||
text: a.textContent.trim() || href,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取所有图片链接
|
||||
document.querySelectorAll('img').forEach(img => {
|
||||
const src = img.src;
|
||||
if (src && !seen.has(src) && src.startsWith('http')) {
|
||||
seen.add(src);
|
||||
links.push({
|
||||
type: 'image',
|
||||
url: src,
|
||||
alt: img.alt || 'Image',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取所有脚本链接
|
||||
document.querySelectorAll('script').forEach(script => {
|
||||
const src = script.src;
|
||||
if (src && !seen.has(src) && src.startsWith('http')) {
|
||||
seen.add(src);
|
||||
links.push({
|
||||
type: 'script',
|
||||
url: src,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取所有样式表链接
|
||||
document.querySelectorAll('link[rel="stylesheet"]').forEach(link => {
|
||||
const href = link.href;
|
||||
if (href && !seen.has(href) && href.startsWith('http')) {
|
||||
seen.add(href);
|
||||
links.push({
|
||||
type: 'stylesheet',
|
||||
url: href,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.links = links;
|
||||
this.lastUpdate = new Date();
|
||||
return links;
|
||||
}
|
||||
|
||||
// 按类型过滤链接
|
||||
filterLinksByType(type) {
|
||||
return this.links.filter(link => link.type === type);
|
||||
}
|
||||
|
||||
// 获取链接统计信息
|
||||
getLinkStats() {
|
||||
const stats = {
|
||||
total: this.links.length,
|
||||
byType: {}
|
||||
};
|
||||
|
||||
this.links.forEach(link => {
|
||||
if (!stats.byType[link.type]) {
|
||||
stats.byType[link.type] = 0;
|
||||
}
|
||||
stats.byType[link.type]++;
|
||||
});
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
// 构建链接面板的 HTML
|
||||
buildLinksPanel() {
|
||||
const links = this.getAllLinks();
|
||||
const stats = this.getLinkStats();
|
||||
|
||||
let html = `
|
||||
<div class="links-stats">
|
||||
<div class="stats-item">
|
||||
<span>🔗</span>
|
||||
<span>总链接: ${stats.total}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="links-filters">
|
||||
<button class="filter-btn active" data-type="all">
|
||||
<span>🔍</span>
|
||||
<span>全部 (${stats.total})</span>
|
||||
</button>
|
||||
${Object.entries(stats.byType).map(([type, count]) => `
|
||||
<button class="filter-btn" data-type="${type}">
|
||||
<span>${this._getTypeIcon(type)}</span>
|
||||
<span>${this._getTypeName(type)} (${count})</span>
|
||||
</button>
|
||||
`).join('')}
|
||||
</div>
|
||||
<div class="links-list">
|
||||
${links.map(link => this._buildLinkItem(link)).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// 获取链接类型图标
|
||||
_getTypeIcon(type) {
|
||||
const icons = {
|
||||
anchor: '🔗',
|
||||
image: '🖼️',
|
||||
script: '📜',
|
||||
stylesheet: '🎨'
|
||||
};
|
||||
return icons[type] || '🔗';
|
||||
}
|
||||
|
||||
// 获取链接类型名称
|
||||
_getTypeName(type) {
|
||||
const names = {
|
||||
anchor: '链接',
|
||||
image: '图片',
|
||||
script: '脚本',
|
||||
stylesheet: '样式'
|
||||
};
|
||||
return names[type] || type;
|
||||
}
|
||||
|
||||
// 构建单个链接项的 HTML
|
||||
_buildLinkItem(link) {
|
||||
return `
|
||||
<div class="link-item" data-type="${link.type}">
|
||||
<div class="link-icon">${this._getTypeIcon(link.type)}</div>
|
||||
<div class="link-content">
|
||||
<div class="link-url" title="${link.url}">${link.url}</div>
|
||||
${link.text ? `<div class="link-text" title="${link.text}">${link.text}</div>` : ''}
|
||||
${link.alt ? `<div class="link-alt" title="${link.alt}">${link.alt}</div>` : ''}
|
||||
</div>
|
||||
<button class="copy-btn" data-url="${link.url}" title="复制链接">📋</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 绑定事件处理
|
||||
bindEvents(container) {
|
||||
// 过滤按钮点击事件
|
||||
container.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const type = btn.dataset.type;
|
||||
console.log('Filter clicked:', type);
|
||||
|
||||
// 更新按钮状态
|
||||
container.querySelectorAll('.filter-btn').forEach(b => {
|
||||
b.classList.remove('active');
|
||||
});
|
||||
btn.classList.add('active');
|
||||
|
||||
// 过滤链接显示
|
||||
container.querySelectorAll('.link-item').forEach(item => {
|
||||
if (type === 'all' || item.dataset.type === type) {
|
||||
item.removeAttribute('data-hidden');
|
||||
} else {
|
||||
item.setAttribute('data-hidden', 'true');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 复制按钮点击事件
|
||||
container.querySelectorAll('.copy-btn').forEach(btn => {
|
||||
btn.addEventListener('click', async (e) => {
|
||||
e.stopPropagation();
|
||||
const url = btn.dataset.url;
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = '✓';
|
||||
btn.style.setProperty('color', '#52c41a', 'important');
|
||||
setTimeout(() => {
|
||||
btn.textContent = originalText;
|
||||
btn.style.removeProperty('color');
|
||||
}, 1000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
btn.textContent = '❌';
|
||||
setTimeout(() => {
|
||||
btn.textContent = '📋';
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("LinksFinder module loaded");
|
||||
// 导出模块
|
||||
window.LinksFinder = LinksFinder;
|
||||
@@ -1,160 +0,0 @@
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.proxy-item {
|
||||
background: white;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 16px;
|
||||
padding: 16px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.proxy-item:hover {
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.09);
|
||||
}
|
||||
|
||||
.proxy-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.proxy-name {
|
||||
font-size: 14px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.proxy-name:focus {
|
||||
border-color: #40a9ff;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(24,144,255,0.2);
|
||||
}
|
||||
|
||||
.proxy-type-select,
|
||||
.proxy-scheme,
|
||||
.proxy-host,
|
||||
.proxy-port {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding: 4px 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.proxy-type-select:focus,
|
||||
.proxy-scheme:focus,
|
||||
.proxy-host:focus,
|
||||
.proxy-port:focus {
|
||||
border-color: #40a9ff;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(24,144,255,0.2);
|
||||
}
|
||||
|
||||
.proxy-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.proxy-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #1890ff;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 4px 15px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: white;
|
||||
border: 1px solid #d9d9d9;
|
||||
color: rgba(0,0,0,0.85);
|
||||
padding: 4px 15px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
border-color: #40a9ff;
|
||||
color: #40a9ff;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
padding: 4px 8px;
|
||||
background: #ff4d4f;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #ff7875;
|
||||
}
|
||||
|
||||
.pac-script {
|
||||
font-family: monospace;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.options-page {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>代理设置</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="../options.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,116 +0,0 @@
|
||||
// 代理认证管理
|
||||
import { proxyStore } from '../db/proxy-store.js';
|
||||
|
||||
export class ProxyAuth {
|
||||
static async setupAuthListener() {
|
||||
// 使用 chrome.webRequest.onAuthRequired 的非阻塞版本
|
||||
chrome.webRequest.onAuthRequired.addListener(
|
||||
async (details) => {
|
||||
try {
|
||||
// 获取认证处理器
|
||||
const handlers = await proxyStore.getAuthHandlers();
|
||||
const handler = handlers.find(h =>
|
||||
details.challenger?.host === h.host
|
||||
);
|
||||
|
||||
if (handler) {
|
||||
// 使用 declarativeNetRequest 规则来处理认证
|
||||
await chrome.declarativeNetRequest.updateDynamicRules({
|
||||
removeRuleIds: [handler.id],
|
||||
addRules: [{
|
||||
id: parseInt(handler.id),
|
||||
priority: 1,
|
||||
action: {
|
||||
type: 'modifyHeaders',
|
||||
requestHeaders: [
|
||||
{
|
||||
header: 'Proxy-Authorization',
|
||||
operation: 'set',
|
||||
value: 'Basic ' + btoa(`${handler.username}:${handler.password}`)
|
||||
}
|
||||
]
|
||||
},
|
||||
condition: {
|
||||
domains: [handler.host],
|
||||
resourceTypes: ['main_frame', 'sub_frame', 'stylesheet', 'script', 'image', 'font', 'object', 'xmlhttprequest', 'ping', 'csp_report', 'media', 'websocket', 'other']
|
||||
}
|
||||
}]
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth error:', error);
|
||||
}
|
||||
},
|
||||
{ urls: ["<all_urls>"] }
|
||||
);
|
||||
}
|
||||
|
||||
static async saveAuthHandler(host, username, password) {
|
||||
const handler = {
|
||||
id: Date.now().toString(),
|
||||
host,
|
||||
username,
|
||||
password
|
||||
};
|
||||
await proxyStore.saveAuthHandler(handler);
|
||||
await this.setupAuthListener(); // 重新设置认证规则
|
||||
}
|
||||
|
||||
static async removeAuthHandler(host) {
|
||||
const handlers = await proxyStore.getAuthHandlers();
|
||||
const handler = handlers.find(h => h.host === host);
|
||||
if (handler) {
|
||||
await proxyStore.deleteAuthHandler(handler.id);
|
||||
// 移除对应的认证规则
|
||||
await chrome.declarativeNetRequest.updateDynamicRules({
|
||||
removeRuleIds: [parseInt(handler.id)]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static setupErrorHandler() {
|
||||
// 使用 storage 记录错误
|
||||
return {
|
||||
logError: async (error) => {
|
||||
const errors = await proxyStore.getErrors() || [];
|
||||
errors.push({
|
||||
timestamp: Date.now(),
|
||||
error: error.message || error
|
||||
});
|
||||
await proxyStore.saveErrors(errors.slice(-100)); // 只保留最近100条错误记录
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 设置代理认证信息
|
||||
static async setProxyAuth(username, password) {
|
||||
try {
|
||||
await proxyStore.saveAuth({ username, password, timestamp: Date.now() });
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error setting proxy auth:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取代理认证信息
|
||||
static async getProxyAuth() {
|
||||
try {
|
||||
return await proxyStore.getAuth();
|
||||
} catch (error) {
|
||||
console.error('Error getting proxy auth:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 清除代理认证信息
|
||||
static async clearProxyAuth() {
|
||||
try {
|
||||
await proxyStore.clearAuth();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error clearing proxy auth:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
import { proxyStore } from '../db/proxy-store.js';
|
||||
|
||||
// 日志数据库管理
|
||||
class ProxyLogs {
|
||||
async getResourceType(details) {
|
||||
try {
|
||||
// 首先检查请求类型
|
||||
if (details.type) {
|
||||
// 直接使用 Chrome 提供的类型
|
||||
switch (details.type) {
|
||||
case 'main_frame': return 'page';
|
||||
case 'xmlhttprequest': {
|
||||
// 检查请求头来区分 XHR 和 Fetch
|
||||
const isFetch = details.requestHeaders?.some(
|
||||
header => header.name.toLowerCase() === 'sec-fetch-mode' &&
|
||||
header.value === 'cors'
|
||||
);
|
||||
return isFetch ? 'fetch' : 'xhr';
|
||||
}
|
||||
case 'script': return 'script';
|
||||
case 'stylesheet': return 'stylesheet';
|
||||
case 'image': return 'image';
|
||||
case 'media': return 'media';
|
||||
case 'font': return 'font';
|
||||
case 'websocket': return 'websocket';
|
||||
}
|
||||
}
|
||||
|
||||
// 根据文件扩展名和内容类型判断
|
||||
const contentType = details.requestHeaders?.find(
|
||||
header => header.name.toLowerCase() === 'content-type'
|
||||
)?.value || '';
|
||||
|
||||
const url = new URL(details.url);
|
||||
const pathname = url.pathname.toLowerCase();
|
||||
|
||||
// 检查文件扩展名
|
||||
if (pathname.endsWith('.js')) return 'script';
|
||||
if (pathname.endsWith('.css')) return 'stylesheet';
|
||||
if (/\.(png|jpg|jpeg|gif|webp|svg|ico)$/.test(pathname)) return 'image';
|
||||
if (/\.(mp3|mp4|wav|ogg|webm)$/.test(pathname)) return 'media';
|
||||
if (/\.(woff|woff2|ttf|eot|otf)$/.test(pathname)) return 'font';
|
||||
|
||||
// 根据内容类型判断
|
||||
if (contentType) {
|
||||
if (contentType.includes('javascript')) return 'script';
|
||||
if (contentType.includes('css')) return 'stylesheet';
|
||||
if (contentType.includes('image/')) return 'image';
|
||||
if (contentType.includes('audio/') || contentType.includes('video/')) return 'media';
|
||||
if (contentType.includes('font/') || contentType.includes('application/font')) return 'font';
|
||||
if (contentType.includes('application/json')) return 'xhr';
|
||||
if (contentType.includes('application/x-www-form-urlencoded')) return 'xhr';
|
||||
}
|
||||
|
||||
// 检查 Accept 头
|
||||
const acceptHeader = details.requestHeaders?.find(
|
||||
header => header.name.toLowerCase() === 'accept'
|
||||
)?.value || '';
|
||||
|
||||
if (acceptHeader) {
|
||||
if (acceptHeader.includes('application/json')) return 'xhr';
|
||||
if (acceptHeader.includes('text/javascript')) return 'script';
|
||||
if (acceptHeader.includes('text/css')) return 'stylesheet';
|
||||
if (acceptHeader.includes('image/')) return 'image';
|
||||
}
|
||||
|
||||
console.log('Resource type detection:', {
|
||||
url: details.url,
|
||||
type: details.type,
|
||||
contentType,
|
||||
acceptHeader,
|
||||
headers: details.requestHeaders
|
||||
});
|
||||
|
||||
return 'other';
|
||||
} catch (error) {
|
||||
console.error('Error determining resource type:', error);
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
|
||||
async logRequest(details, proxyConfig, error = null) {
|
||||
try {
|
||||
// 检查是否是扩展自身的请求
|
||||
if (details.url.startsWith('chrome-extension://')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取资源类型
|
||||
const resourceType = await this.getResourceType(details);
|
||||
|
||||
const log = {
|
||||
id: Date.now().toString(),
|
||||
timestamp: Date.now(),
|
||||
url: details.url,
|
||||
proxyId: proxyConfig.id,
|
||||
proxyName: proxyConfig.name,
|
||||
status: error ? 'error' : 'success',
|
||||
errorMessage: error?.message,
|
||||
method: details.method,
|
||||
requestHeaders: details.requestHeaders?.reduce((acc, header) => {
|
||||
acc[header.name] = header.value;
|
||||
return acc;
|
||||
}, {}),
|
||||
requestBody: details.requestBody?.raw?.[0]?.bytes
|
||||
? decodeURIComponent(String.fromCharCode.apply(null, new Uint8Array(details.requestBody.raw[0].bytes)))
|
||||
: null,
|
||||
responseHeaders: details.responseHeaders?.reduce((acc, header) => {
|
||||
acc[header.name] = header.value;
|
||||
return acc;
|
||||
}, {}),
|
||||
timing: {
|
||||
startTime: details.timeStamp,
|
||||
endTime: Date.now(),
|
||||
duration: Date.now() - details.timeStamp
|
||||
},
|
||||
protocol: details.protocol || details.type,
|
||||
ip: details.ip,
|
||||
fromCache: details.fromCache,
|
||||
resourceType
|
||||
};
|
||||
|
||||
// 使用 proxyStore 存储日志
|
||||
await proxyStore.addLog(log);
|
||||
this.notifyLogUpdate();
|
||||
} catch (error) {
|
||||
console.error('Error logging proxy request:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async getLogs() {
|
||||
return await proxyStore.getLogs();
|
||||
}
|
||||
|
||||
async clearLogs() {
|
||||
await proxyStore.clearLogs();
|
||||
this.notifyLogUpdate();
|
||||
}
|
||||
|
||||
notifyLogUpdate() {
|
||||
// 通知前端日志已更新
|
||||
chrome.runtime.sendMessage({
|
||||
action: 'PROXY_LOGS_UPDATED'
|
||||
}).catch(() => {
|
||||
// 忽略接收者不存在的错误
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const proxyLogs = new ProxyLogs();
|
||||
@@ -1,51 +0,0 @@
|
||||
import { proxyStore } from '../db/proxy-store.js';
|
||||
|
||||
// 代理配置存储和管理
|
||||
export class ProxySettings {
|
||||
static async importSettings(settings) {
|
||||
try {
|
||||
if (Array.isArray(settings) && settings.every(s => s.proxyType)) {
|
||||
await proxyStore.saveProxyConfigs(settings);
|
||||
return {success: true};
|
||||
}
|
||||
return {success: false, error: "Invalid settings format"};
|
||||
} catch (error) {
|
||||
return {success: false, error: error.message};
|
||||
}
|
||||
}
|
||||
|
||||
static async exportSettings() {
|
||||
try {
|
||||
const configs = await proxyStore.getProxyConfigs();
|
||||
return {success: true, settings: configs || []};
|
||||
} catch (error) {
|
||||
return {success: false, error: error.message};
|
||||
}
|
||||
}
|
||||
|
||||
static async setDefaultConfigs() {
|
||||
const configs = await proxyStore.getProxyConfigs();
|
||||
if (!configs || configs.length === 0) {
|
||||
// 设置默认的直接连接配置
|
||||
await proxyStore.saveProxyConfigs([
|
||||
{
|
||||
id: 'direct',
|
||||
name: '直接连接',
|
||||
proxyType: 'direct',
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
name: '系统代理',
|
||||
proxyType: 'system',
|
||||
enabled: false
|
||||
}
|
||||
]);
|
||||
}
|
||||
// 确保日志存储已初始化
|
||||
const logs = await proxyStore.getLogs();
|
||||
if (!logs || logs.length === 0) {
|
||||
await proxyStore.clearLogs();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>My Sidepanel</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>All sites sidepanel extension</h1>
|
||||
<p>This side panel is enabled on all sites</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,134 +0,0 @@
|
||||
export const ActionType = {
|
||||
CONNECT: 'connect',
|
||||
SEND_MESSAGE: 'send_message',
|
||||
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",
|
||||
BADGE_COUNT: "yakit_badge",
|
||||
}
|
||||
|
||||
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) => {
|
||||
console.log("event", 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);
|
||||
};
|
||||
}
|
||||
|
||||
sendMessage(message) {
|
||||
if (this.isConnected()) {
|
||||
try {
|
||||
console.log("发射", message)
|
||||
this.socket.send(JSON.stringify(message));
|
||||
} catch (e) {
|
||||
console.error("Error sending message:", e);
|
||||
}
|
||||
} else {
|
||||
console.error("WebSocket is not connected.");
|
||||
}
|
||||
}
|
||||
|
||||
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(), 25000);
|
||||
}
|
||||
|
||||
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) {
|
||||
message = JSON.parse(message);
|
||||
if (message && message.type === "eval") {
|
||||
(async () => {
|
||||
const [tab] = await getTab();
|
||||
await injectScriptAndSendMessage(tab.id, {
|
||||
type: ActionType.INJECT_SCRIPT,
|
||||
value: {
|
||||
mode: "CONTENT_EVAL_CODE", code: message.code,
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getTab = async () => {
|
||||
return chrome.tabs.query({active: true, lastFocusedWindow: true})
|
||||
}
|
||||
|
||||
export const injectScriptAndSendMessage = async (tabId, message) => {
|
||||
try {
|
||||
// 注入 JS 脚本
|
||||
await chrome.scripting.executeScript({
|
||||
target: {tabId: tabId},
|
||||
files: ['content.js']
|
||||
});
|
||||
|
||||
// 发送消息
|
||||
const response = await chrome.tabs.sendMessage(tabId, message);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// 这个是插件 background 中使用的 action 类型
|
||||
// 和前端的 action 要保持一致
|
||||
export const ProxyActionType = {
|
||||
SET_PROXY_CONFIG: "SET_PROXY_CONFIG",
|
||||
CLEAR_PROXY_CONFIG: "CLEAR_PROXY_CONFIG",
|
||||
GET_PROXY_STATUS: "GET_PROXY_STATUS",
|
||||
GET_PROXY_LOGS: "GET_PROXY_LOGS",
|
||||
CLEAR_PROXY_LOGS: "CLEAR_PROXY_LOGS",
|
||||
GET_PROXY_CONFIGS: "GET_PROXY_CONFIGS",
|
||||
ADD_PROXY_CONFIG: "ADD_PROXY_CONFIG",
|
||||
UPDATE_PROXY_CONFIG: "UPDATE_PROXY_CONFIG",
|
||||
OPEN_OPTIONS_PAGE: "OPEN_OPTIONS_PAGE",
|
||||
};
|
||||
Reference in New Issue
Block a user