From 533a2b6a8e9912dc5e266fb8619a5d8d228b231a Mon Sep 17 00:00:00 2001 From: iven Date: Mon, 11 May 2026 10:24:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(server):=20BLE=20=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E9=99=90=E6=B5=81=20=E2=80=94=20=E6=AF=8F?= =?UTF-8?q?=E7=BD=91=E5=85=B3=2060=20req/60s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 /health/gateway 路由添加 gateway_id 级别的速率限制, 网关认证(API Key)→ 限流检查 → handler 三层中间件。 Redis 不可达时同样遵循 fail_close 策略。 --- crates/erp-server/src/main.rs | 4 +++ .../erp-server/src/middleware/rate_limit.rs | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/crates/erp-server/src/main.rs b/crates/erp-server/src/main.rs index b751427..9fd6380 100644 --- a/crates/erp-server/src/main.rs +++ b/crates/erp-server/src/main.rs @@ -750,6 +750,10 @@ async fn main() -> anyhow::Result<()> { .nest( "/health/gateway", erp_health::HealthModule::gateway_routes() + .layer(axum::middleware::from_fn_with_state( + state.clone(), + middleware::rate_limit::rate_limit_by_gateway, + )) .layer(axum::middleware::from_fn_with_state( state.clone(), erp_health::gateway_auth::gateway_auth_middleware, diff --git a/crates/erp-server/src/middleware/rate_limit.rs b/crates/erp-server/src/middleware/rate_limit.rs index 1acb2b4..e9ed74f 100644 --- a/crates/erp-server/src/middleware/rate_limit.rs +++ b/crates/erp-server/src/middleware/rate_limit.rs @@ -268,3 +268,34 @@ fn extract_client_ip(headers: &axum::http::HeaderMap) -> String { }) .unwrap_or_else(|| "unknown".to_string()) } + +/// BLE 网关级别的限流中间件。 +/// +/// 从 GatewayAuthContext 中读取 gateway_id 作为标识符。 +/// 限制每个网关设备 60 req/60s。 +/// 必须在 gateway_auth_middleware 之后挂载(需要认证上下文)。 +pub async fn rate_limit_by_gateway( + State(state): State, + req: Request, + next: Next, +) -> Response { + let identifier = req + .extensions() + .get::() + .map(|ctx| ctx.gateway_id.clone()) + .unwrap_or_else(|| "unknown_gateway".to_string()); + let fail_close = state.config.rate_limit.fail_close; + apply_rate_limit( + RateLimitParams { + redis_client: &state.redis, + fail_close, + max_requests: 60, + window_secs: 60, + prefix: "gateway", + }, + &identifier, + req, + next, + ) + .await +}