use std::{net::SocketAddr, process::ExitCode}; use kakeibo::{config::Config, db, routes, services, state::AppState}; use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> ExitCode { // .env dosyasını yükle (proje kökünde; üst klasörler de aranır). Yoksa sorun değil. dotenvy::dotenv().ok(); tracing_subscriber::fmt() .with_env_filter( EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("kakeibo=info")), ) .init(); let config = match Config::from_env() { Ok(c) => c, Err(e) => { tracing::error!("Ayar hatası: {e}"); return ExitCode::FAILURE; } }; let db = match db::connect(&config.mongodb_uri).await { Ok(db) => { tracing::info!("MongoDB bağlantısı kuruldu (veritabanı: {})", db.name()); db } Err(e) => { tracing::error!( "MongoDB'ye bağlanılamadı. MONGODB_URI doğru mu, veritabanı çalışıyor mu? \ (yerelde: npm run db:up)\nDetay: {e}" ); return ExitCode::FAILURE; } }; if let Err(e) = db::prepare(&db).await { tracing::error!("Veritabanı hazırlanamadı (migration/indeks): {e}"); return ExitCode::FAILURE; } // Kurlar arka planda (açılışta hemen, sonra aralıkla); hata uygulamayı durdurmaz services::rates::spawn_refresher(db.clone(), config.rates_refresh_minutes); let port = config.port; let state = AppState::new(db, config); let app = routes::app(state); let addr = SocketAddr::from(([0, 0, 0, 0], port)); let listener = match tokio::net::TcpListener::bind(addr).await { Ok(l) => l, Err(e) => { tracing::error!( "{port} portu dinlenemedi (başka bir uygulama kullanıyor olabilir): {e}" ); return ExitCode::FAILURE; } }; tracing::info!("Kakeibo API çalışıyor: http://localhost:{port}"); if let Err(e) = axum::serve(listener, app) .with_graceful_shutdown(shutdown_signal()) .await { tracing::error!("Sunucu hatası: {e}"); return ExitCode::FAILURE; } ExitCode::SUCCESS } /// Docker/Dokploy durdururken (SIGTERM) veya Ctrl+C'de: yeni istek almayı bırak, /// süren istekleri bitir, sonra kapan (yarım kalan yazma olmasın). async fn shutdown_signal() { let ctrl_c = async { let _ = tokio::signal::ctrl_c().await; }; #[cfg(unix)] let terminate = async { if let Ok(mut s) = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) { s.recv().await; } }; #[cfg(not(unix))] let terminate = std::future::pending::<()>(); tokio::select! { _ = ctrl_c => {}, _ = terminate => {}, } tracing::info!("Kapatma sinyali alındı, sunucu kapanıyor…"); }