first commit

This commit is contained in:
2026-02-13 15:57:29 +08:00
commit aacda0b66a
53 changed files with 10029 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
use crate::AppState;
use axum::{
extract::{Request, State},
http::{HeaderMap, StatusCode},
middleware::Next,
response::Response,
};
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Claims {
pub sub: String, // user_id
#[allow(dead_code)]
pub exp: usize,
}
/// JWT 认证中间件
pub async fn auth_middleware(
State(state): State<AppState>,
headers: HeaderMap,
mut req: Request,
next: Next,
) -> Result<Response, StatusCode> {
// 1. 提取 Authorization header
let auth_header = headers
.get("Authorization")
.and_then(|h| h.to_str().ok())
.ok_or(StatusCode::UNAUTHORIZED)?;
if !auth_header.starts_with("Bearer ") {
return Err(StatusCode::UNAUTHORIZED);
}
let token = &auth_header[7..];
// 2. 验证 JWT
let jwt_secret = &state.config.auth.jwt_secret;
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret(jwt_secret.as_ref()),
&Validation::default(),
)
.map_err(|_| StatusCode::UNAUTHORIZED)?;
// 3. 将 user_id 添加到请求扩展
req.extensions_mut().insert(token_data.claims.sub);
Ok(next.run(req).await)
}
+71
View File
@@ -0,0 +1,71 @@
use axum::{extract::Request, response::Response};
use std::time::Instant;
/// Request ID 标记
#[derive(Clone)]
pub struct RequestId(pub String);
/// 请求日志中间件
pub async fn request_logging_middleware(
mut req: Request,
next: axum::middleware::Next,
) -> Response {
let start = Instant::now();
// 提取请求信息
let method = req.method().clone();
let path = req.uri().path().to_string();
let query = req.uri().query().map(|s| s.to_string());
// 生成请求 ID
let request_id = uuid::Uuid::new_v4().to_string();
// 将 request_id 存储到请求扩展中
req.extensions_mut().insert(RequestId(request_id.clone()));
// 第1条日志:请求开始
let separator = "=".repeat(80);
let header = format!("{} {}", method, path);
tracing::info!("{}", separator);
tracing::info!("{}", header);
tracing::info!("{}", separator);
let now_beijing = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
let query_str = query.as_deref().unwrap_or("");
tracing::info!(
"[{}] 📥 查询参数: {} | 时间: {}",
request_id,
query_str,
now_beijing
);
// 调用下一个处理器
let response = next.run(req).await;
// 第3条日志:请求完成
let duration = start.elapsed();
let status = response.status();
tracing::info!(
"[{}] ✅ 状态码: {} | 耗时: {}ms",
request_id,
status.as_u16(),
duration.as_millis()
);
tracing::info!("{}", separator);
response
}
/// 请求日志辅助工具
pub fn log_info<T: std::fmt::Debug>(request_id: &RequestId, label: &str, data: T) {
let data_str = format!("{:?}", data);
let truncated = if data_str.len() > 300 {
format!("{}...", &data_str[..300])
} else {
data_str
};
tracing::info!("[{}] 🔧 {} | {}", request_id.0, label, truncated);
}
+2
View File
@@ -0,0 +1,2 @@
pub mod auth;
pub mod logging;