refactor: single refusal shape with openapi contract test

This commit is contained in:
2026-10-25 21:55:38 +01:00
parent 816d39ffd6
commit c8d9138349
25 changed files with 764 additions and 273 deletions

View File

@@ -1,14 +1,21 @@
use std::num::NonZeroU32;
use axum::{Router, routing};
use axum_governor::{GovernorConfigBuilder, GovernorLayer, Quota, extractor::PeerIp};
use axum::{Router, http::StatusCode, routing};
use axum_governor::{
GovernorConfigBuilder, GovernorLayer, Quota, RejectionReason, extractor::PeerIp,
};
use tower_http::{
cors::CorsLayer,
services::{ServeDir, ServeFile},
trace::TraceLayer,
};
use crate::{api, federation, html, state::AppState};
use crate::{
api,
errors::{INTERNAL, RATE_LIMITED, refuse},
federation, html,
state::AppState,
};
pub fn build_router(state: AppState, ap_router: Router) -> Router {
let rate_limit = state.app_ctx.config.rate_limit;
@@ -41,6 +48,22 @@ async fn health_handler() -> axum::Json<serde_json::Value> {
axum::Json(serde_json::json!({ "status": "ok" }))
}
/// JSON API rate-limit refusals use the one error shape.
fn slow_down(reason: RejectionReason) -> axum::response::Response {
match reason {
RejectionReason::QuotaExceeded { .. } => refuse(
StatusCode::TOO_MANY_REQUESTS,
RATE_LIMITED,
"too many requests: slow down and try again shortly",
),
RejectionReason::KeyExtractionFailed(_) => refuse(
StatusCode::INTERNAL_SERVER_ERROR,
INTERNAL,
"the caller could not be identified for rate limiting",
),
}
}
fn per_minute(n: u64) -> Quota {
let n = NonZeroU32::new(n.clamp(1, u32::MAX as u64) as u32).unwrap();
Quota::requests_per_minute(n)
@@ -111,6 +134,7 @@ fn api_routes(rate_limit: u64, cors_origins: &str) -> Router<AppState> {
.with_extractor(PeerIp::default())
.expect_connect_info()
.quota_default(per_minute(rate_limit))
.error_handler(slow_down)
.finish()
.unwrap();
@@ -120,6 +144,7 @@ fn api_routes(rate_limit: u64, cors_origins: &str) -> Router<AppState> {
.with_extractor(PeerIp::default())
.expect_connect_info()
.quota_default(per_minute(rate_limit / 4))
.error_handler(slow_down)
.finish()
.unwrap();
let webhook_routes = api::webhook_router().layer(GovernorLayer::new(webhook_cfg));