feat(server): add Redis-based rate limiting middleware

Store Redis client in AppState instead of discarding it. Create
rate_limit middleware using Redis INCR + EXPIRE for fixed-window
counting. Apply user-based rate limiting (100 req/min) to all
protected routes. Graceful degradation when Redis is unavailable.
This commit is contained in:
iven
2026-04-11 23:58:54 +08:00
parent db2cd24259
commit 529d90ff46
4 changed files with 128 additions and 3 deletions

View File

@@ -1,6 +1,7 @@
mod config;
mod db;
mod handlers;
mod middleware;
mod state;
/// OpenAPI 规范定义(预留,未来可通过 utoipa derive 合并各模块 schema
@@ -16,7 +17,7 @@ mod state;
struct ApiDoc;
use axum::Router;
use axum::middleware;
use axum::middleware as axum_middleware;
use config::AppConfig;
use erp_auth::middleware::jwt_auth_middleware_fn;
use state::AppState;
@@ -104,7 +105,7 @@ async fn main() -> anyhow::Result<()> {
}
// Connect to Redis
let _redis_client = redis::Client::open(&config.redis.url[..])?;
let redis_client = redis::Client::open(&config.redis.url[..])?;
tracing::info!("Redis client created");
// Initialize event bus (capacity 1024 events)
@@ -153,6 +154,7 @@ async fn main() -> anyhow::Result<()> {
config,
event_bus,
module_registry: registry,
redis: redis_client.clone(),
};
// --- Build the router ---
@@ -172,11 +174,16 @@ async fn main() -> anyhow::Result<()> {
.with_state(state.clone());
// Protected routes (JWT authentication required)
// User-based rate limiting (100 req/min) applied after JWT auth
let protected_routes = erp_auth::AuthModule::protected_routes()
.merge(erp_config::ConfigModule::protected_routes())
.merge(erp_workflow::WorkflowModule::protected_routes())
.merge(erp_message::MessageModule::protected_routes())
.layer(middleware::from_fn(move |req, next| {
.layer(axum::middleware::from_fn_with_state(
state.clone(),
middleware::rate_limit::rate_limit_by_user,
))
.layer(axum_middleware::from_fn(move |req, next| {
let secret = jwt_secret.clone();
async move { jwt_auth_middleware_fn(secret, req, next).await }
}))