use axum::{middleware, Router};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::time::Duration;
use tower::ServiceBuilder;
use tower_http::{
cors::CorsLayer,
timeout::TimeoutLayer,
trace::TraceLayer,
};
use super::{middleware as api_middleware, routes};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiServerConfig {
pub host: String,
pub port: u16,
pub request_timeout_secs: u64,
pub enable_cors: bool,
pub enable_logging: bool,
}
impl Default for ApiServerConfig {
fn default() -> Self {
Self {
host: "0.0.0.0".to_string(),
port: 8080,
request_timeout_secs: 30,
enable_cors: true,
enable_logging: true,
}
}
}
impl ApiServerConfig {
pub fn socket_addr(&self) -> Result<SocketAddr, String> {
format!("{}:{}", self.host, self.port)
.parse()
.map_err(|e| format!("Invalid socket address: {}", e))
}
}
pub struct ApiServer {
config: ApiServerConfig,
}
impl ApiServer {
pub fn new(config: ApiServerConfig) -> Self {
Self { config }
}
pub fn build_router(&self) -> Router {
create_api_router(&self.config)
}
pub async fn run(self) -> Result<(), Box<dyn std::error::Error>> {
let addr = self.config.socket_addr()?;
let app = self.build_router();
tracing::info!("Starting API server on {}", addr);
let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(listener, app).await?;
Ok(())
}
}
pub fn create_api_router(config: &ApiServerConfig) -> Router {
let mut router = routes::create_routes();
let middleware_stack = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_secs(
config.request_timeout_secs,
)))
.layer(middleware::from_fn(api_middleware::request_id_middleware));
router = router.layer(middleware_stack);
if config.enable_cors {
router = router.layer(CorsLayer::permissive());
}
if config.enable_logging {
router = router.layer(TraceLayer::new_for_http());
}
router
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = ApiServerConfig::default();
assert_eq!(config.host, "0.0.0.0");
assert_eq!(config.port, 8080);
assert!(config.enable_cors);
}
#[test]
fn test_socket_addr() {
let config = ApiServerConfig {
host: "127.0.0.1".to_string(),
port: 3000,
..Default::default()
};
let addr = config.socket_addr().unwrap();
assert_eq!(addr.to_string(), "127.0.0.1:3000");
}
#[test]
fn test_server_creation() {
let config = ApiServerConfig::default();
let server = ApiServer::new(config);
let _router = server.build_router();
}
}