main.rs (3940B)
1 //! Products Service 2 //! 3 //! A microservice for managing product catalog, details, and ratings. 4 //! 5 //! # Features 6 //! - Product listing with pagination and filtering 7 //! - Product detail retrieval 8 //! - Product rating/review management 9 //! - PostgreSQL database with products schema 10 //! - CORS enabled for web client access 11 //! 12 //! # API Endpoints 13 //! - `GET /products` - List products with filters and pagination 14 //! - `GET /products/{id}` - Get detailed product information 15 //! - `PUT /products/{id}/ratings` - Create or update a product rating 16 //! 17 //! # Configuration 18 //! The service is configured via: 19 //! - Environment variables (loaded from .env if present) 20 //! - config.toml file 21 //! 22 //! # Database 23 //! Uses the `products` schema in PostgreSQL with automatic search path configuration. 24 //! Migrations are run automatically on startup. 25 26 mod config; 27 mod db; 28 mod handlers; 29 mod models; 30 mod telemetry; 31 mod utils; 32 33 use anyhow::Result; 34 use axum::{ 35 routing::{get, put}, 36 Router, 37 }; 38 use axum_otel_metrics::HttpMetricsLayerBuilder; 39 use axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer}; 40 use std::net::SocketAddr; 41 use tower_http::cors::CorsLayer; 42 use tracing::info; 43 44 use config::Config; 45 use db::Database; 46 47 #[tokio::main] 48 async fn main() -> Result<()> { 49 // Load environment variables from .env file if present 50 // This is optional - the service will work without it 51 dotenvy::dotenv().ok(); 52 53 let _telemetry_guard = telemetry::init_telemetry("products"); 54 55 // Load configuration from config.toml 56 let config = Config::load()?; 57 58 // Print startup information 59 println!("Starting Products Service..."); 60 println!("Server port: {}", config.server.port); 61 println!("Database URL: {}", config.database.url); 62 63 // Initialize database connection pool 64 // This sets the search path to the products schema for all connections 65 let db = Database::new(&config.database.url, config.database.max_connections).await?; 66 67 // Register observable gauges for connection pool health metrics 68 let meter = opentelemetry::global::meter("inventory-service"); 69 telemetry::register_pool_metrics(&meter, db.pool().clone()); 70 71 // Build automatic HTTP RED metrics layer 72 let metrics = HttpMetricsLayerBuilder::new().build(); 73 74 // Build the application router with all endpoints 75 let app = Router::new() 76 // Product endpoints 77 .route("/products", get(handlers::list_products)) 78 .route("/products/{id}", get(handlers::get_product_by_id)) 79 // Rating endpoints 80 .route("/products/{id}/ratings", put(handlers::upsert_rating)) 81 // Enable CORS for all routes 82 // This allows the Angular frontend to make API calls 83 .layer(CorsLayer::permissive()) 84 // Add database pool to application state 85 // All handlers will have access to this via State extractor 86 .with_state(db.pool().clone()) 87 // Automatic RED metrics (request rate, error rate, duration) 88 .layer(metrics) 89 .layer(OtelInResponseLayer) 90 .layer(OtelAxumLayer::default()) 91 .layer(CorsLayer::permissive()); 92 93 // Create socket address from configuration 94 let addr = SocketAddr::from(([0, 0, 0, 0], config.server.port)); 95 96 // Print API documentation 97 info!(port = config.server.port, "Products Service listening on "); 98 info!("\nProducts Service listening on {}", addr); 99 info!("\nAvailable endpoints:"); 100 info!(" GET /products - List products (with pagination & filters)"); 101 info!(" GET /products/{{id}} - Get product details"); 102 info!(" PUT /products/{{id}}/ratings - Create/update product rating"); 103 104 // Start the HTTP server 105 let listener = tokio::net::TcpListener::bind(addr).await?; 106 axum::serve(listener, app).await?; 107 108 // Telemetry is flushed and shut down by the `Drop` impl on `_telemetry` 109 // when this function returns. 110 111 Ok(()) 112 }