//! Provisioning Platform Daemon //! //! A thin wrapper around daemon-cli library with provisioning-specific //! configuration. All core functionality (Nushell execution, config rendering, //! i18n) is provided by daemon-cli. mod config; use std::path::PathBuf; use std::sync::Arc; use axum::Router; use clap::Parser; use config::ProvisioningDaemonConfigWrapper; use daemon_cli::{ api::api_routes, core::{DaemonConfig, HierarchicalCache, Result}, orchestration::{ AuditOperation, EncryptOperation, InitServsOperation, OperationRegistry, RuntimeOperation, ValidaOperation, }, AppState, }; use tokio::net::TcpListener; use tracing_subscriber::EnvFilter; /// Provisioning daemon - Nushell execution and configuration rendering service #[derive(Parser, Debug)] #[command(name = "provisioning-daemon")] #[command(about = "Provisioning platform daemon with Nushell execution and config rendering")] #[command(version = env!("CARGO_PKG_VERSION"))] struct Args { /// Configuration file path (highest priority) #[arg(short = 'c', long, env = "PROVISIONING_DAEMON_CONFIG")] config: Option, /// Configuration directory (searches for provisioning-daemon.ncl|toml|json) #[arg(long, env = "PROVISIONING_CONFIG_DIR")] config_dir: Option, /// Deployment mode (solo, multiuser, cicd, enterprise) #[arg(short = 'm', long, env = "PROVISIONING_DAEMON_MODE")] mode: Option, /// Enable verbose logging #[arg(short = 'v', long)] verbose: bool, /// Validate configuration and exit #[arg(long)] validate_config: bool, /// Show configuration and exit #[arg(long)] show_config: bool, } #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); // Initialize logging let log_level = if args.verbose { "debug" } else { "info" }; let filter = EnvFilter::new(format!( "provisioning_daemon={},tower_http=info,daemon_cli=info", log_level )); tracing_subscriber::fmt() .with_env_filter(filter) .with_target(false) .init(); tracing::info!( "Starting Provisioning Daemon v{}", env!("CARGO_PKG_VERSION") ); // Load configuration with hierarchical support let config_wrapper = if let Some(explicit_path) = args.config { // Use explicit path from CLI argument ProvisioningDaemonConfigWrapper { config_path: explicit_path, } } else { // Use hierarchical loading (env vars or defaults) ProvisioningDaemonConfigWrapper::load_from_hierarchy() }; tracing::debug!("Loading configuration from: {}", config_wrapper.path_str()); // Load configuration using external daemon library let config = DaemonConfig::load(Some(config_wrapper.path().clone()))?; // Handle special commands if args.validate_config { config.validate()?; println!("✓ Configuration is valid"); return Ok(()); } if args.show_config { config.print(); return Ok(()); } // Validate configuration config.validate()?; tracing::info!("Configuration validated: bind={}", config.server.bind); // Create cache system let cache = HierarchicalCache::new()?; let cache_arc = Arc::new(cache); // Create operation registry let mut registry = OperationRegistry::default(); registry.register("valida", Arc::new(ValidaOperation::new(cache_arc.clone()))); registry.register( "runtime", Arc::new(RuntimeOperation::new(cache_arc.clone())), ); registry.register( "encrypt", Arc::new(EncryptOperation::new(cache_arc.clone())), ); registry.register( "init-servs", Arc::new(InitServsOperation::new(cache_arc.clone())), ); registry.register("audit", Arc::new(AuditOperation::new(cache_arc.clone()))); tracing::info!( "✓ Registered {} operations: {}", registry.len(), registry.list().join(", ") ); // Create app state let state = AppState::new(config.clone(), cache_arc, registry); // Create router let app = Router::new().nest("/api/v1", api_routes(state.clone())); // Start server let addr = config.bind_addr()?; let listener = TcpListener::bind(addr).await?; tracing::info!("✓ Provisioning daemon listening on http://{}", addr); tracing::info!("API documentation: http://{}/api/v1/health", addr); tracing::info!("Config rendering: http://{}/api/v1/render", addr); tracing::info!("i18n translation: http://{}/api/v1/translate", addr); // Run server axum::serve(listener, app).await?; Ok(()) }