feat(backend): ayarlar, MongoDB bağlantısı, Türkçe hata yapısı ve DB'li health check

- config.rs: ortam değişkenleri ve doğrulama
- db.rs: bağlantı + ping (5 sn zaman aşımı)
- error.rs: AppError → Türkçe JSON hata yanıtı
- /api/health: veritabanına erişilemezse 503

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-11 23:09:46 +03:00
parent 306c550b28
commit 125bb7f476
13 changed files with 2068 additions and 27 deletions
+68
View File
@@ -0,0 +1,68 @@
//! Uygulama genelindeki hata türü.
//! Kullanıcıya Türkçe ve sade mesaj döner; teknik detay yalnızca loga yazılır.
use axum::{
Json,
http::StatusCode,
response::{IntoResponse, Response},
};
use serde_json::json;
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("Kayıt bulunamadı")]
NotFound,
#[error("{0}")]
BadRequest(String),
#[error("Oturum açmanız gerekiyor")]
Unauthorized,
#[error("Bu işlem için yetkiniz yok")]
Forbidden,
#[error("{0}")]
Conflict(String),
#[error("Çok fazla deneme yapıldı, lütfen biraz sonra tekrar deneyin")]
TooManyRequests,
#[error("veritabanı hatası: {0}")]
Database(#[from] mongodb::error::Error),
#[error("iç hata: {0}")]
Internal(String),
}
impl AppError {
fn status_and_code(&self) -> (StatusCode, &'static str) {
match self {
Self::NotFound => (StatusCode::NOT_FOUND, "not_found"),
Self::BadRequest(_) => (StatusCode::BAD_REQUEST, "bad_request"),
Self::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized"),
Self::Forbidden => (StatusCode::FORBIDDEN, "forbidden"),
Self::Conflict(_) => (StatusCode::CONFLICT, "conflict"),
Self::TooManyRequests => (StatusCode::TOO_MANY_REQUESTS, "too_many_requests"),
Self::Database(_) | Self::Internal(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, "internal")
}
}
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, code) = self.status_and_code();
let message = if status.is_server_error() {
// Teknik detay sadece logda; kullanıcıya genel mesaj.
tracing::error!(error = %self, "sunucu hatası");
"Beklenmeyen bir hata oluştu, lütfen tekrar deneyin".to_string()
} else {
self.to_string()
};
(
status,
Json(json!({ "error": { "code": code, "message": message } })),
)
.into_response()
}
}
/// Route fonksiyonlarında kısa dönüş tipi: `AppResult<Json<T>>`
pub type AppResult<T> = Result<T, AppError>;