use super::AppState;
use axum::{
extract::State,
http::StatusCode,
middleware::Next,
response::{IntoResponse, Json, Response},
};
use lopi_ratelimit::TokenBucket;
use serde_json::json;
use std::net::{IpAddr, SocketAddr};
pub(super) async fn auth_middleware(
State(s): State<AppState>,
request: axum::extract::Request,
next: Next,
) -> Response {
if let Some(expected) = &s.auth_token {
let provided = request
.headers()
.get(axum::http::header::AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "));
if !provided.is_some_and(|p| lopi_core::constant_time_eq(p, expected.as_ref())) {
return (
StatusCode::UNAUTHORIZED,
Json(json!({"error": "unauthorized"})),
)
.into_response();
}
}
next.run(request).await
}
pub(super) async fn rate_limit_middleware(
State(s): State<AppState>,
req: axum::extract::Request,
next: Next,
) -> Response {
use axum::extract::connect_info::ConnectInfo;
let ip: IpAddr = req
.headers()
.get("x-forwarded-for")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.split(',').next())
.and_then(|s| s.trim().parse().ok())
.or_else(|| {
req.extensions()
.get::<ConnectInfo<SocketAddr>>()
.map(|c| c.0.ip())
})
.unwrap_or_else(|| IpAddr::from([127, 0, 0, 1]));
let bucket = s.rate_limiter.get(&ip).map_or_else(
|| {
let new_bucket = TokenBucket::new(60.0, 1.0);
s.rate_limiter.insert(ip, new_bucket.clone());
new_bucket
},
|b| b.clone(),
);
if !bucket.try_acquire(1.0).await {
return (
StatusCode::TOO_MANY_REQUESTS,
Json(json!({"error": "rate limit exceeded"})),
)
.into_response();
}
next.run(req).await
}