pub mod auth; pub mod dictionary; pub mod info; pub mod major; use crate::state::AppState; use axum::{ Json, Router, extract::Request, extract::State, http::StatusCode, middleware::Next, response::{IntoResponse, Response}, }; use serde::Serialize; use std::sync::Arc; #[derive(Debug, Serialize)] struct ErrorResponseBody { error: String, } pub fn routes(state: Arc) -> Router> { Router::new() .nest("/info", info::routes()) .nest("/auth", auth::routes()) .nest("/dicts", dictionary::routes()) .nest("/major", major::routes()) .route_layer(axum::middleware::from_fn_with_state( state, |state: State>, request: Request, next: Next| async move { auth_middleware_inner(state, request, next).await }, )) } async fn auth_middleware_inner( state: State>, mut request: Request, next: Next, ) -> Response { let auth_header = request .headers() .get("Authorization") .and_then(|h| h.to_str().ok()); let api_key_header = request .headers() .get("X-API-Key") .and_then(|h| h.to_str().ok()); let token = if let Some(header) = auth_header { header.to_string() } else if let Some(key) = api_key_header { key.to_string() } else { let error = ErrorResponseBody { error: "Missing authorization header or API key".to_string(), }; return (StatusCode::UNAUTHORIZED, Json(error)).into_response(); }; match state.0.dependencies.auth_service.authenticate(&token).await { Ok(claims) => { request.extensions_mut().insert(claims); next.run(request).await } Err(_) => { let error = ErrorResponseBody { error: "Unauthorized".to_string(), }; (StatusCode::UNAUTHORIZED, Json(error)).into_response() } } }