mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 00:35:32 +08:00
4c94e70f97
Add a complete HTTP proxy server implementation built on Axum framework, enabling local API request forwarding with automatic provider failover and load balancing capabilities. Backend Implementation (Rust): - Add proxy server module with 7 core components: * server.rs: Axum HTTP server lifecycle management (start/stop/status) * router.rs: API routing configuration for Claude/OpenAI/Gemini endpoints * handlers.rs: Request/response handling and transformation * forwarder.rs: Upstream forwarding logic with retry mechanism (652 lines) * error.rs: Comprehensive error handling and HTTP status mapping * types.rs: Shared types (ProxyConfig, ProxyStatus, ProxyServerInfo) * health.rs: Provider health check infrastructure Service Layer: - Add ProxyService (services/proxy.rs, 157 lines): * Manage proxy server lifecycle * Handle configuration updates * Track runtime status and metrics Database Layer: - Add proxy configuration DAO (dao/proxy.rs, 242 lines): * Persist proxy settings (listen address, port, timeout) * Store provider priority and availability flags - Update schema with proxy_config table (schema.rs): * Support runtime configuration persistence Tauri Commands: - Add 6 command endpoints (commands/proxy.rs): * start_proxy_server: Launch proxy server * stop_proxy_server: Gracefully shutdown server * get_proxy_status: Query runtime status * get_proxy_config: Retrieve current configuration * update_proxy_config: Modify settings without restart * is_proxy_running: Check server state Frontend Implementation (React + TypeScript): - Add ProxyPanel component (222 lines): * Real-time server status display * Start/stop controls * Provider availability monitoring - Add ProxySettingsDialog component (420 lines): * Configuration editor (address, port, timeout) * Provider priority management * Settings validation - Add React hooks: * useProxyConfig: Manage proxy configuration state * useProxyStatus: Poll and display server status - Add TypeScript types (types/proxy.ts): * Define ProxyConfig, ProxyStatus interfaces Provider Integration: - Extend Provider model with availability field (providers.rs): * Track provider health for failover logic - Update ProviderCard UI to display proxy status - Integrate proxy controls in Settings page Dependencies: - Add Axum 0.7 (async web framework) - Add Tower 0.4 (middleware and service abstractions) - Add Tower-HTTP (CORS layer) - Add Tokio sync primitives (oneshot, RwLock) Technical Details: - Graceful shutdown via oneshot channel - Shared state with Arc<RwLock<T>> for thread-safe config updates - CORS enabled for cross-origin frontend access - Request/response streaming support - Automatic retry with exponential backoff (forwarder) - API key extraction from multiple config formats (Claude/Codex/Gemini) File Statistics: - 41 files changed - 3491 insertions(+), 41 deletions(-) - Core modules: 1393 lines (server + forwarder + handlers) - Frontend UI: 642 lines (ProxyPanel + ProxySettingsDialog) - Database/DAO: 326 lines This implementation provides the foundation for advanced features like: - Multi-provider load balancing - Automatic failover on provider errors - Request logging and analytics - Usage tracking and cost monitoring
158 lines
4.8 KiB
Rust
158 lines
4.8 KiB
Rust
//! 代理服务业务逻辑层
|
|
//!
|
|
//! 提供代理服务器的启动、停止和配置管理
|
|
|
|
use crate::database::Database;
|
|
use crate::proxy::server::ProxyServer;
|
|
use crate::proxy::types::*;
|
|
use std::sync::Arc;
|
|
use tokio::sync::RwLock;
|
|
|
|
#[derive(Clone)]
|
|
pub struct ProxyService {
|
|
db: Arc<Database>,
|
|
server: Arc<RwLock<Option<ProxyServer>>>,
|
|
}
|
|
|
|
impl ProxyService {
|
|
pub fn new(db: Arc<Database>) -> Self {
|
|
Self {
|
|
db,
|
|
server: Arc::new(RwLock::new(None)),
|
|
}
|
|
}
|
|
|
|
/// 启动代理服务器
|
|
pub async fn start(&self) -> Result<ProxyServerInfo, String> {
|
|
// 1. 获取配置
|
|
let config = self
|
|
.db
|
|
.get_proxy_config()
|
|
.await
|
|
.map_err(|e| format!("获取代理配置失败: {e}"))?;
|
|
|
|
// 2. 检查是否启用
|
|
if !config.enabled {
|
|
return Err("代理服务未启用,请先在设置中启用".to_string());
|
|
}
|
|
|
|
// 3. 检查是否已在运行
|
|
if self.server.read().await.is_some() {
|
|
return Err("代理服务已在运行中".to_string());
|
|
}
|
|
|
|
// 4. 创建并启动服务器
|
|
let server = ProxyServer::new(config, self.db.clone());
|
|
let info = server
|
|
.start()
|
|
.await
|
|
.map_err(|e| format!("启动代理服务器失败: {e}"))?;
|
|
|
|
// 5. 保存服务器实例
|
|
*self.server.write().await = Some(server);
|
|
|
|
log::info!("代理服务器已启动: {}:{}", info.address, info.port);
|
|
Ok(info)
|
|
}
|
|
|
|
/// 停止代理服务器
|
|
pub async fn stop(&self) -> Result<(), String> {
|
|
if let Some(server) = self.server.write().await.take() {
|
|
server
|
|
.stop()
|
|
.await
|
|
.map_err(|e| format!("停止代理服务器失败: {e}"))?;
|
|
log::info!("代理服务器已停止");
|
|
Ok(())
|
|
} else {
|
|
Err("代理服务器未运行".to_string())
|
|
}
|
|
}
|
|
|
|
/// 获取服务器状态
|
|
pub async fn get_status(&self) -> Result<ProxyStatus, String> {
|
|
if let Some(server) = self.server.read().await.as_ref() {
|
|
Ok(server.get_status().await)
|
|
} else {
|
|
// 服务器未运行时返回默认状态
|
|
Ok(ProxyStatus {
|
|
running: false,
|
|
..Default::default()
|
|
})
|
|
}
|
|
}
|
|
|
|
/// 获取代理配置
|
|
pub async fn get_config(&self) -> Result<ProxyConfig, String> {
|
|
self.db
|
|
.get_proxy_config()
|
|
.await
|
|
.map_err(|e| format!("获取代理配置失败: {e}"))
|
|
}
|
|
|
|
/// 更新代理配置
|
|
pub async fn update_config(&self, config: &ProxyConfig) -> Result<(), String> {
|
|
// 记录旧配置用于判定是否需要重启
|
|
let previous = self
|
|
.db
|
|
.get_proxy_config()
|
|
.await
|
|
.map_err(|e| format!("获取代理配置失败: {e}"))?;
|
|
|
|
// 保存到数据库
|
|
self.db
|
|
.update_proxy_config(config.clone())
|
|
.await
|
|
.map_err(|e| format!("保存代理配置失败: {e}"))?;
|
|
|
|
// 检查服务器当前状态
|
|
let mut server_guard = self.server.write().await;
|
|
if server_guard.is_none() {
|
|
return Ok(());
|
|
}
|
|
|
|
// 如果关闭代理,直接停止
|
|
if !config.enabled {
|
|
if let Some(server) = server_guard.take() {
|
|
server
|
|
.stop()
|
|
.await
|
|
.map_err(|e| format!("停止代理服务器失败: {e}"))?;
|
|
log::info!("代理配置禁用了服务,已自动停止代理服务器");
|
|
}
|
|
return Ok(());
|
|
}
|
|
|
|
let require_restart = config.listen_address != previous.listen_address
|
|
|| config.listen_port != previous.listen_port;
|
|
|
|
if require_restart {
|
|
if let Some(server) = server_guard.take() {
|
|
server
|
|
.stop()
|
|
.await
|
|
.map_err(|e| format!("重启前停止代理服务器失败: {e}"))?;
|
|
}
|
|
|
|
let new_server = ProxyServer::new(config.clone(), self.db.clone());
|
|
new_server
|
|
.start()
|
|
.await
|
|
.map_err(|e| format!("重启代理服务器失败: {e}"))?;
|
|
|
|
*server_guard = Some(new_server);
|
|
log::info!("代理配置已更新,服务器已自动重启应用最新配置");
|
|
} else if let Some(server) = server_guard.as_ref() {
|
|
server.apply_runtime_config(config).await;
|
|
log::info!("代理配置已实时应用,无需重启代理服务器");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 检查服务器是否正在运行
|
|
pub async fn is_running(&self) -> bool {
|
|
self.server.read().await.is_some()
|
|
}
|
|
}
|