use axum::{ body::Body, error_handling::HandleErrorLayer, headers::HeaderName, http::{HeaderValue, Request, StatusCode}, response::{IntoResponse, Response}, routing::get, BoxError, Router, }; use tower::{ServiceBuilder, ServiceExt}; use tower_governor::{ errors::display_error, governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor, GovernorLayer, }; use tower_http::services::ServeFile; use crate::types::http::ResponseCode; pub mod admin; pub mod console; pub mod file; pub mod pages; pub fn router() -> Router { let governor_conf = Box::new( GovernorConfigBuilder::default() .burst_size(20) .per_second(1) .key_extractor(SmartIpKeyExtractor) .finish() .expect("Failed to create rate limiter"), ); Router::new() .nest("/", pages::router()) .route("/favicon.ico",get(file::favicon)) .route("/js/*path", get(file::js)) .route("/css/*path", get(file::css)) .route("/fonts/*path", get(file::fonts)) .route("/image/*path", get(file::image)) .route("/image/avatar", get(file::avatar)) .route("/image/banner", get(file::banner)) .layer( ServiceBuilder::new() .layer(HandleErrorLayer::new(|e: BoxError| async move { display_error(e) })) .layer(GovernorLayer { config: Box::leak(governor_conf), }), ) } pub async fn serve(path: &str) -> Response { if !path.chars().any(|c| c == '.') { return ResponseCode::BadRequest.text("Invalid file path"); } let path = format!("public{path}"); let file = ServeFile::new(path); let Ok(mut res) = file.oneshot(Request::new(Body::empty())).await else { tracing::error!("Error while fetching file"); return ResponseCode::InternalServerError.text("Error while fetching file"); }; if res.status() != StatusCode::OK { return ResponseCode::NotFound.text("File not found"); } res.headers_mut().insert( HeaderName::from_static("cache-control"), HeaderValue::from_static("public max-age=300"), ); res.into_response() }