lmrc-cli 0.3.16

CLI tool for scaffolding LMRC Stack infrastructure projects
Documentation
//! {{app_name}} - HTTP API Service
//!
//! A clean HTTP API service built with Axum and SeaORM, following hexagonal architecture.
//!
//! ## Architecture
//!
//! Hexagonal architecture (Ports & Adapters):
//! - **Inbound adapters**: HTTP handlers in `features/*/handler.rs`
//! - **Core logic**: Business logic in `features/*/service.rs`
//! - **Outbound adapters**: Database access via SeaORM entities in `adapters/`
//!
//! ## Features
//!
//! - RESTful API with Axum
//! - PostgreSQL database with SeaORM
//! - CORS support
//! - Request logging and tracing
//! - Health check endpoint
//! - Structured error handling

mod adapters;
mod config;
mod error;
mod features;
mod state;

use axum::{
    middleware,
    routing::get,
    Router,
};
use lmrc_http_common::middleware::{add_request_id, cors_with_origins, log_request};
use sea_orm::Database;
use state::AppState;
use std::net::SocketAddr;
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Initialize tracing
    tracing_subscriber::registry()
        .with(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "{{app_name}}=debug,tower_http=debug".into()),
        )
        .with(tracing_subscriber::fmt::layer())
        .init();

    // Load environment variables from .env if present
    dotenvy::dotenv().ok();

    // Load configuration
    let config = config::Config::from_env()?;

    tracing::info!("Starting {{app_name}} on {}:{}", config.server.host, config.server.port);

    // Connect to database
    tracing::info!("Connecting to database...");
    let db = Database::connect(&config.database.url).await?;
    tracing::info!("Database connected successfully");

    // Create application state
    let state = AppState::new(config.clone(), db);

    // Build router
    let app = create_router(state);

    // Start server
    let addr = SocketAddr::from(([0, 0, 0, 0], config.server.port));
    tracing::info!("Listening on {}", addr);

    let listener = tokio::net::TcpListener::bind(addr).await?;
    axum::serve(listener, app).await?;

    Ok(())
}

/// Create the application router
fn create_router(state: AppState) -> Router {
    use axum::routing::{delete, post};

    Router::new()
        .route("/health", get(features::health::health_check))
        // Example CRUD routes - Users API
        .route(
            "/api/users",
            get(features::examples::handler::list_users)
                .post(features::examples::handler::create_user),
        )
        .route(
            "/api/users/search",
            get(features::examples::handler::search_users),
        )
        .route(
            "/api/users/:id",
            get(features::examples::handler::get_user)
                .put(features::examples::handler::update_user)
                .delete(features::examples::handler::delete_user),
        )
        // Add your additional API routes here
        // .route("/api/products", get(features::products::list_products))
        .layer(TraceLayer::new_for_http())
        .layer(middleware::from_fn(log_request))
        .layer(middleware::from_fn(add_request_id))
        .layer(cors_with_origins(state.config.server.cors_origins.clone()))
        .with_state(state)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_router_compiles() {
        // Basic compilation test
        let config = config::Config {
            server: config::ServerConfig {
                host: "localhost".to_string(),
                port: 8080,
                cors_origins: vec!["http://localhost:3000".to_string()],
            },
            database: config::DatabaseConfig {
                url: "postgres://test:test@localhost/test".to_string(),
                max_connections: 5,
            },
        };

        // Note: This will fail without a real database connection
        // In production tests, use a test database or mock
    }
}