fix setting pac

This commit is contained in:
go0p
2026-08-06 15:11:35 +08:00
committed by go0p
parent 3ad7379800
commit 9dd9d37460
10 changed files with 1109 additions and 940 deletions
-1
View File
@@ -25,7 +25,6 @@ chrome.sidePanel.setOptions({
}); });
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log("msg", msg)
switch (msg.action) { switch (msg.action) {
case ActionType.CONNECT: case ActionType.CONNECT:
console.info("Start to connect websocket") console.info("Start to connect websocket")
+1 -1
View File
@@ -24,7 +24,7 @@
}, },
"content_scripts": [ "content_scripts": [
{ {
"matches": ["https://github.com/*"], "matches": ["http://mitm/"],
"run_at": "document_start", "run_at": "document_start",
"js": ["proxy/content.js"] "js": ["proxy/content.js"]
} }
+205 -166
View File
@@ -13,111 +13,13 @@ function getProxySettings() {
async function handleSetProxyConfig(config, sendResponse) { async function handleSetProxyConfig(config, sendResponse) {
try { try {
// 处理直接连接的情况
if (config.proxyType === 'direct') {
await new Promise((resolve) => {
chrome.proxy.settings.set({
value: {mode: "direct"},
scope: 'regular'
}, resolve);
});
const settings = await getProxySettings();
const isSuccess = settings.value.mode === "direct";
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('Direct connection set successfully');
sendResponse({success: true});
// 通知所有 content scripts 更新
await notifyProxyStatusChanged();
} else {
console.error('Failed to set direct connection');
sendResponse({
success: false,
error: '无法设置直接连接'
});
}
return;
}
// 添加系统代理的处理
if (config.proxyType === 'system') {
try {
// 先清除当前的代理设置
await new Promise((resolve) => {
chrome.proxy.settings.clear({
scope: 'regular'
}, resolve);
});
// 然后设置为系统代理
await new Promise((resolve) => {
chrome.proxy.settings.set({
value: {mode: "system"},
scope: 'regular'
}, resolve);
});
const settings = await getProxySettings();
const isSuccess = settings.value.mode === "system";
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('System proxy set successfully');
sendResponse({success: true});
// 通知所有 content scripts 更新
await notifyProxyStatusChanged();
} else {
console.error('Failed to set system proxy');
sendResponse({
success: false,
error: '无法设置系统代理'
});
}
} catch (error) {
console.error('Error setting system proxy:', error);
sendResponse({
success: false,
error: error.message || '设置系统代理时发生错误'
});
}
return;
}
// 处理代理服务器的情况 // 处理代理服务器的情况
if (config.proxyType === 'fixed_servers') {
// 固定代理服务器模式需要验证 host 和 port
if (!config || !config.host || !config.port) { if (!config || !config.host || !config.port) {
sendResponse({ sendResponse({
success: false, success: false,
error: '无效的代理配置' error: '无效的代理配置:缺少主机或端口'
}); });
return; return;
} }
@@ -130,7 +32,7 @@ async function handleSetProxyConfig(config, sendResponse) {
host: config.host, host: config.host,
port: parseInt(config.port) port: parseInt(config.port)
}, },
bypassList: ["localhost", "127.0.0.1"] bypassList: config.bypassList || ["localhost", "127.0.0.1"]
} }
}; };
@@ -171,6 +73,112 @@ async function handleSetProxyConfig(config, sendResponse) {
error: '代理设置验证失败' 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) { } catch (error) {
console.error('Error setting proxy:', error); console.error('Error setting proxy:', error);
sendResponse({ sendResponse({
@@ -276,51 +284,73 @@ async function queueProxyLog(details, error = null) {
} }
} }
// 添加 URL 拦截处理 // 添加检查和设置初始代理的函数
function setupUrlInterceptor() { async function checkAndSetInitialProxy() {
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
try { try {
const url = new URL(details.url); // 确保默认配置存在
await ProxySettings.setDefaultConfigs();
if (url.hostname === 'mitm') { // 获取当前的代理设置
// 解析参数 const settings = await getProxySettings();
const host = url.searchParams.get('host'); console.log('Current proxy settings:', settings);
const port = parseInt(url.searchParams.get('port') || '0');
const scheme = url.searchParams.get('scheme');
if (host && port && scheme) { // 检查是否存在固定代理服务器设置
// 创建新的代理配置 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 = { const newConfig = {
id: Date.now().toString(), id: Date.now().toString(),
name: "Yakit MITM", name: "Yakit MITM",
proxyType: 'fixed_servers', proxyType: 'fixed_servers',
scheme: scheme, scheme: proxy.scheme || 'http',
host: host, host: proxy.host,
port: port, port: proxy.port,
enabled: true enabled: true,
bypassList: ["localhost", "127.0.0.1"],
matchList: []
}; };
// 直接使用 proxyStore 实例的方法 // 添加到现有配置中
const result = await proxyStore.addAndEnableProxy(newConfig); const updatedConfigs = [...existingConfigs, newConfig];
await proxyStore.saveProxyConfigs(updatedConfigs);
if (result.success) { // 启用新配置
// 重定向回 mitm 页面 await handleSetProxyConfig(newConfig, () => {});
chrome.tabs.update(details.tabId, {
url: "http://mitm/" console.log('Added and enabled Yakit MITM config from existing proxy settings');
}); return;
} else {
console.error('Failed to add and enable proxy:', result.error);
}
} }
} }
// 如果没有检测到代理或已存在配置,则设置为系统代理
await handleSetProxyConfig({
id: 'system',
name: '[系统代理]',
proxyType: 'system',
enabled: true
}, () => {});
// 设置认证监听
await ProxyAuth.setupAuthListener();
} catch (error) { } catch (error) {
console.error('Error handling proxy URL:', error); console.error('Error during initialization:', error);
} }
}, {
url: [{
hostEquals: 'mitm'
}]
});
} }
// 修改 setupProxyHandlers 函数 // 修改 setupProxyHandlers 函数
@@ -332,9 +362,6 @@ export function setupProxyHandlers() {
// 设置代理请求监听器 // 设置代理请求监听器
setupProxyRequestListener(); setupProxyRequestListener();
// 设置 URL 拦截器
setupUrlInterceptor();
// 消息监听器 // 消息监听器
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log("Proxy message:", msg); console.log("Proxy message:", msg);
@@ -458,44 +485,17 @@ export function setupProxyHandlers() {
}); });
sendResponse({ success: true }); sendResponse({ success: true });
return true; return true;
return handleClearProxyConfig(sendResponse);
} }
}); });
// 在扩展启动时初始化 // 在扩展启动时初始化
chrome.runtime.onInstalled.addListener(async () => { chrome.runtime.onInstalled.addListener(async () => {
try { await checkAndSetInitialProxy();
// 确保默认配置存在
await ProxySettings.setDefaultConfigs();
// 设置为系统代理
await handleSetProxyConfig({
id: 'system',
name: '[系统代理]',
proxyType: 'system',
enabled: true
}, () => {});
// 设置认证监听
await ProxyAuth.setupAuthListener();
} catch (error) {
console.error('Error during installation:', error);
}
}); });
// 添加启动时初始化 // 浏览器启动时初始化
chrome.runtime.onStartup.addListener(async () => { chrome.runtime.onStartup.addListener(async () => {
try { await checkAndSetInitialProxy();
// 设置为系统代理
await handleSetProxyConfig({
id: 'system',
name: '[系统代理]',
proxyType: 'system',
enabled: true
}, () => {});
} catch (error) {
console.error('Error during startup:', error);
}
}); });
} }
@@ -510,3 +510,42 @@ async function notifyProxyStatusChanged() {
} }
} }
} }
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 };
}
}
+366 -205
View File
@@ -16,8 +16,7 @@ const YAK_ICON_URL = chrome.runtime.getURL('/images/yak.svg');
async function sendMessageWithRetry(message, maxRetries = 3) { async function sendMessageWithRetry(message, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) { for (let i = 0; i < maxRetries; i++) {
try { try {
const response = await chrome.runtime.sendMessage(message); return await chrome.runtime.sendMessage(message);
return response;
} catch (error) { } catch (error) {
console.warn(`Attempt ${i + 1} failed:`, error); console.warn(`Attempt ${i + 1} failed:`, error);
if (i === maxRetries - 1) { if (i === maxRetries - 1) {
@@ -36,40 +35,23 @@ async function getCurrentProxy() {
action: ProxyActionType.GET_PROXY_STATUS action: ProxyActionType.GET_PROXY_STATUS
}); });
if (!response) { if (!response || !response.success) {
console.log('No response from background script'); console.log('No valid response from background script');
return { enable: false, proxy: '' }; return { enable: false, proxy: '', currentMode: 'direct' };
}
// 检查响应格式
if (!response.success) {
console.error('Error in proxy status response:', response.error);
return { enable: false, proxy: '' };
} }
const status = response.data; const status = response.data;
console.log('Proxy status from background:', status); console.log('Proxy status from background:', status);
// 根据状态返回正确的格 // 返回当前模
if (status.mode === 'fixed_servers' && status.enabled && status.config) {
const config = status.config;
// 只在确实有配置时才返回代理信息
if (config.scheme && config.host && config.port) {
return { return {
enable: true, enable: status.enabled,
proxy: `${config.scheme}://${config.host}:${config.port}` proxy: status.mode === 'system' ? 'system' : '',
}; currentMode: status.mode || 'direct' // 使用 mode 来判断当前激活的代理
}
}
// 对于直连或系统代理,返回相应状态
return {
enable: false,
proxy: status.mode === 'system' ? 'system' : ''
}; };
} catch (error) { } catch (error) {
console.error('Error getting proxy status:', error); console.error('Error getting proxy status:', error);
return { enable: false, proxy: '' }; return { enable: false, proxy: '', currentMode: 'direct' };
} }
} }
@@ -99,12 +81,36 @@ async function getProxyConfigs() {
async function switchProxy(config) { async function switchProxy(config) {
try { try {
console.log('Switching proxy:', config); console.log('Switching proxy:', config);
// 根据配置类型构建正确的配置对象
let proxyConfig;
if (config.proxyType === 'system') {
proxyConfig = {
id: 'system',
name: '[系统代理]',
proxyType: 'system',
enabled: true
};
} else if (config.proxyType === 'direct') {
proxyConfig = {
id: 'direct',
name: '[直接连接]',
proxyType: 'direct',
enabled: false
};
} else {
proxyConfig = {
...config,
enabled: true
};
}
await sendMessageWithRetry({ await sendMessageWithRetry({
action: ProxyActionType.SET_PROXY_CONFIG, action: ProxyActionType.SET_PROXY_CONFIG,
config: config config: proxyConfig
}); });
await updatePanel(); await PanelManager.updatePanel();
} catch (error) { } catch (error) {
console.error('Error switching proxy:', error); console.error('Error switching proxy:', error);
} }
@@ -124,128 +130,67 @@ async function clearProxy() {
}); });
console.log('Clear proxy response:', response); console.log('Clear proxy response:', response);
await updatePanel(); // 使用 PanelManager 的 updatePanel 方法
await PanelManager.updatePanel();
} catch (error) { } catch (error) {
console.error('Error clearing proxy:', error); console.error('Error clearing proxy:', error);
} }
} }
// 更新面板显示 // 修改 PanelManager
async function updatePanel() { const PanelManager = {
const panel = document.getElementById('yakit-proxy-panel')?.shadowRoot?.querySelector('.panel-content'); panel: null,
if (!panel) return; messageListener: null,
_updating: false,
_updateQueue: Promise.resolve(),
_currentState: null, // 用于跟踪当前状态
_lastUpdate: null, // 添加最后更新时间戳
_lastState: null,
_pollingInterval: null,
const currentProxy = await getCurrentProxy(); init() {
const configs = await getProxyConfigs(); // 确保 document.body 存在
if (!document.body) {
console.log('Body not ready, waiting...');
this.waitForBody();
return;
}
console.log('Current proxy status:', currentProxy); if (this.panel) {
console.log('Panel already exists, updating...');
this.updatePanel();
return;
}
// 修改这里的判断逻辑 console.log('Creating new panel...');
let html = ` this.createPanel();
<div class="proxy-item ${!currentProxy.enable && currentProxy.proxy === '' ? 'active' : ''}" },
data-id="direct"
title="直接连接">
<span style="color: ${!currentProxy.enable && currentProxy.proxy === '' ? '#ff6b00' : '#666'}">🟢</span>
<span>[直接连接]</span>
<img src="${YAK_ICON_URL}" class="watermark-icon" alt="" />
</div>
<div class="proxy-item ${currentProxy.proxy === 'system' ? 'active' : ''}"
data-id="system"
title="系统代理">
<span style="color: ${currentProxy.proxy === 'system' ? '#ff6b00' : '#666'}">⚙️</span>
<span>[系统代理]</span>
<img src="${YAK_ICON_URL}" class="watermark-icon" alt="" />
</div>
<div class="divider"></div>
`;
// 修改自定义代理配置的判断逻辑 // 添加等待 body 的方法
configs.forEach(config => { waitForBody() {
if (config.id !== 'direct' && config.id !== 'system') { if (document.body) {
const proxyUrl = `${config.scheme}://${config.host}:${config.port}`; this.init();
const isActive = currentProxy.enable && currentProxy.proxy === proxyUrl && config.enabled; return;
}
// 添加 title 属性显示详细信息 console.log('Setting up MutationObserver for body');
const tooltipText = `${config.scheme.toUpperCase()} ${config.host}:${config.port}`; const observer = new MutationObserver((mutations, obs) => {
if (document.body) {
html += ` console.log('Body found via observer');
<div class="proxy-item ${isActive ? 'active' : ''}" obs.disconnect();
data-id="${config.id}" this.init();
title="${tooltipText}">
<span style="color: ${isActive ? '#ff6b00' : '#666'}">🌐</span>
<span>${config.name}</span>
<img src="${YAK_ICON_URL}" class="watermark-icon" alt="" />
</div>
`;
} }
}); });
// 修改添加操作按钮部分 observer.observe(document.documentElement, {
html += ` childList: true,
<div class="add-proxy"> subtree: true
<span></span> });
<span>添加代理...</span> },
</div>
<div class="settings">
<span>👨‍💻</span>
<span>选项</span>
</div>
`;
panel.innerHTML = html; createPanel() {
if (!document.body) {
// 添加事件监听 console.log('Body not available during panel creation');
panel.querySelectorAll('.proxy-item').forEach(item => {
item.addEventListener('click', async () => {
const id = item.dataset.id;
if (id === 'direct') {
await clearProxy();
} else if (id === 'system') {
await switchProxy({
id: 'system',
name: '[系统代理]',
proxyType: 'system'
});
} else {
const config = configs.find(c => c.id === id);
if (config) {
await switchProxy(config);
}
}
});
});
// 修改添加代理和选项按钮的事件处理
panel.querySelector('.add-proxy')?.addEventListener('click', async () => {
try {
// 通过发送消息给 background script 来处理添加代理
await sendMessageWithRetry({
action: 'OPEN_OPTIONS_PAGE',
triggerAdd: true // 标记需要触发添加代理
});
} catch (error) {
console.error('Error handling add proxy:', error);
}
});
panel.querySelector('.settings')?.addEventListener('click', async () => {
try {
// 通过发送消息给 background script 来打开选项页
await sendMessageWithRetry({
action: 'OPEN_OPTIONS_PAGE'
});
} catch (error) {
console.error('Error opening options page:', error);
}
});
}
// 创建并注入悬浮框
function createFloatingPanel() {
console.log("Creating floating panel...");
// 检查是否已存在面板
if (document.getElementById('yakit-proxy-panel')) {
return; return;
} }
@@ -459,94 +404,310 @@ function createFloatingPanel() {
shadow.appendChild(style); shadow.appendChild(style);
shadow.appendChild(panel); shadow.appendChild(panel);
// 将容器添加到页面 // 确保安全地添加到 body
try {
document.body.appendChild(container); document.body.appendChild(container);
this.panel = container;
// 添加事件监听器 // 添加折叠触发器的点击事件
const addEventListeners = () => { const floatingPanel = shadow.querySelector('.floating-panel');
// 收起/展开功能 const collapseTrigger = shadow.querySelector('.collapse-trigger');
panel.querySelector('.collapse-trigger')?.addEventListener('click', (e) => {
collapseTrigger.addEventListener('click', () => {
floatingPanel.classList.toggle('collapsed');
});
// 设置消息监听和开始轮询
this.setupMessageListener();
// 初始更新面板
this.updatePanel();
// 添加页面卸载时的清理
window.addEventListener('unload', () => {
if (this._pollingInterval) {
clearInterval(this._pollingInterval);
}
if (this.messageListener) {
chrome.runtime.onMessage.removeListener(this.messageListener);
}
});
} catch (error) {
console.error('Error creating panel:', error);
}
},
setupMessageListener() {
if (this.messageListener) {
chrome.runtime.onMessage.removeListener(this.messageListener);
}
this.messageListener = async (message, sender) => {
// 只检查消息是否来自同一个扩展
if (sender.id !== chrome.runtime.id) {
return;
}
// 处理状态更新消息
if (message.action === 'PROXY_STATUS_CHANGED' ||
message.action === 'PROXY_CONFIGS_UPDATED') {
console.log('Received update message:', message, 'from:', sender);
await this.updatePanel();
}
};
chrome.runtime.onMessage.addListener(this.messageListener);
},
async updatePanel() {
if (!this.panel || !document.body.contains(this.panel)) {
console.log('Panel not in document, recreating...');
this.createPanel();
return;
}
const panel = this.panel.shadowRoot?.querySelector('.panel-content');
if (!panel) return;
// 使用更新队列确保更新按顺序执行
this._updateQueue = this._updateQueue.then(async () => {
if (this._updating) {
console.log('Update already in progress, skipping...');
return;
}
try {
this._updating = true;
// 获取最新状态
const [currentProxy, configs] = await Promise.all([
getCurrentProxy(),
getProxyConfigs()
]);
// 状态没有变化时不更新
const newState = JSON.stringify({ currentProxy, configs });
if (this._currentState === newState) {
console.log('State unchanged, skipping update');
return;
}
this._currentState = newState;
// 再次检查面板状态
if (!this.panel || !document.body.contains(this.panel)) {
console.log('Panel was removed during data fetch');
return;
}
console.log('Updating panel with:', { currentProxy, configs });
if (!Array.isArray(configs)) {
console.error('Invalid configs:', configs);
return;
}
// 保存当前激活的项
const currentActiveId = panel.querySelector('.proxy-item.active')?.dataset.id;
// 构建新的 HTML
const newHtml = this._buildPanelHtml(currentProxy, configs);
// 创建一个临时容器来比较内容
const temp = document.createElement('div');
temp.innerHTML = newHtml;
// 只在内容真正改变时更新
if (panel.innerHTML !== temp.innerHTML) {
requestAnimationFrame(() => {
panel.innerHTML = newHtml;
this._bindEventListeners(panel, configs, currentProxy);
// 验证更新后的状态
const newActiveId = panel.querySelector('.proxy-item.active')?.dataset.id;
if (currentActiveId !== newActiveId) {
console.log('Active state changed:', {
from: currentActiveId,
to: newActiveId
});
}
});
}
} catch (error) {
console.error('Error updating panel:', error);
} finally {
this._updating = false;
}
}).catch(error => {
console.error('Error in update queue:', error);
this._updating = false;
});
return this._updateQueue;
},
// 将 HTML 构建逻辑抽离成单独的方法
_buildPanelHtml(currentProxy, configs) {
let html = `
<div class="proxy-item ${currentProxy.currentMode === 'direct' ? 'active' : ''}"
data-id="direct"
title="直接连接">
<span style="color: ${currentProxy.currentMode === 'direct' ? '#ff6b00' : '#666'}">🟢</span>
<span>[直接连接]</span>
<img src="${YAK_ICON_URL}" class="watermark-icon" alt="" />
</div>
<div class="proxy-item ${currentProxy.currentMode === 'system' ? 'active' : ''}"
data-id="system"
title="系统代理">
<span style="color: ${currentProxy.currentMode === 'system' ? '#ff6b00' : '#666'}">⚙️</span>
<span>[系统代理]</span>
<img src="${YAK_ICON_URL}" class="watermark-icon" alt="" />
</div>
<div class="divider"></div>
`;
// 添加自定义代理配置
configs.forEach(config => {
if (config.proxyType !== 'direct' && config.proxyType !== 'system') {
// 判断是否激活:当前模式为 fixed_servers 且配置已启用
const isActive = currentProxy.currentMode === 'fixed_servers' &&
config.enabled;
const tooltipText = config.proxyType === 'pac_script'
? 'PAC Script'
: `${(config.proxyType || 'HTTP').toUpperCase()} ${config.host || ''}:${config.port || ''}`;
const proxyIcon = config.proxyType === 'pac_script' ? '📜' : '🌐';
html += `
<div class="proxy-item ${isActive ? 'active' : ''}"
data-id="${config.id}"
title="${tooltipText}">
<span style="color: ${isActive ? '#ff6b00' : '#666'}">${proxyIcon}</span>
<span>${config.name || '未命名代理'}</span>
<img src="${YAK_ICON_URL}" class="watermark-icon" alt="" />
</div>
`;
}
});
// 添加操作按钮
html += `
<div class="add-proxy">
<span></span>
<span>添加代理...</span>
</div>
<div class="settings">
<span>👨‍💻</span>
<span>选项</span>
</div>
`;
return html;
},
_bindEventListeners(panel, configs, currentProxy) {
// 代理项点击事件
panel.querySelectorAll('.proxy-item').forEach(item => {
const id = item.dataset.id;
// 使用事件委托来提高性能
const clickHandler = async (e) => {
e.stopPropagation(); e.stopPropagation();
panel.classList.toggle('collapsed');
if (this._updating) {
console.log('Panel is updating, ignoring click');
return;
}
// 添加点击反馈
const originalOpacity = item.style.opacity;
item.style.opacity = '0.7';
try {
// 立即更新 UI 状态,不等待响应
panel.querySelectorAll('.proxy-item').forEach(i => {
i.classList.remove('active');
i.querySelector('span').style.color = '#666';
});
item.classList.add('active');
item.querySelector('span').style.color = '#ff6b00';
if (id === 'direct') {
await clearProxy();
} else if (id === 'system') {
await switchProxy({
id: 'system',
name: '[系统代理]',
proxyType: 'system'
});
} else {
const config = configs.find(c => c.id === id);
if (config) {
await switchProxy(config);
}
}
} catch (error) {
console.error('Error handling proxy item click:', error);
// 发生错误时恢复原状
await this.updatePanel();
} finally {
item.style.opacity = originalOpacity;
}
};
// 使用 { once: true } 确保事件监听器不会重复
item.addEventListener('click', clickHandler, { once: true });
}); });
// 添加代理按钮 // 添加代理按钮
panel.querySelector('.add-proxy')?.addEventListener('click', () => { panel.querySelector('.add-proxy')?.addEventListener('click', async () => {
chrome.runtime.sendMessage({ action: 'OPEN_OPTIONS_PAGE' }); try {
await sendMessageWithRetry({
action: 'OPEN_OPTIONS_PAGE',
triggerAdd: true
});
} catch (error) {
console.error('Error handling add proxy:', error);
}
}); });
// 设置按钮 // 设置按钮
panel.querySelector('.settings')?.addEventListener('click', () => { panel.querySelector('.settings')?.addEventListener('click', async () => {
chrome.runtime.sendMessage({ action: 'OPEN_OPTIONS_PAGE' }); try {
await sendMessageWithRetry({
action: 'OPEN_OPTIONS_PAGE'
}); });
}; } catch (error) {
console.error('Error opening options page:', error);
// 初始化事件监听器
addEventListeners();
// 初始更新面板
updatePanel();
// 添加代理变化监听
let messageListener = (message) => {
if (message.action === 'PROXY_STATUS_CHANGED') {
updatePanel();
}
};
// 确保只添加一次监听器
chrome.runtime.onMessage.removeListener(messageListener);
chrome.runtime.onMessage.addListener(messageListener);
// 验证面板是否成功创建
console.log("Panel created:", {
containerExists: !!document.getElementById('yakit-proxy-panel'),
containerVisible: window.getComputedStyle(container).display !== 'none',
shadowRoot: !!container.shadowRoot,
panelElement: !!container.shadowRoot?.querySelector('.floating-panel')
});
}
// 使用 MutationObserver 确保在 DOM 准备好时创建面板
function initPanel() {
if (document.body) {
console.log("Body found, creating panel");
// 确保background script已经准备好
sendMessageWithRetry({ action: 'PING' })
.then(() => {
createFloatingPanel();
})
.catch(error => {
console.error('Failed to initialize panel:', error);
// 可以在这里添加重试逻辑
setTimeout(initPanel, 1000);
});
} else {
console.log("Body not found, waiting...");
const observer = new MutationObserver((mutations, obs) => {
if (document.body) {
console.log("Body found via observer");
obs.disconnect();
initPanel();
} }
}); });
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
} }
} };
// 尝试多种方式来确保面板被创建 // 修改初始化调用
console.log("Setting up initialization..."); console.log("Setting up initialization...");
// 根据文档状态决定初始化方式
if (document.readyState === 'loading') {
console.log('Document still loading, waiting for DOMContentLoaded');
document.addEventListener('DOMContentLoaded', () => {
console.log('DOMContentLoaded fired');
PanelManager.init();
});
} else {
console.log('Document already loaded, initializing immediately');
PanelManager.init();
}
// 保留 load 事件作为备份
window.addEventListener('load', () => { window.addEventListener('load', () => {
console.log("Window load triggered"); console.log("Window load triggered");
initPanel(); if (!PanelManager.panel) {
PanelManager.init();
}
}); });
// 在关键位置添加更多日志 // 添加更详细的日志
console.log("Document readyState:", document.readyState); console.log("Document readyState:", document.readyState);
console.log("Document body exists:", !!document.body); console.log("Document body exists:", !!document.body);
console.log("Document documentElement exists:", !!document.documentElement); console.log("Document documentElement exists:", !!document.documentElement);
-167
View File
@@ -1,167 +0,0 @@
console.log('Options page script loaded');
import { ProxySettings } from './proxy-settings.js';
let currentConfigs = [];
document.addEventListener('DOMContentLoaded', async () => {
try {
await initOptionsPage();
setupEventListeners();
} catch (error) {
console.error('Failed to initialize options page:', error);
showError(error.message);
}
});
async function initOptionsPage() {
const result = await ProxySettings.exportSettings();
currentConfigs = result.settings || [];
renderProxyConfigs();
}
function renderProxyConfigs() {
const proxyList = document.getElementById('proxyList');
const template = document.getElementById('proxyItemTemplate');
// 清除除了直接连接以外的所有配置
const items = proxyList.querySelectorAll('.proxy-item:not([data-id="direct"])');
items.forEach(item => item.remove());
// 渲染其他代理配置
currentConfigs.forEach(config => {
if (config.id === 'direct') return; // 跳过直接连接
const clone = template.content.cloneNode(true);
const proxyItem = clone.querySelector('.proxy-item');
proxyItem.dataset.id = config.id;
proxyItem.querySelector('.proxy-name').value = config.name;
proxyItem.querySelector('.proxy-type-select').value = config.proxyType;
updateProxyTypeSettings(proxyItem, config);
proxyList.appendChild(proxyItem);
});
}
function updateProxyTypeSettings(proxyItem, config) {
const serverSettings = proxyItem.querySelector('.proxy-server-settings');
const pacSettings = proxyItem.querySelector('.pac-script-settings');
if (config.proxyType === 'fixed_servers') {
serverSettings.style.display = 'block';
pacSettings.style.display = 'none';
proxyItem.querySelector('.proxy-scheme').value = config.scheme || 'http';
proxyItem.querySelector('.proxy-host').value = config.host || '';
proxyItem.querySelector('.proxy-port').value = config.port || '';
}
else if (config.proxyType === 'pac_script') {
serverSettings.style.display = 'none';
pacSettings.style.display = 'block';
proxyItem.querySelector('.pac-script').value = config.pacScript || '';
}
else {
serverSettings.style.display = 'none';
pacSettings.style.display = 'none';
}
}
function setupEventListeners() {
// 添加代理按钮
document.getElementById('addProxy').addEventListener('click', addNewProxy);
// 代理列表变更事件
document.getElementById('proxyList').addEventListener('change', handleProxyChange);
// 删除代理按钮
document.getElementById('proxyList').addEventListener('click', handleProxyDelete);
}
async function addNewProxy() {
const newConfig = {
id: Date.now().toString(),
name: '新建代理',
proxyType: 'fixed_servers',
scheme: 'http',
host: '127.0.0.1',
port: 8080
};
currentConfigs = [...currentConfigs, newConfig];
await ProxySettings.importSettings(currentConfigs);
renderProxyConfigs();
}
async function handleProxyChange(e) {
const proxyItem = e.target.closest('.proxy-item');
if (!proxyItem) return;
const configId = proxyItem.dataset.id;
const configIndex = currentConfigs.findIndex(c => c.id === configId);
if (configIndex === -1) return;
const updatedConfig = { ...currentConfigs[configIndex] };
if (e.target.classList.contains('proxy-name')) {
updatedConfig.name = e.target.value;
} else if (e.target.classList.contains('proxy-type-select')) {
updatedConfig.proxyType = e.target.value;
// 如果切换到固定服务器模式,设置默认值
if (updatedConfig.proxyType === 'fixed_servers' && !updatedConfig.scheme) {
updatedConfig.scheme = 'http';
updatedConfig.host = '127.0.0.1';
updatedConfig.port = 8080;
}
} else if (e.target.classList.contains('proxy-scheme')) {
updatedConfig.scheme = e.target.value;
} else if (e.target.classList.contains('proxy-host')) {
updatedConfig.host = e.target.value;
} else if (e.target.classList.contains('proxy-port')) {
updatedConfig.port = parseInt(e.target.value) || '';
} else if (e.target.classList.contains('pac-script')) {
updatedConfig.pacScript = e.target.value;
}
currentConfigs[configIndex] = updatedConfig;
await ProxySettings.importSettings(currentConfigs);
// 如果代理类型改变,需要更新UI
if (e.target.classList.contains('proxy-type-select')) {
updateProxyTypeSettings(proxyItem, updatedConfig);
}
}
async function handleProxyDelete(e) {
const deleteBtn = e.target.closest('.delete-btn');
if (!deleteBtn) return;
const proxyItem = deleteBtn.closest('.proxy-item');
const configId = proxyItem.dataset.id;
currentConfigs = currentConfigs.filter(c => c.id !== configId);
await ProxySettings.importSettings(currentConfigs);
renderProxyConfigs();
}
function showError(message) {
const container = document.querySelector('.container');
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.style.cssText = `
color: #ff4d4f;
padding: 8px 16px;
margin-bottom: 16px;
background-color: #fff2f0;
border: 1px solid #ffccc7;
border-radius: 4px;
`;
errorDiv.textContent = message;
container.insertBefore(errorDiv, container.firstChild);
// 5秒后自动移除错误信息
setTimeout(() => {
errorDiv.remove();
}, 3000);
}
+1
View File
@@ -9,4 +9,5 @@ export const ProxyActionType = {
GET_PROXY_CONFIGS: "GET_PROXY_CONFIGS", GET_PROXY_CONFIGS: "GET_PROXY_CONFIGS",
ADD_PROXY_CONFIG: "ADD_PROXY_CONFIG", ADD_PROXY_CONFIG: "ADD_PROXY_CONFIG",
UPDATE_PROXY_CONFIG: "UPDATE_PROXY_CONFIG", UPDATE_PROXY_CONFIG: "UPDATE_PROXY_CONFIG",
OPEN_OPTIONS_PAGE: "OPEN_OPTIONS_PAGE",
}; };
+32 -48
View File
@@ -57,12 +57,25 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = () => {
const [customProxies, setCustomProxies] = useState<CustomProxy[]>([]); const [customProxies, setCustomProxies] = useState<CustomProxy[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
// 修改存储变化监听
useEffect(() => {
const handleMessage = (message: any) => {
if (message.action === 'PROXY_CONFIGS_UPDATED' && message.source !== 'proxy_switch') {
loadCustomProxies();
}
};
chrome.runtime.onMessage.addListener(handleMessage);
return () => {
chrome.runtime.onMessage.removeListener(handleMessage);
};
}, []);
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
await Promise.all([ // 修改初始化逻辑,避免并行请求
loadProxyStatus(), await loadProxyStatus();
loadCustomProxies() await loadCustomProxies();
]);
setInitialized(true); setInitialized(true);
}; };
init(); init();
@@ -93,39 +106,6 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = () => {
const loadCustomProxies = async () => { const loadCustomProxies = async () => {
try { try {
// 首先检查当前是否在 options 页面的上下文中
const currentUrl = window.location.href;
const isInOptionsContext = currentUrl.includes('chrome-extension://') && currentUrl.includes('options.html');
if (isInOptionsContext) {
// 如果在 options 页面上下文中,直接使用消息通信
const response = await chrome.runtime.sendMessage({
action: ProxyActionType.GET_PROXY_CONFIGS
});
if (response?.success && response.data) {
const proxies = response.data
.filter((proxy: ProxyConfig) => !FIXED_MODES.some(mode => mode.key === proxy.id))
.map((proxy: ProxyConfig): CustomProxy => ({
key: proxy.id,
name: proxy.name,
color: '#1890ff',
config: proxy,
enabled: proxy.enabled
}));
setCustomProxies(proxies);
const enabledProxy = response.data.find((proxy: ProxyConfig) => proxy.enabled);
if (enabledProxy) {
setCurrentMode(enabledProxy.id);
} else {
setCurrentMode('direct');
}
return;
}
}
// 如果不在 options 页面上下文中,使用 IndexedDB
const DB_NAME = 'yaklang_extension'; const DB_NAME = 'yaklang_extension';
const STORE_NAME = 'proxy_configs'; const STORE_NAME = 'proxy_configs';
@@ -165,23 +145,18 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = () => {
const enabledProxy = configs.find((proxy: ProxyConfig) => proxy.enabled); const enabledProxy = configs.find((proxy: ProxyConfig) => proxy.enabled);
if (enabledProxy) { if (enabledProxy) {
setCurrentMode(enabledProxy.id); setCurrentMode(enabledProxy.id);
} else {
setCurrentMode('direct');
} }
} catch (error) { } catch (error) {
console.error('Error loading custom proxies:', error); console.error('Error loading custom proxies:', error);
setCustomProxies([]); setCustomProxies([]);
setCurrentMode('direct');
} }
}; };
const handleModeChange = async (mode: string) => { const handleModeChange = async (mode: string) => {
if (mode === 'setting' || mode === 'add') {
if (mode === 'setting') { if (mode === 'setting') {
await chrome.runtime.openOptionsPage?.(); await chrome.runtime.openOptionsPage?.();
return;
} }
if (mode === 'add') { if (mode === 'add') {
try { try {
const [activeTab] = await chrome.tabs.query({ const [activeTab] = await chrome.tabs.query({
@@ -213,6 +188,7 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = () => {
} catch (error) { } catch (error) {
console.error('Failed to get current tab:', error); console.error('Failed to get current tab:', error);
} }
}
return; return;
} }
@@ -227,8 +203,8 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = () => {
return; return;
} }
// 立即更新UI状态
setCurrentMode(mode); setCurrentMode(mode);
if (customProxy) { if (customProxy) {
setCustomProxies(prev => prev.map(p => ({ setCustomProxies(prev => prev.map(p => ({
...p, ...p,
@@ -238,16 +214,20 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = () => {
const response = await chrome.runtime.sendMessage({ const response = await chrome.runtime.sendMessage({
action: ProxyActionType.SET_PROXY_CONFIG, action: ProxyActionType.SET_PROXY_CONFIG,
config config,
// 添加一个标志,表示这是从 ProxySwitch 发起的更改
source: 'proxy_switch'
}); });
if (response?.success === false) { if (response?.success === false) {
throw new Error(response.error || '设置代理失败'); throw new Error(response.error || '设置代理失败');
} }
await loadCustomProxies(); // 不需要重新加载,因为我们已经更新了本地状态
} catch (error) { } catch (error) {
console.error('Error applying proxy config:', error); console.error('Error applying proxy config:', error);
// 发生错误时才重新加载以确保状态正确
await loadCustomProxies();
throw error; throw error;
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -265,13 +245,17 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = () => {
{ type: 'divider' }, { type: 'divider' },
...customProxies.map(proxy => ({ ...customProxies.map(proxy => ({
key: proxy.key, key: proxy.key,
icon: <span className="menu-icon" style={{ color: proxy.color }}><GlobalOutlined /></span>, icon: <span className="menu-icon" style={{ color: proxy.color }}>
{proxy.config.proxyType === 'pac_script' ? '📜' : <GlobalOutlined />}
</span>,
label: <span style={{ label: <span style={{
color: currentMode === proxy.key ? 'var(--yakit-primary)' : 'inherit', color: currentMode === proxy.key ? 'var(--yakit-primary)' : 'inherit',
opacity: isLoading ? 0.7 : 1 opacity: isLoading ? 0.7 : 1
}}>{proxy.name}</span>, }}>{proxy.name}</span>,
className: `${currentMode === proxy.key ? 'menu-item-selected' : ''} ${isLoading ? 'menu-item-loading' : ''}`, className: `${currentMode === proxy.key ? 'menu-item-selected' : ''} ${isLoading ? 'menu-item-loading' : ''}`,
title: `${proxy.config.scheme.toUpperCase()} ${proxy.config.host}:${proxy.config.port}` title: proxy.config.scheme
? `${proxy.config.scheme.toUpperCase()} ${proxy.config.host}:${proxy.config.port}`
: `${proxy.config.host}:${proxy.config.port}`
})), })),
{ {
key: 'add', key: 'add',
@@ -4,6 +4,7 @@ import type { ColumnsType } from 'antd/es/table';
import { DeleteOutlined, PlusOutlined, EditOutlined, CheckOutlined } from '@ant-design/icons'; import { DeleteOutlined, PlusOutlined, EditOutlined, CheckOutlined } from '@ant-design/icons';
import { ProxyConfig } from '@/types/proxy'; import { ProxyConfig } from '@/types/proxy';
import './index.css'; import './index.css';
import punycode from 'punycode';
interface ProxySettingsProps { interface ProxySettingsProps {
proxyConfigs: ProxyConfig[]; proxyConfigs: ProxyConfig[];
@@ -14,7 +15,7 @@ interface ProxySettingsProps {
onClear: (configId: string) => Promise<void>; onClear: (configId: string) => Promise<void>;
} }
// 添加编辑表单的接口 // 修改 EditFormData 接口
interface EditFormData { interface EditFormData {
name: string; name: string;
proxyType: "direct" | "system" | "fixed_servers" | "pac_script" | "auto_detect"; proxyType: "direct" | "system" | "fixed_servers" | "pac_script" | "auto_detect";
@@ -22,6 +23,9 @@ interface EditFormData {
host?: string; host?: string;
port?: number; port?: number;
pacScript?: string; pacScript?: string;
bypassList?: string;
matchList?: string; // 仅用于 UI 编辑
proxyServer?: string; // 添加 proxyServer 字段,用于 PAC 脚本模式选择代理服务器
} }
// 或者更好的方式是创建一个专门的类型 // 或者更好的方式是创建一个专门的类型
@@ -48,7 +52,11 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
scheme: editingConfig.scheme, scheme: editingConfig.scheme,
host: editingConfig.host, host: editingConfig.host,
port: editingConfig.port, port: editingConfig.port,
pacScript: editingConfig.pacScript bypassList: editingConfig.bypassList?.join('\n') || '',
matchList: editingConfig.matchList?.join('\n') || '',
proxyServer: editingConfig.host && editingConfig.port
? `${editingConfig.host}:${editingConfig.port}`
: undefined
}); });
} }
}, [editModalVisible, editingConfig, form]); }, [editModalVisible, editingConfig, form]);
@@ -73,6 +81,17 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
setEditModalVisible(true); setEditModalVisible(true);
}; };
// 添加一个函数来获取可用的代理服务器列表
const getAvailableProxies = (configs: ProxyConfig[]) => {
return configs
.filter(config => config.proxyType === 'fixed_servers')
.map(config => ({
label: `${config.name} (${config.scheme}://${config.host}:${config.port})`,
value: `${config.host}:${config.port}`,
config
}));
};
// 处理编辑保存 // 处理编辑保存
const handleEditSave = async () => { const handleEditSave = async () => {
try { try {
@@ -82,12 +101,107 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
await onClear(editingConfig.id); await onClear(editingConfig.id);
} }
const updatedConfig: ProxyConfig = { let updatedConfig: ProxyConfig;
...editingConfig,
...values, if (values.proxyType === 'fixed_servers') {
proxyType: values.proxyType as "direct" | "system" | "fixed_servers" | "pac_script" | "auto_detect", // 处理固定代理服务器模式
scheme: values.scheme as "http" | "https" | "socks4" | "socks5" const bypassList = values.bypassList
? values.bypassList.split('\n').map(line => line.trim()).filter(line => line.length > 0)
: ["localhost", "127.0.0.1"];
updatedConfig = {
id: editingConfig.id,
name: values.name,
enabled: editingConfig.enabled,
proxyType: 'fixed_servers',
scheme: values.scheme,
host: values.host,
port: values.port,
bypassList,
}; };
} else if (values.proxyType === 'pac_script') {
const domains = values.matchList
? values.matchList.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0)
.map(domain => {
try {
// 如果域名包含非 ASCII 字符,转换为 Punycode
if (/[^\x00-\x7F]/.test(domain)) {
if (domain.startsWith('*.')) {
const suffix = domain.substring(2);
return '*.' + suffix.split('.').map(part => {
return /[^\x00-\x7F]/.test(part) ? 'xn--' + punycode.encode(part) : part;
}).join('.');
} else {
return domain.split('.').map(part => {
return /[^\x00-\x7F]/.test(part) ? 'xn--' + punycode.encode(part) : part;
}).join('.');
}
}
return domain;
} catch (error) {
console.error('Error encoding domain:', domain, error);
return domain;
}
})
: [];
// 从选择的代理服务器中获取配置
const [host, port] = values.proxyServer.split(':');
// 生成 PAC 脚本
const pacScriptContent = `
function FindProxyForURL(url, host) {
// Convert host to lowercase for case-insensitive matching
host = host.toLowerCase();
// Define domain patterns
var domains = ${JSON.stringify(domains)};
// Check each domain pattern
for (var i = 0; i < domains.length; i++) {
var pattern = domains[i].toLowerCase();
if (pattern.startsWith('*.')) {
var suffix = pattern.substring(2);
if (host === suffix || host.endsWith('.' + suffix)) {
return 'PROXY ${host}:${port}';
}
} else if (host === pattern) {
return 'PROXY ${host}:${port}';
}
}
return 'DIRECT';
}`;
updatedConfig = {
id: editingConfig.id,
name: values.name,
enabled: editingConfig.enabled,
proxyType: 'pac_script',
mode: 'pac_script',
// 保存代理服务器信息
host,
port: parseInt(port),
// 保存匹配域名列表
matchList: domains,
pacScript: {
data: pacScriptContent,
mandatory: true
}
};
} else {
// 处理其他模式
updatedConfig = {
id: editingConfig.id,
name: values.name,
enabled: editingConfig.enabled,
proxyType: values.proxyType,
bypassList: [], // 其他模式下设置为空数组
};
}
if (!proxyConfigs.find(config => config.id === editingConfig.id)) { if (!proxyConfigs.find(config => config.id === editingConfig.id)) {
await onAdd(updatedConfig); await onAdd(updatedConfig);
@@ -232,7 +346,7 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
> >
<Select <Select
options={[ options={[
{ label: '直接连接', value: 'direct' }, // { label: '直接连接', value: 'direct' },
{ label: '代理服务器', value: 'fixed_servers' }, { label: '代理服务器', value: 'fixed_servers' },
{ label: 'PAC 脚本', value: 'pac_script' } { label: 'PAC 脚本', value: 'pac_script' }
]} ]}
@@ -254,7 +368,8 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
> >
{({ getFieldValue }) => { {({ getFieldValue }) => {
const proxyType = getFieldValue('proxyType'); const proxyType = getFieldValue('proxyType');
return proxyType === 'fixed_servers' ? ( if (proxyType === 'fixed_servers') {
return (
<> <>
<Form.Item <Form.Item
name="scheme" name="scheme"
@@ -289,16 +404,54 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
style={{ width: '100%' }} style={{ width: '100%' }}
/> />
</Form.Item> </Form.Item>
</>
) : proxyType === 'pac_script' ? (
<Form.Item <Form.Item
name="pacScript" name="bypassList"
label="PAC 脚本" label="不经过代理的地址"
rules={[{ required: true, message: '请输入 PAC 脚本' }]} help="每行一个地址,支持通配符 *"
> >
<Input.TextArea rows={4} /> <Input.TextArea
rows={4}
placeholder={`例如:
localhost
127.0.0.1
*.example.com`}
/>
</Form.Item> </Form.Item>
) : null; </>
);
} else if (proxyType === 'pac_script') {
const availableProxies = getAvailableProxies(proxyConfigs);
return (
<>
<Form.Item
name="matchList"
label="匹配域名"
help="每行一个域名,支持通配符 *"
rules={[{ required: true, message: '请输入至少一个匹配域名' }]}
>
<Input.TextArea
rows={4}
placeholder={`例如:
*.example.com
google.com
github.com`}
/>
</Form.Item>
<Form.Item
name="proxyServer"
label="选择代理服务器"
rules={[{ required: true, message: '请选择代理服务器' }]}
>
<Select
placeholder="选择一个代理服务器"
options={availableProxies}
/>
</Form.Item>
</>
);
}
return null;
}} }}
</Form.Item> </Form.Item>
</Form> </Form>
+11 -21
View File
@@ -9,19 +9,16 @@ export const useProxyConfigs = () => {
useEffect(() => { useEffect(() => {
loadConfigs(); loadConfigs();
// 监听配置更新 const messageListener = (message: any) => {
const handleConfigUpdate = () => { if (message.action === 'PROXY_CONFIGS_UPDATED') {
loadConfigs(); loadConfigs();
}
}; };
chrome.runtime.onMessage.addListener((message) => { chrome.runtime.onMessage.addListener(messageListener);
if (message.action === 'PROXY_CONFIGS_UPDATED') {
handleConfigUpdate();
}
});
return () => { return () => {
chrome.runtime.onMessage.removeListener(handleConfigUpdate); chrome.runtime.onMessage.removeListener(messageListener);
}; };
}, []); }, []);
@@ -70,6 +67,9 @@ export const useProxyConfigs = () => {
if (response?.success) { if (response?.success) {
setProxyConfigs(response.data || updatedConfigs); setProxyConfigs(response.data || updatedConfigs);
await chrome.runtime.sendMessage({
action: 'PROXY_CONFIGS_UPDATED'
});
message.success('更新配置成功'); message.success('更新配置成功');
} else { } else {
message.error(response?.error || '更新配置失败'); message.error(response?.error || '更新配置失败');
@@ -88,7 +88,6 @@ export const useProxyConfigs = () => {
const updatedConfigs = proxyConfigs.filter(config => config.id !== configId); const updatedConfigs = proxyConfigs.filter(config => config.id !== configId);
console.log('Updated configs after delete:', updatedConfigs); console.log('Updated configs after delete:', updatedConfigs);
// 使用 Promise 包装消息发送
const response = await new Promise<any>((resolve) => { const response = await new Promise<any>((resolve) => {
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
action: ProxyActionType.UPDATE_PROXY_CONFIG, action: ProxyActionType.UPDATE_PROXY_CONFIG,
@@ -121,24 +120,15 @@ export const useProxyConfigs = () => {
const config = proxyConfigs.find(c => c.id === configId); const config = proxyConfigs.find(c => c.id === configId);
if (!config) return; if (!config) return;
// 先取消当前启用的代理
const currentEnabled = proxyConfigs.find(c => c.enabled);
if (currentEnabled && currentEnabled.id !== configId) {
// 如果当前启用的不是直接连接,需要先清除代理设置
if (currentEnabled.id !== 'direct') {
await chrome.runtime.sendMessage({
action: ProxyActionType.CLEAR_PROXY_CONFIG
});
}
}
// 应用新的代理设置
const response = await chrome.runtime.sendMessage({ const response = await chrome.runtime.sendMessage({
action: ProxyActionType.SET_PROXY_CONFIG, action: ProxyActionType.SET_PROXY_CONFIG,
config: config config: config
}); });
if (response.success) { if (response.success) {
await chrome.runtime.sendMessage({
action: 'PROXY_STATUS_CHANGED'
});
message.success('代理设置已应用'); message.success('代理设置已应用');
} else { } else {
message.error(response.error || '应用代理设置失败'); message.error(response.error || '应用代理设置失败');
+10 -1
View File
@@ -1,12 +1,21 @@
export interface PacScript {
data?: string;
url?: string;
mandatory?: boolean;
}
export interface ProxyConfig { export interface ProxyConfig {
id: string; id: string;
name: string; name: string;
enabled: boolean; enabled: boolean;
proxyType: "direct" | "system" | "fixed_servers" | "pac_script" | "auto_detect"; proxyType: "direct" | "system" | "fixed_servers" | "pac_script" | "auto_detect";
mode?: string;
host?: string; host?: string;
port?: number; port?: number;
scheme?: "http" | "https" | "socks4" | "socks5"; scheme?: "http" | "https" | "socks4" | "socks5";
pacScript?: string; pacScript?: PacScript;
bypassList?: string[];
matchList?: string[];
} }
export interface ProxyLog { export interface ProxyLog {