diff --git a/src/api/upload.rs b/src/api/upload.rs index 276fc6e..768060b 100644 --- a/src/api/upload.rs +++ b/src/api/upload.rs @@ -7,7 +7,7 @@ #[cfg(feature = "server")] use axum::{ - extract::{ConnectInfo, Multipart}, + extract::{ConnectInfo, Extension, Multipart}, http::{HeaderMap, StatusCode}, response::Json, }; @@ -70,13 +70,19 @@ fn validate_raw_image(data: &[u8], mime_type: &str) -> bool { /// /// 流程:限流 → 解析 session → 校验 admin → 读取 multipart → 校验类型/大小 → /// 转码(如适用)→ 按日期落盘 → 返回相对 URL。 +/// +/// `ConnectInfo` 以可选扩展注入:`dioxus::server::serve()` 接管了 listener, +/// 无法调用 `into_make_service_with_connect_info::()`,所以这里 +/// 与 `serve_image` 保持一致的优雅降级——扩展缺失时退回 `"unknown"` 限流桶。 +/// 生产环境应在反向代理后部署并配置 `TRUSTED_PROXY_COUNT`,让限流拿到真实 IP。 pub async fn upload_image( - ConnectInfo(addr): ConnectInfo, + connect_info: Option>>, headers: HeaderMap, mut multipart: Multipart, ) -> Result, (StatusCode, Json)> { // 0. Rate limit check - let ip = crate::api::rate_limit::get_client_ip_with_peer(&headers, Some(addr)); + let peer = connect_info.map(|Extension(ConnectInfo(addr))| addr); + let ip = crate::api::rate_limit::get_client_ip_with_peer(&headers, peer); if let Err(msg) = crate::api::rate_limit::check_upload_limit(&ip) { return Err(( StatusCode::TOO_MANY_REQUESTS, diff --git a/src/main.rs b/src/main.rs index 1b9558f..35a384e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -326,10 +326,12 @@ fn main() { )); // 静态资源路由:图片文件服务。 - // 注意:axum 0.8 没有 ConnectInfoLayer,且 dioxus::server::serve 不会把 - // ConnectInfo 扩展传播到手动 merge 的路由,所以 serve_image 使用 - // Option>> 优雅降级。生产环境应在反向代理后 - // 部署并配置 TRUSTED_PROXY_COUNT,使限流能拿到真实客户端 IP。 + // 注意:`dioxus::server::serve()` 接管了 listener 与 `into_make_service` + // 调用,没有机会换成 `into_make_service_with_connect_info::()`, + // 所以手动 merge 进来的路由(含 static_routes)拿不到 `ConnectInfo` 扩展。 + // serve_image / upload_image 因此都用 `Option>>` + // 优雅降级。生产环境应在反向代理后部署并配置 TRUSTED_PROXY_COUNT, + // 使限流能拿到真实客户端 IP。 let static_routes = axum::Router::new() .route( "/uploads/{*path}",