use anyhow::Result; use axum::Router; use axum::http::HeaderValue; use std::sync::Arc; use tower_http::{ cors::{Any, CorsLayer}, trace::TraceLayer, }; use adapters_postgres::{connect, run_migrations}; use adapters_storage::LocalFileStorage; use presentation::{routes::app_router, state::AppState}; use crate::config::Config; use crate::services; pub async fn build_app(config: &Config) -> Result { let pool = connect(&config.database_url).await?; run_migrations(&pool).await?; let nats_client = async_nats::connect(&config.nats_url).await?; adapters_nats::ensure_stream(&nats_client).await?; let transport = adapters_nats::NatsTransport::new(nats_client); let nats_publisher: Arc = Arc::new(event_transport::EventPublisherAdapter::new(transport)); let event_store: Arc = Arc::new(adapters_postgres::PostgresEventStore::new(pool.clone())); let event_publisher: Arc = Arc::new(event_transport::CompositeEventPublisher::new(nats_publisher, event_store)); let storage_path = std::env::var("STORAGE_PATH").unwrap_or_else(|_| "./data/media".to_string()); let file_storage: Arc = Arc::new(LocalFileStorage::new(&storage_path)); // Build per-context services let identity = services::identity::build(&pool, &config.jwt_secret); let (storage_repos, storage) = services::storage::build(&pool); let catalog = services::catalog::build(&pool, &storage_repos, file_storage, event_publisher.clone()); let organization = services::organization::build(&pool); let sidecar = services::sidecar::build(&pool); let processing = services::processing::build(&pool, event_publisher.clone()); let sharing = services::sharing::build(&pool, event_publisher); let state = AppState { identity: identity.handlers, catalog, organization, storage, sharing, sidecar, processing, token_issuer: identity.token_issuer, }; let cors = CorsLayer::new() .allow_origin( config .cors_allowed_origins .iter() .filter_map(|o| o.parse::().ok()) .collect::>(), ) .allow_methods(Any) .allow_headers(Any); Ok(app_router() .with_state(state) .layer(TraceLayer::new_for_http()) .layer(cors)) }