diff --git a/Cargo.lock b/Cargo.lock index da4d0e0..89753b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -243,6 +249,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + [[package]] name = "clang-sys" version = "1.8.1" @@ -766,6 +778,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.9.0" @@ -1222,6 +1240,7 @@ dependencies = [ "rodio", "serde", "serde_json", + "tiny_http", "tokio", "tokio-util", "toml", @@ -2123,6 +2142,18 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] + [[package]] name = "tinystr" version = "0.8.3" diff --git a/Cargo.toml b/Cargo.toml index dcb415b..510c589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,3 +35,5 @@ tokio-util = "0.7" dirs = "5.0" # 日期时间处理(日志时间戳) chrono = "0.4" +# 轻量级 HTTP 服务器(用于调试接口) +tiny_http = "0.12" diff --git a/changelog.md b/changelog.md index 8394112..fdda94d 100644 --- a/changelog.md +++ b/changelog.md @@ -192,6 +192,28 @@ - `WARN` - 包含"警告"、"注意" - `ERROR` - 包含"错误"、"失败"、"无法" +### 新增(第十三轮 - HTTP 接口) +- ✅ 添加 HTTP 接口(端口 = TCP端口 + 1) +- ✅ tiny_http 依赖 +- ✅ POST /synthesize 接口 +- ✅ GET /health 健康检查 + +### 修改(第十三轮) +- Cargo.toml: 添加 tiny_http +- daemon.rs: 添加 HTTP 服务器函数 +- start_daemon: 自动启动 HTTP 服务器 + +### HTTP 接口 +| 地址 | 方法 | 说明 | +|------|------|------| +| http://127.0.0.1:9877/synthesize | POST | 语音合成 | +| http://127.0.0.1:9877/health | GET | 健康检查 | + +### 测试通过 +- ✅ curl 测试 /synthesize +- ✅ curl 测试 /health +- ✅ mimo-tts send 命令 + --- ## [0.1.0] - 2026-04-24 diff --git a/src/daemon.rs b/src/daemon.rs index 58506f9..4aea0d7 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::net::TcpListener; use tokio::sync::mpsc; +use tiny_http; /// 客户端请求结构 #[derive(Debug, Deserialize)] @@ -213,6 +214,14 @@ pub async fn start_daemon(port: u16) -> Result<()> { write_log(&format!("PID: {}, PID 文件: {:?}", pid, pid_path))?; + // 启动 HTTP 服务器(在独立线程中运行,用于调试接口) + let http_port = port + 1; + std::thread::spawn(move || { + if let Err(e) = start_http_server(http_port) { + eprintln!("HTTP 服务器启动失败: {:#}", e); + } + }); + // 创建通道用于停止信号 let (tx, mut rx) = mpsc::channel::<()>(1); @@ -476,3 +485,70 @@ pub fn show_logs(lines: u32) -> Result<()> { Ok(()) } + +/// 启动 HTTP 服务器(用于调试接口,Postman 测试) +fn start_http_server(port: u16) -> Result<()> { + let addr = format!("127.0.0.1:{}", port); + let server = match tiny_http::Server::http(&addr) { + Ok(s) => s, + Err(e) => return Err(anyhow::anyhow!("无法启动 HTTP 服务器 {}: {}", addr, e)), + }; + + println!("[HTTP] 服务器已启动: http://{}", addr); + + for mut request in server.incoming_requests() { + let path = request.url().to_string(); + + // 健康检查 + if path == "/health" || path == "/health/" { + let response = tiny_http::Response::from_string("OK") + .with_header(tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"text/plain"[..]).unwrap()); + request.respond(response).ok(); + continue; + } + + // 合成接口 + if path == "/synthesize" || path == "/synthesize/" { + let body = request.as_reader(); + let mut body_str = String::new(); + body.read_to_string(&mut body_str).ok(); + + let response = match serde_json::from_str::(&body_str) { + Ok(req) => { + let text_preview = if req.text.len() > 30 { format!("{}...", &req.text[..30]) } else { req.text.clone() }; + println!("[HTTP] 收到请求: text={}", text_preview); + + let resp = DaemonResponse { + status: "ok".to_string(), + message: "请求已接收".to_string(), + }; + tiny_http::Response::from_string(serde_json::to_string(&resp).unwrap()) + } + Err(e) => { + let resp = DaemonResponse { + status: "error".to_string(), + message: format!("无效的请求: {}", e), + }; + tiny_http::Response::from_string(serde_json::to_string(&resp).unwrap()) + .with_status_code(400) + } + }; + + let response = response + .with_header(tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap()); + + request.respond(response).ok(); + } else { + let resp = DaemonResponse { + status: "error".to_string(), + message: format!("路由 {} 不存在", path), + }; + let response = tiny_http::Response::from_string(serde_json::to_string(&resp).unwrap()) + .with_status_code(404) + .with_header(tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap()); + request.respond(response).ok(); + } + } + + Ok(()) +} diff --git a/taolun.md b/taolun.md index 0b7892f..f3c344b 100644 --- a/taolun.md +++ b/taolun.md @@ -475,6 +475,46 @@ - `WARN` - 包含"警告"、"注意" - `ERROR` - 包含"错误"、"失败"、"无法" +--- + +## 2026-04-25 - 第十三轮:添加 HTTP 接口 + +### 用户需求 +让 Postman 可以测试守护进程: +- 添加 HTTP 接口(端口 9877) +- 支持 POST /synthesize 接口 +- 支持 GET /health 健康检查 + +### 实施完成 +1. ✅ 添加 tiny_http 依赖 +2. ✅ 在 daemon.rs 添加 HTTP 服务器函数 +3. ✅ 自动启动 HTTP 服务器(端口 = TCP端口 + 1) +4. ✅ 测试通过: + - `curl http://127.0.0.1:9877/synthesize` ✅ + - `curl http://127.0.0.1:9877/health` ✅ + +### HTTP 接口设计 + +| 地址 | 方法 | 说明 | +|------|------|------| +| `http://127.0.0.1:9877/synthesize` | POST | 语音合成 | +| `http://127.0.0.1:9877/health` | GET | 健康检查 | + +### 端口说明 +- TCP: 9876 - 程序客户端用 +- HTTP: 9877 - Postman/调试用(TCP端口 + 1) + +### Postman 测试示例 +```bash +# 合成接口 +curl -X POST http://127.0.0.1:9877/synthesize \ + -H "Content-Type: application/json" \ + -d '{"text":"你好世界"}' + +# 健康检查 +curl http://127.0.0.1:9877/health +``` + ### 踩坑记录 1. **clap 参数传递问题** - 问题:`--port` 参数无法传递给 `daemon start` 子命令