use axum::extract::{Request, State};
use axum::middleware::Next;
use axum::response::{IntoResponse, Response};
use yorishiro_core::models::maintenance;
use crate::error::ApiError;
use crate::state::AppState;
fn always_served(path: &str) -> bool {
matches!(path, "/up" | "/health" | "/api/system/maintenance")
}
fn is_write(request: &Request) -> bool {
!matches!(request.method().as_str(), "GET" | "HEAD" | "OPTIONS")
}
pub async fn maintenance_guard(
State(state): State<AppState>,
request: Request,
next: Next,
) -> Response {
if always_served(request.uri().path()) {
return next.run(request).await;
}
let mut conn = match state.identity_pool.acquire().await {
Ok(conn) => conn,
Err(_) => return next.run(request).await,
};
let current = match maintenance::get(&mut *conn).await {
Ok(current) => current,
Err(err) => return ApiError::from(err).into_response(),
};
drop(conn);
match current.refusal(is_write(&request)) {
Some(err) => {
let retry_after = current.retry_after;
let mut response = ApiError::from(err).into_response();
if let Ok(value) = retry_after.to_string().parse() {
response.headers_mut().insert("retry-after", value);
}
response
}
None => next.run(request).await,
}
}
#[cfg(test)]
#[path = "../../../tests/http/middleware/maintenance.rs"]
mod tests;