Files
kakeibo/backend/src/main.rs
T
suphicakir 3f60efdc33 feat(birikim): otomatik ve manuel kurlar, portföy değerlemesi
- TCMB (döviz alış kuru) ve xaus.com (altın) saatlik arka planda çekilir, gün başına saklanır
- Kaynak erişilemezse son kur geçerli; altın için son USD kuru kullanılır
- Elle kur girişi/silme; aynı gün elle girilen kur önceliklidir
- GET /portfolio: güncel değer, kâr/zarar, toplamlar, son kur çekimi
- RATES_REFRESH_MINUTES ayarı

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 00:59:01 +03:00

95 lines
2.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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…");
}