feat(auth): kayıt, giriş, çıkış ve oturum (argon2 + JWT httpOnly cookie)

- Kayıt: kullanıcı + hane + örnek kategori/ödeme türü tek transaction'da
- Giriş: beni hatırla (30 gün / oturum), e-posta başına deneme sınırı
- AuthUser extractor: cookie'deki JWT'yi doğrular
- ValidJson: JSON + doğrulama, Türkçe hata mesajları
- api.http: auth istekleri eklendi
- Birim testleri: şifre, JWT, deneme sınırı, seed (11 test)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-11 23:37:52 +03:00
parent 4103b90e4b
commit d6136959b1
27 changed files with 1573 additions and 32 deletions
+17
View File
@@ -8,6 +8,7 @@ use axum::{
};
use serde_json::json;
#[allow(dead_code)] // NotFound/Forbidden: Faz 1 – Adım 4-5'te kullanılacak
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("Kayıt bulunamadı")]
@@ -16,6 +17,8 @@ pub enum AppError {
BadRequest(String),
#[error("Oturum açmanız gerekiyor")]
Unauthorized,
#[error("E-posta veya şifre hatalı")]
InvalidCredentials,
#[error("Bu işlem için yetkiniz yok")]
Forbidden,
#[error("{0}")]
@@ -34,6 +37,7 @@ impl AppError {
Self::NotFound => (StatusCode::NOT_FOUND, "not_found"),
Self::BadRequest(_) => (StatusCode::BAD_REQUEST, "bad_request"),
Self::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized"),
Self::InvalidCredentials => (StatusCode::UNAUTHORIZED, "invalid_credentials"),
Self::Forbidden => (StatusCode::FORBIDDEN, "forbidden"),
Self::Conflict(_) => (StatusCode::CONFLICT, "conflict"),
Self::TooManyRequests => (StatusCode::TOO_MANY_REQUESTS, "too_many_requests"),
@@ -64,5 +68,18 @@ impl IntoResponse for AppError {
}
}
/// Doğrulama hatası → ilk alanın Türkçe mesajıyla 400.
impl From<validator::ValidationErrors> for AppError {
fn from(errors: validator::ValidationErrors) -> Self {
let message = errors
.field_errors()
.values()
.flat_map(|list| list.iter())
.find_map(|e| e.message.as_ref().map(|m| m.to_string()))
.unwrap_or_else(|| "Girilen bilgiler geçersiz".into());
AppError::BadRequest(message)
}
}
/// Route fonksiyonlarında kısa dönüş tipi: `AppResult<Json<T>>`
pub type AppResult<T> = Result<T, AppError>;