a2eff1a984
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
115 lines
3.6 KiB
Rust
115 lines
3.6 KiB
Rust
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);
|
||
|
||
if config.turnstile_secret_key.is_some() {
|
||
tracing::info!("Bot doğrulaması (Cloudflare Turnstile) açık");
|
||
} else if config.is_https() {
|
||
tracing::warn!(
|
||
"Bot doğrulaması KAPALI: TURNSTILE_SITE_KEY / TURNSTILE_SECRET_KEY tanımlayın (docs/DEPLOY.md)"
|
||
);
|
||
}
|
||
tracing::info!(
|
||
"İstemci IP'si: {}",
|
||
if config.trust_proxy {
|
||
"X-Forwarded-For (vekil arkasında, TRUST_PROXY=true)"
|
||
} else {
|
||
"bağlantı adresi (TRUST_PROXY=false)"
|
||
}
|
||
);
|
||
|
||
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}");
|
||
// ConnectInfo: vekil yoksa istemci IP'si bağlantı adresinden (deneme kilidi)
|
||
if let Err(e) = axum::serve(
|
||
listener,
|
||
app.into_make_service_with_connect_info::<SocketAddr>(),
|
||
)
|
||
.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…");
|
||
}
|