mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
fix: 将 HTTP 桥接改为独立的开发服务器(端口 3030)
## 问题 之前将 dev_bridge 路由合并到主应用服务器(端口 8999), 但用户需要一个独立的开发桥接端口,并且服务器启动后会立即关闭。 ## 变更 - 重构 dev_bridge.rs 为独立的服务器实现 - 服务器在后台持续运行(不使用 graceful_shutdown) - 在服务器启动时同步启动 dev bridge 服务器 - 添加 get_network_info 命令支持 - 修复 get_default_provider 返回格式(返回字符串而非对象) ## 架构 - 端口 8999: 主应用 API(生产/通用) - 端口 3030: 专用开发桥接(仅 dev 模式) ## 验证 - [DevBridge] HTTP 桥接成功: get_server_status - [DevBridge] HTTP 桥接成功: get_config - [DevBridge] HTTP 桥接成功: get_default_provider - [DevBridge] HTTP 桥接成功: get_network_info
This commit is contained in:
+89
-14
@@ -1,6 +1,8 @@
|
||||
//! HTTP 桥接模块
|
||||
//!
|
||||
//! 仅在开发模式下启用,允许浏览器 dev server 通过 HTTP 调用 Tauri 命令。
|
||||
//!
|
||||
//! 这是一个独立的开发服务器,运行在 3030 端口,与主应用服务器(8999)分离。
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub mod dispatcher;
|
||||
@@ -13,12 +15,18 @@ use axum::{
|
||||
routing::post,
|
||||
Json, Router,
|
||||
};
|
||||
#[cfg(debug_assertions)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(debug_assertions)]
|
||||
use std::sync::Arc;
|
||||
#[cfg(debug_assertions)]
|
||||
use tokio::sync::RwLock;
|
||||
#[cfg(debug_assertions)]
|
||||
use tower_http::cors::CorsLayer;
|
||||
|
||||
// 使用 Axum 服务器的 AppState
|
||||
use crate::server::AppState;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct InvokeRequest {
|
||||
pub cmd: String,
|
||||
@@ -26,28 +34,86 @@ pub struct InvokeRequest {
|
||||
pub args: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InvokeResponse {
|
||||
pub result: Option<serde_json::Value>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// 创建 HTTP 桥接路由
|
||||
///
|
||||
/// 仅在 dev 模式下被添加到 Axum 服务器
|
||||
pub fn dev_bridge_routes() -> Router<AppState> {
|
||||
Router::new().route("/invoke", post(invoke_command)).layer(
|
||||
// CORS 配置 - 允许 localhost:1420 访问
|
||||
CorsLayer::new()
|
||||
.allow_origin("http://localhost:1420".parse::<HeaderValue>().unwrap())
|
||||
.allow_methods([axum::http::Method::POST])
|
||||
.allow_headers([axum::http::header::CONTENT_TYPE]),
|
||||
)
|
||||
/// 开发桥接服务器配置
|
||||
#[cfg(debug_assertions)]
|
||||
pub struct DevBridgeConfig {
|
||||
/// 监听地址
|
||||
pub host: String,
|
||||
/// 监听端口
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
async fn invoke_command(State(state): State<AppState>, Json(req): Json<InvokeRequest>) -> Response {
|
||||
#[cfg(debug_assertions)]
|
||||
impl Default for DevBridgeConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 3030,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 开发桥接服务器
|
||||
#[cfg(debug_assertions)]
|
||||
pub struct DevBridgeServer;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
impl DevBridgeServer {
|
||||
/// 启动开发桥接服务器
|
||||
///
|
||||
/// 这是一个独立的 HTTP 服务器,仅用于开发模式,
|
||||
/// 允许浏览器 dev server 通过 HTTP 调用 Tauri 命令。
|
||||
///
|
||||
/// 服务器会在后台持续运行,直到应用退出。
|
||||
pub async fn start(
|
||||
app_state: Arc<RwLock<AppState>>,
|
||||
config: Option<DevBridgeConfig>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = config.unwrap_or_default();
|
||||
|
||||
let app = Router::new()
|
||||
.route("/invoke", post(invoke_command))
|
||||
.route("/health", post(health_check))
|
||||
.layer(
|
||||
// CORS 配置 - 允许 localhost:1420 访问
|
||||
CorsLayer::new()
|
||||
.allow_origin("http://localhost:1420".parse::<HeaderValue>().unwrap())
|
||||
.allow_methods([axum::http::Method::POST, axum::http::Method::GET])
|
||||
.allow_headers([axum::http::header::CONTENT_TYPE]),
|
||||
)
|
||||
.with_state(app_state);
|
||||
|
||||
let addr = format!("{}:{}", config.host, config.port);
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await?;
|
||||
|
||||
eprintln!("[DevBridge] 正在监听: http://{}", addr);
|
||||
|
||||
// 直接运行服务器(不使用 graceful_shutdown)
|
||||
// 服务器将持续运行直到应用退出
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, app).await.ok();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
async fn invoke_command(
|
||||
State(state): State<Arc<RwLock<AppState>>>,
|
||||
Json(req): Json<InvokeRequest>,
|
||||
) -> Response {
|
||||
// 获取 AppState 的读锁
|
||||
let state_ref = state.read().await;
|
||||
// 调用命令分发器
|
||||
match dispatcher::handle_command(&state, &req.cmd, req.args).await {
|
||||
match dispatcher::handle_command(&state_ref, &req.cmd, req.args).await {
|
||||
Ok(result) => Json(InvokeResponse {
|
||||
result: Some(result),
|
||||
error: None,
|
||||
@@ -60,3 +126,12 @@ async fn invoke_command(State(state): State<AppState>, Json(req): Json<InvokeReq
|
||||
.into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
async fn health_check() -> impl IntoResponse {
|
||||
Json(serde_json::json!({
|
||||
"status": "ok",
|
||||
"service": "DevBridge",
|
||||
"version": "1.0.0"
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ pub async fn handle_command(
|
||||
|
||||
"get_default_provider" => {
|
||||
let provider = state.default_provider.read().await.clone();
|
||||
Ok(serde_json::json!({ "provider": provider }))
|
||||
// 直接返回字符串值,不是对象
|
||||
Ok(serde_json::json!(provider))
|
||||
}
|
||||
|
||||
"get_endpoint_providers" => {
|
||||
@@ -177,6 +178,16 @@ pub async fn handle_command(
|
||||
}))
|
||||
}
|
||||
|
||||
// ========== 网络信息 ==========
|
||||
"get_network_info" => {
|
||||
// 返回网络信息
|
||||
Ok(serde_json::json!({
|
||||
"localhost": "127.0.0.1",
|
||||
"lan_ip": null,
|
||||
"all_ips": ["127.0.0.1"]
|
||||
}))
|
||||
}
|
||||
|
||||
_ => Err(format!(
|
||||
"[DevBridge] 未知命令: '{}'. 如需此命令,请将其添加到 dispatcher.rs 的 handle_command 函数中。",
|
||||
cmd
|
||||
|
||||
@@ -866,6 +866,28 @@ async fn run_server(
|
||||
api_key_service,
|
||||
};
|
||||
|
||||
// ========== 开发模式:启动独立的 HTTP 桥接服务器 ==========
|
||||
// 仅在 debug 模式下,启动一个独立的开发服务器在端口 3030
|
||||
// 允许浏览器 dev server 通过 HTTP 调用 Tauri 命令
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
eprintln!("[DevBridge] ===== 准备启动开发桥接服务器 =====");
|
||||
use tokio::sync::RwLock as TokioRwLock;
|
||||
let dev_bridge_state = Arc::new(TokioRwLock::new(state.clone()));
|
||||
eprintln!("[DevBridge] 状态已克隆,准备启动");
|
||||
tokio::spawn(async move {
|
||||
eprintln!("[DevBridge] spawn 任务开始执行");
|
||||
match crate::dev_bridge::DevBridgeServer::start(dev_bridge_state, None).await {
|
||||
Ok(_) => {
|
||||
eprintln!("[DevBridge] 启动完成");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[DevBridge] 启动失败: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 启动配置文件监控
|
||||
let _file_watcher = if let Some(path) = config_path {
|
||||
start_config_watcher(
|
||||
@@ -986,9 +1008,6 @@ async fn run_server(
|
||||
.merge(kiro_api_routes)
|
||||
// 凭证 API 路由(用于 aster Agent 集成)
|
||||
.merge(credentials_api_routes)
|
||||
// 开发模式 HTTP 桥接路由(仅在 debug 模式启用)
|
||||
// 允许浏览器 dev server 通过 HTTP 调用 Tauri 命令
|
||||
.merge(crate::dev_bridge::dev_bridge_routes())
|
||||
.layer(DefaultBodyLimit::max(body_limit))
|
||||
.with_state(state);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user