re-uploading work
This commit is contained in:
125
crates/api/src/server.rs
Normal file
125
crates/api/src/server.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
//! Server setup and lifecycle management
|
||||
|
||||
use anyhow::Result;
|
||||
use axum::{middleware, Router};
|
||||
use std::sync::Arc;
|
||||
use tokio::net::TcpListener;
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::trace::TraceLayer;
|
||||
use tracing::info;
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
use crate::{
|
||||
middleware::{create_cors_layer, log_request},
|
||||
openapi::ApiDoc,
|
||||
routes,
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
/// Server configuration and lifecycle manager
|
||||
pub struct Server {
|
||||
/// Application state
|
||||
state: Arc<AppState>,
|
||||
/// Server host address
|
||||
host: String,
|
||||
/// Server port
|
||||
port: u16,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Create a new server instance
|
||||
pub fn new(state: Arc<AppState>) -> Self {
|
||||
let host = state.config.server.host.clone();
|
||||
let port = state.config.server.port;
|
||||
|
||||
Self { state, host, port }
|
||||
}
|
||||
|
||||
/// Get the router for testing purposes
|
||||
pub fn router(&self) -> Router {
|
||||
self.build_router()
|
||||
}
|
||||
|
||||
/// Build the application router with all routes and middleware
|
||||
fn build_router(&self) -> Router {
|
||||
// API v1 routes (versioned endpoints)
|
||||
let api_v1 = Router::new()
|
||||
.merge(routes::pack_routes())
|
||||
.merge(routes::action_routes())
|
||||
.merge(routes::rule_routes())
|
||||
.merge(routes::execution_routes())
|
||||
.merge(routes::trigger_routes())
|
||||
.merge(routes::inquiry_routes())
|
||||
.merge(routes::event_routes())
|
||||
.merge(routes::key_routes())
|
||||
.merge(routes::workflow_routes())
|
||||
.merge(routes::webhook_routes())
|
||||
// TODO: Add more route modules here
|
||||
// etc.
|
||||
.with_state(self.state.clone());
|
||||
|
||||
// Auth routes at root level (not versioned for frontend compatibility)
|
||||
let auth_routes = routes::auth_routes().with_state(self.state.clone());
|
||||
|
||||
// Health endpoint at root level (operational endpoint, not versioned)
|
||||
let health_routes = routes::health_routes().with_state(self.state.clone());
|
||||
|
||||
// Root router with versioning and documentation
|
||||
Router::new()
|
||||
.merge(SwaggerUi::new("/docs").url("/api-spec/openapi.json", ApiDoc::openapi()))
|
||||
.merge(health_routes)
|
||||
.nest("/auth", auth_routes)
|
||||
.nest("/api/v1", api_v1)
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
// Add tracing for all requests
|
||||
.layer(TraceLayer::new_for_http())
|
||||
// Add CORS support with configured origins
|
||||
.layer(create_cors_layer(self.state.cors_origins.clone()))
|
||||
// Add custom request logging
|
||||
.layer(middleware::from_fn(log_request)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Start the server and listen for requests
|
||||
pub async fn run(self) -> Result<()> {
|
||||
let router = self.build_router();
|
||||
let addr = format!("{}:{}", self.host, self.port);
|
||||
|
||||
info!("Starting server on {}", addr);
|
||||
info!("API documentation available at http://{}/docs", addr);
|
||||
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
info!("Server listening on {}", addr);
|
||||
|
||||
axum::serve(listener, router).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Graceful shutdown handler
|
||||
pub async fn shutdown(&self) {
|
||||
info!("Shutting down server...");
|
||||
// Perform any cleanup here
|
||||
// - Close database connections
|
||||
// - Flush logs
|
||||
// - Wait for in-flight requests
|
||||
info!("Server shutdown complete");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[tokio::test]
|
||||
#[ignore] // Ignore until we have test database setup
|
||||
async fn test_server_creation() {
|
||||
// This test is ignored because it requires a test database pool
|
||||
// When implemented, create a test pool and verify server creation
|
||||
// let pool = PgPool::connect(&test_db_url).await.unwrap();
|
||||
// let state = AppState::new(pool);
|
||||
// let server = Server::new(state, "127.0.0.1".to_string(), 8080);
|
||||
// assert_eq!(server.host, "127.0.0.1");
|
||||
// assert_eq!(server.port, 8080);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user