优化: Keep-Alive/rate-limiter/dead-code清理 + deploy.sh蓝绿自动检测 + 压测文档
This commit is contained in:
@@ -149,7 +149,31 @@ pub async fn refresh_token(
|
||||
pool: web::Data<PgPool>,
|
||||
req: web::Json<RefreshTokenRequest>,
|
||||
app_state: web::Data<AppState>,
|
||||
http_req: HttpRequest,
|
||||
) -> impl Responder {
|
||||
let client_ip = http_req
|
||||
.headers()
|
||||
.get("X-Forwarded-For")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.split(',').next().unwrap_or(s).trim().to_string())
|
||||
.or_else(|| {
|
||||
http_req
|
||||
.headers()
|
||||
.get("X-Real-IP")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
http_req
|
||||
.peer_addr()
|
||||
.map(|a| a.ip().to_string())
|
||||
.unwrap_or_else(|| "unknown".to_string())
|
||||
});
|
||||
if let Err(e) = LOGIN_RATE_LIMITER.check_rate_limit(&client_ip).await {
|
||||
warn!("refresh_token 请求被限流: client_ip={}", client_ip);
|
||||
return HttpResponse::TooManyRequests().json(ErrorResponse::<()>::error(e.to_string()));
|
||||
}
|
||||
|
||||
let refresh_token = &req.refresh_token;
|
||||
|
||||
let refresh_token_record = match db::verify_refresh_token(pool.get_ref(), refresh_token).await {
|
||||
@@ -247,15 +271,13 @@ pub async fn mock_login(
|
||||
}
|
||||
|
||||
// 如果设置了 MOCK_LOGIN_KEY,验证请求头
|
||||
if let Ok(key) = std::env::var("MOCK_LOGIN_KEY") {
|
||||
if !key.is_empty() {
|
||||
let header_key = req.headers()
|
||||
.get("X-Mock-Key")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("");
|
||||
if header_key != key {
|
||||
return HttpResponse::Forbidden().json(ErrorResponse::<()>::error("模拟登录密钥错误"));
|
||||
}
|
||||
if let Ok(key) = std::env::var("MOCK_LOGIN_KEY") && !key.is_empty() {
|
||||
let header_key = req.headers()
|
||||
.get("X-Mock-Key")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("");
|
||||
if header_key != key {
|
||||
return HttpResponse::Forbidden().json(ErrorResponse::<()>::error("模拟登录密钥错误"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +635,7 @@ pub async fn web_login_confirm(
|
||||
.await
|
||||
{
|
||||
Ok(Some((is_member, expires))) => {
|
||||
let active = is_member && expires.map_or(true, |e| e > Utc::now());
|
||||
let active = is_member && expires.is_none_or(|e| e > Utc::now());
|
||||
(active, expires.map(|e| e.to_rfc3339()))
|
||||
}
|
||||
_ => (false, None),
|
||||
@@ -745,7 +767,7 @@ pub async fn web_login_auto_confirm(
|
||||
|
||||
let (is_active_member, membership_expires_at): (bool, Option<String>) = match paid_info {
|
||||
Some((is_member, expires)) => {
|
||||
let active = is_member && expires.map_or(true, |e| e > Utc::now());
|
||||
let active = is_member && expires.is_none_or(|e| e > Utc::now());
|
||||
(active, expires.map(|e| e.to_rfc3339()))
|
||||
}
|
||||
None => (false, None),
|
||||
|
||||
@@ -849,8 +849,8 @@ fn urlencoding(s: &str) -> String {
|
||||
while let Some(b) = chars.next() {
|
||||
if b == b'%' {
|
||||
had_escape = true;
|
||||
let hi = chars.next().and_then(|c| hex_val(c));
|
||||
let lo = chars.next().and_then(|c| hex_val(c));
|
||||
let hi = chars.next().and_then(hex_val);
|
||||
let lo = chars.next().and_then(hex_val);
|
||||
if let (Some(h), Some(l)) = (hi, lo) {
|
||||
result.push((h << 4 | l) as char);
|
||||
} else {
|
||||
@@ -1171,16 +1171,14 @@ fn check_mock_payment_allowed(req: &HttpRequest) -> Result<(), AppError> {
|
||||
}
|
||||
|
||||
// 规则 3:如果设了 MOCK_PAY_KEY,验证请求头
|
||||
if let Ok(key) = std::env::var("MOCK_PAY_KEY") {
|
||||
if !key.is_empty() {
|
||||
let header_key = req
|
||||
.headers()
|
||||
.get("X-Mock-Key")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("");
|
||||
if header_key != key {
|
||||
return Err(AppError::Forbidden("Mock 支付密钥错误".to_string()));
|
||||
}
|
||||
if let Ok(key) = std::env::var("MOCK_PAY_KEY") && !key.is_empty() {
|
||||
let header_key = req
|
||||
.headers()
|
||||
.get("X-Mock-Key")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("");
|
||||
if header_key != key {
|
||||
return Err(AppError::Forbidden("Mock 支付密钥错误".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1246,7 +1244,7 @@ pub async fn sync_order(
|
||||
.ok_or_else(|| AppError::NotFound("用户不存在".to_string()))?;
|
||||
|
||||
let (is_member, membership_expires_at) = user;
|
||||
let is_active_member = is_member && membership_expires_at.map_or(true, |expires| expires > Utc::now());
|
||||
let is_active_member = is_member && membership_expires_at.is_none_or(|expires| expires > Utc::now());
|
||||
|
||||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||
"success": true,
|
||||
@@ -1407,6 +1405,7 @@ pub async fn payment_login_status(
|
||||
let code = query.code.trim();
|
||||
|
||||
// 查询登录码记录(包含 token 字段用于判断是否已确认)
|
||||
#[allow(clippy::type_complexity)]
|
||||
let record: Option<(String, chrono::DateTime<chrono::Utc>, Option<String>, Option<i32>)> =
|
||||
sqlx::query_as(
|
||||
"SELECT code, expires_at, token, user_id FROM web_login_codes WHERE code = $1",
|
||||
@@ -1472,7 +1471,7 @@ pub async fn payment_login_status(
|
||||
.await
|
||||
{
|
||||
Ok(Some((is_member, expires))) => {
|
||||
let active = is_member && expires.map_or(true, |e| e > Utc::now());
|
||||
let active = is_member && expires.is_none_or(|e| e > Utc::now());
|
||||
(active, expires.map(|e| e.to_rfc3339()))
|
||||
}
|
||||
_ => (false, None),
|
||||
|
||||
@@ -3,8 +3,10 @@ use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SentryEventRequest {
|
||||
#[allow(dead_code)]
|
||||
pub level: Option<String>,
|
||||
pub message: String,
|
||||
#[allow(dead_code)]
|
||||
pub stack: Option<String>,
|
||||
pub page: Option<String>,
|
||||
pub user_id: Option<i32>,
|
||||
@@ -28,11 +30,11 @@ pub async fn report_frontend_error(body: web::Json<SentryEventRequest>) -> HttpR
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
if let Some(extra) = &body.extra {
|
||||
if let Some(obj) = extra.as_object() {
|
||||
for (k, v) in obj {
|
||||
scope.set_extra(k, sentry::protocol::Value::from(v.clone()));
|
||||
}
|
||||
if let Some(extra) = &body.extra
|
||||
&& let Some(obj) = extra.as_object()
|
||||
{
|
||||
for (k, v) in obj {
|
||||
scope.set_extra(k, v.clone());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ pub async fn get_current_user_profile(
|
||||
true
|
||||
} else {
|
||||
user.is_member &&
|
||||
user.membership_expires_at.map_or(true, |expires| expires > chrono::Utc::now())
|
||||
user.membership_expires_at.is_none_or(|expires| expires > chrono::Utc::now())
|
||||
};
|
||||
|
||||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||
|
||||
Reference in New Issue
Block a user