mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-04 11:43:57 +08:00
feat(proxy): add local proxy auto-scan and fix hot-reload
- Add scan_local_proxies command to detect common proxy ports - Fix SkillService not using updated proxy after hot-reload - Move global proxy settings to advanced tab - Add error handling for scan failures
This commit is contained in:
@@ -2,18 +2,18 @@
|
||||
//!
|
||||
//! 提供获取、设置和测试全局代理的 Tauri 命令。
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::proxy::http_client;
|
||||
use crate::store::AppState;
|
||||
use serde::Serialize;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4, TcpStream};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// 获取全局代理 URL
|
||||
///
|
||||
/// 返回当前配置的代理 URL,null 表示直连。
|
||||
#[tauri::command]
|
||||
pub fn get_global_proxy_url(db: tauri::State<'_, Arc<Database>>) -> Result<Option<String>, String> {
|
||||
db.get_global_proxy_url().map_err(|e| e.to_string())
|
||||
pub fn get_global_proxy_url(state: tauri::State<'_, AppState>) -> Result<Option<String>, String> {
|
||||
state.db.get_global_proxy_url().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// 设置全局代理 URL
|
||||
@@ -21,10 +21,7 @@ pub fn get_global_proxy_url(db: tauri::State<'_, Arc<Database>>) -> Result<Optio
|
||||
/// - 传入非空字符串:启用代理
|
||||
/// - 传入空字符串:清除代理(直连)
|
||||
#[tauri::command]
|
||||
pub fn set_global_proxy_url(
|
||||
db: tauri::State<'_, Arc<Database>>,
|
||||
url: String,
|
||||
) -> Result<(), String> {
|
||||
pub fn set_global_proxy_url(state: tauri::State<'_, AppState>, url: String) -> Result<(), String> {
|
||||
let url_opt = if url.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -32,7 +29,10 @@ pub fn set_global_proxy_url(
|
||||
};
|
||||
|
||||
// 1. 保存到数据库
|
||||
db.set_global_proxy_url(url_opt).map_err(|e| e.to_string())?;
|
||||
state
|
||||
.db
|
||||
.set_global_proxy_url(url_opt)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// 2. 热更新全局 HTTP 客户端
|
||||
http_client::update_proxy(url_opt)?;
|
||||
@@ -69,14 +69,14 @@ pub async fn test_proxy_url(url: String) -> Result<ProxyTestResult, String> {
|
||||
let start = Instant::now();
|
||||
|
||||
// 构建带代理的临时客户端
|
||||
let proxy = reqwest::Proxy::all(&url).map_err(|e| format!("Invalid proxy URL: {}", e))?;
|
||||
let proxy = reqwest::Proxy::all(&url).map_err(|e| format!("Invalid proxy URL: {e}"))?;
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.proxy(proxy)
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.connect_timeout(std::time::Duration::from_secs(10))
|
||||
.build()
|
||||
.map_err(|e| format!("Failed to build client: {}", e))?;
|
||||
.map_err(|e| format!("Failed to build client: {e}"))?;
|
||||
|
||||
// 测试连接到 api.anthropic.com
|
||||
// 使用 HEAD 请求,即使返回 401 也说明网络通了
|
||||
@@ -99,7 +99,7 @@ pub async fn test_proxy_url(url: String) -> Result<ProxyTestResult, String> {
|
||||
}
|
||||
Err(e) => {
|
||||
let latency = start.elapsed().as_millis() as u64;
|
||||
log::debug!("[GlobalProxy] Test failed: {} -> {} ({}ms)", url, e, latency);
|
||||
log::debug!("[GlobalProxy] Test failed: {url} -> {e} ({latency}ms)");
|
||||
Ok(ProxyTestResult {
|
||||
success: false,
|
||||
latency_ms: latency,
|
||||
@@ -130,3 +130,48 @@ pub struct UpstreamProxyStatus {
|
||||
/// 代理 URL
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
/// 检测到的代理信息
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DetectedProxy {
|
||||
/// 代理 URL
|
||||
pub url: String,
|
||||
/// 代理类型 (http/socks5)
|
||||
pub proxy_type: String,
|
||||
/// 端口
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
/// 常见代理端口配置
|
||||
const PROXY_PORTS: &[(u16, &str)] = &[
|
||||
(7890, "http"), // Clash
|
||||
(7891, "socks5"), // Clash SOCKS
|
||||
(1080, "socks5"), // 通用 SOCKS5
|
||||
(8080, "http"), // 通用 HTTP
|
||||
(8888, "http"), // Charles/Fiddler
|
||||
(3128, "http"), // Squid
|
||||
(10808, "socks5"), // V2Ray
|
||||
(10809, "http"), // V2Ray HTTP
|
||||
];
|
||||
|
||||
/// 扫描本地代理
|
||||
///
|
||||
/// 检测常见端口是否有代理服务在运行。
|
||||
#[tauri::command]
|
||||
pub fn scan_local_proxies() -> Vec<DetectedProxy> {
|
||||
let mut found = Vec::new();
|
||||
|
||||
for &(port, proxy_type) in PROXY_PORTS {
|
||||
let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, port);
|
||||
if TcpStream::connect_timeout(&addr.into(), Duration::from_millis(100)).is_ok() {
|
||||
found.push(DetectedProxy {
|
||||
url: format!("{proxy_type}://127.0.0.1:{port}"),
|
||||
proxy_type: proxy_type.to_string(),
|
||||
port,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
found
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user