feat: add runtime policy and observability
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
237899745
2026-07-25 20:00:11 +08:00
parent f3c7a77a37
commit 64b1169e8c
32 changed files with 1236 additions and 120 deletions

View File

@@ -81,11 +81,18 @@ async fn register(
Json(req): Json<RegisterRequest>,
) -> Result<Json<Envelope<RegisterResponse>>, AppError> {
let ip = context::client_ip(&headers, addr.ip());
let policy = settings::runtime_policy(&state).await?;
if !policy.features.registration_enabled {
return Err(AppError::new(
ErrorCode::Forbidden,
"用户注册功能当前已关闭",
));
}
rate_limit::enforce(
&state,
"auth_register_ip",
&ip.to_string(),
10,
policy.rate_limits.register_ip_per_hour,
60 * 60,
"注册请求过于频繁,请稍后再试",
)
@@ -96,7 +103,7 @@ async fn register(
credentials::validate_password(&req.password)?;
let password_hash = credentials::hash_password(&req.password).await?;
let verification_required = settings::email_verification_required(&state).await?;
let verification_required = policy.auth.email_verification_required;
let verified_at = (!verification_required).then(Utc::now);
let user = sqlx::query_as::<_, UserRow>(
@@ -199,11 +206,12 @@ async fn login(
}
let ip = context::client_ip(&headers, addr.ip());
let policy = settings::runtime_policy(&state).await?;
rate_limit::enforce(
&state,
"auth_login_ip",
&ip.to_string(),
30,
policy.rate_limits.login_ip_per_5_minutes,
5 * 60,
"登录请求过于频繁,请稍后再试",
)
@@ -212,7 +220,7 @@ async fn login(
&state,
"auth_login_identity",
&identity.to_lowercase(),
10,
policy.rate_limits.login_identity_per_5_minutes,
5 * 60,
"该账号登录尝试过于频繁,请稍后再试",
)
@@ -269,7 +277,7 @@ async fn login(
if !credentials::verify_password(&req.password, &user.password_hash).await? {
return Err(AppError::new(ErrorCode::Unauthorized, "账号或密码错误"));
}
let verification_required = settings::email_verification_required(&state).await?;
let verification_required = policy.auth.email_verification_required;
let (token, expires_at) = auth::issue_jwt(
&state.config.jwt_secret,
@@ -305,8 +313,9 @@ async fn send_verification(
headers: HeaderMap,
) -> Result<Json<Envelope<MessageResponse>>, AppError> {
let claims = auth::require_jwt(&state.config.jwt_secret, &headers)?;
let policy = settings::runtime_policy(&state).await?;
if !settings::email_verification_required(&state).await? {
if !policy.auth.email_verification_required {
return Ok(Json(Envelope {
success: true,
data: MessageResponse {
@@ -319,7 +328,7 @@ async fn send_verification(
&state,
"auth_send_verification_user",
&claims.sub.to_string(),
1,
policy.rate_limits.verification_email_per_minute,
60,
"发送过于频繁,请稍后再试",
)
@@ -414,11 +423,12 @@ async fn verify_email(
}
let ip = context::client_ip(&headers, addr.ip());
let policy = settings::runtime_policy(&state).await?;
rate_limit::enforce(
&state,
"auth_verify_email_ip",
&ip.to_string(),
20,
policy.rate_limits.email_verify_ip_per_15_minutes,
15 * 60,
"验证请求过于频繁,请稍后再试",
)
@@ -484,11 +494,12 @@ async fn forgot_password(
credentials::validate_email(&req.email)?;
let ip = context::client_ip(&headers, addr.ip());
let policy = settings::runtime_policy(&state).await?;
rate_limit::enforce(
&state,
"auth_forgot_ip",
&ip.to_string(),
5,
policy.rate_limits.forgot_password_ip_per_15_minutes,
15 * 60,
"找回密码请求过于频繁,请稍后再试",
)
@@ -497,7 +508,7 @@ async fn forgot_password(
&state,
"auth_forgot_email",
&req.email.to_lowercase(),
3,
policy.rate_limits.forgot_password_email_per_15_minutes,
15 * 60,
"找回密码请求过于频繁,请稍后再试",
)
@@ -575,11 +586,12 @@ async fn reset_password(
credentials::validate_password(&req.new_password)?;
let ip = context::client_ip(&headers, addr.ip());
let policy = settings::runtime_policy(&state).await?;
rate_limit::enforce(
&state,
"auth_reset_ip",
&ip.to_string(),
10,
policy.rate_limits.password_reset_ip_per_15_minutes,
15 * 60,
"重置密码请求过于频繁,请稍后再试",
)
@@ -588,7 +600,7 @@ async fn reset_password(
&state,
"auth_reset_token",
&req.token,
5,
policy.rate_limits.password_reset_token_per_15_minutes,
15 * 60,
"该重置链接尝试次数过多,请重新申请",
)