mod api; mod config; mod error; mod fh_client; mod finnhub; mod mapping; mod message_consumer; // mod persistence; // Removed mod state; mod workflow_adapter; mod generic_worker; mod config_poller; use crate::config::AppConfig; use crate::error::Result; use crate::state::AppState; use tracing::info; use common_contracts::lifecycle::ServiceRegistrar; use common_contracts::registry::{ServiceRegistration, ProviderMetadata, ConfigFieldSchema, FieldType, ConfigKey}; use std::sync::Arc; #[tokio::main] async fn main() -> Result<()> { // Initialize logging tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .init(); info!("Starting finnhub-provider-service..."); // Load configuration let config = AppConfig::load().map_err(|e| error::AppError::Configuration(e.to_string()))?; let port = config.server_port; // Initialize application state let app_state = AppState::new(config.clone()); // --- Start the config poller --- tokio::spawn(config_poller::run_config_poller(app_state.clone())); // Create the Axum router let app = api::create_router(app_state.clone()); // --- Start the message consumer --- tokio::spawn(message_consumer::run(app_state)); // --- Service Registration --- let registrar = ServiceRegistrar::new( config.api_gateway_url.clone(), ServiceRegistration { service_id: format!("{}-{}", "finnhub-provider", uuid::Uuid::new_v4()), service_name: "finnhub".to_string(), role: common_contracts::registry::ServiceRole::DataProvider, base_url: format!("http://{}:{}", config.service_host, port), health_check_url: format!("http://{}:{}/health", config.service_host, port), metadata: Some(ProviderMetadata { id: "finnhub".to_string(), name_en: "Finnhub".to_string(), name_cn: "Finnhub".to_string(), description: "Finnhub Stock API".to_string(), icon_url: None, config_schema: vec![ ConfigFieldSchema { key: ConfigKey::ApiKey, label: "API Key".to_string(), field_type: FieldType::Password, required: true, placeholder: Some("Enter your API key...".to_string()), default_value: None, description: Some("Get it from https://finnhub.io".to_string()), options: None, }, ], supports_test_connection: true, }), } ); let _ = registrar.register().await; let registrar = Arc::new(registrar); tokio::spawn(registrar.clone().start_heartbeat_loop()); // Start the HTTP server let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)) .await .unwrap(); info!("HTTP server listening on port {}", port); axum::serve(listener, app) .with_graceful_shutdown(shutdown_signal(registrar)) .await .unwrap(); Ok(()) } async fn shutdown_signal(registrar: Arc) { let ctrl_c = async { tokio::signal::ctrl_c() .await .expect("failed to install Ctrl+C handler"); }; #[cfg(unix)] let terminate = async { tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) .expect("failed to install signal handler") .recv() .await; }; #[cfg(not(unix))] let terminate = std::future::pending::<()>(); tokio::select! { _ = ctrl_c => {}, _ = terminate => {}, } info!("Shutdown signal received, deregistering service..."); let _ = registrar.deregister().await; }