(fix) cors and logging at login and register

This commit is contained in:
Rorik Star Platinum 2025-11-08 17:56:31 +03:00
parent eef798df5f
commit 80ed37647b
6 changed files with 111 additions and 42 deletions

2
Cargo.lock generated
View file

@ -1294,6 +1294,7 @@ dependencies = [
"sqlx", "sqlx",
"thiserror", "thiserror",
"tokio", "tokio",
"tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"url", "url",
@ -2539,6 +2540,7 @@ dependencies = [
"tower", "tower",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing",
] ]
[[package]] [[package]]

View file

@ -26,6 +26,8 @@ uuid = { version = "1", features = ["v4", "serde"] }
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
tower-http = { version = "0.6", features = ["trace", "cors", ] }
thiserror = "2.0" thiserror = "2.0"
anyhow = "1.0" anyhow = "1.0"

View file

@ -1,7 +1,7 @@
// src/api/accounts.rs // src/api/accounts.rs
// Account data retrieval // Account data retrieval
use super::{client::{BankClient, BankingError}, models::{ApiResponse, AccountData, BalanceData, BalanceResponse}}; use super::{client::{BankClient, BankingError}, models::{ApiResponse, AccountData, BalanceResponse}};
use tracing::{info, error, debug}; use tracing::{info, error, debug};

View file

@ -12,6 +12,7 @@ use axum::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use tracing::{info, warn, error, debug}; // ← ONLY ADD THIS
use crate::{state::AppState, db}; use crate::{state::AppState, db};
use super::{jwt::generate_token, password::{hash_password, verify_password}}; use super::{jwt::generate_token, password::{hash_password, verify_password}};
@ -38,8 +39,13 @@ pub async fn register_handler(
State(state): State<AppState>, State(state): State<AppState>,
Json(payload): Json<RegisterRequest>, Json(payload): Json<RegisterRequest>,
) -> impl IntoResponse { ) -> impl IntoResponse {
info!("🔐 REGISTER REQUEST"); // ← ADD
debug!(" bank_user_number: {}", payload.bank_user_number); // ← ADD
debug!(" password length: {} chars", payload.password.len()); // ← ADD
// Validate password length // Validate password length
if payload.password.len() < 3 { if payload.password.len() < 3 {
error!("❌ Password too short"); // ← ADD
return Err(( return Err((
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
Json(json!({ "error": "Password must be at least 3 characters" })) Json(json!({ "error": "Password must be at least 3 characters" }))
@ -48,6 +54,7 @@ pub async fn register_handler(
// Validate bank_user_number (1-10) // Validate bank_user_number (1-10)
if payload.bank_user_number < 1 || payload.bank_user_number > 10 { if payload.bank_user_number < 1 || payload.bank_user_number > 10 {
error!("❌ Invalid bank_user_number: {}", payload.bank_user_number); // ← ADD
return Err(( return Err((
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
Json(json!({ "error": "bank_user_number must be between 1 and 10" })) Json(json!({ "error": "bank_user_number must be between 1 and 10" }))
@ -56,28 +63,45 @@ pub async fn register_handler(
// Hash password // Hash password
let password_hash = hash_password(&payload.password) let password_hash = hash_password(&payload.password)
.map_err(|_| ( .map_err(|e| {
StatusCode::INTERNAL_SERVER_ERROR, error!("❌ Password hashing failed: {}", e); // ← ADD
Json(json!({ "error": "Failed to hash password" })) (
))?; StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({ "error": "Failed to hash password" }))
)
})?;
debug!("✅ Password hashed successfully"); // ← ADD
// Build bank_user_id from environment + number // Build bank_user_id from environment + number
let bank_user_id = format!("{}-{}", state.bank_team_id, payload.bank_user_number); let bank_user_id = format!("{}-{}", state.bank_team_id, payload.bank_user_number);
info!(" Generated bank_user_id: {}", bank_user_id); // ← ADD
// Create user in database // Create user in database
let user = db::users::create_user(&state.db_pool, &bank_user_id, &password_hash) let user = db::users::create_user(&state.db_pool, &bank_user_id, &password_hash)
.await .await
.map_err(|e| ( .map_err(|e| {
StatusCode::CONFLICT, error!("❌ Database error during user creation: {}", e); // ← ADD
Json(json!({ "error": format!("User already exists or database error: {}", e) })) (
))?; StatusCode::CONFLICT,
Json(json!({ "error": format!("User already exists or database error: {}", e) }))
)
})?;
info!("✅ User created in database (ID: {})", user.id); // ← ADD
// Generate JWT token // Generate JWT token
let token = generate_token(user.id, &user.bank_user_id) let token = generate_token(user.id, &user.bank_user_id)
.map_err(|_| ( .map_err(|e| {
StatusCode::INTERNAL_SERVER_ERROR, error!("❌ JWT generation failed: {}", e); // ← ADD
Json(json!({ "error": "Failed to generate token" })) (
))?; StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({ "error": "Failed to generate token" }))
)
})?;
info!("✅ JWT token generated"); // ← ADD
info!("🎉 REGISTER SUCCESSFUL for user: {}", bank_user_id); // ← ADD
Ok::<_, (StatusCode, Json<serde_json::Value>)>(( Ok::<_, (StatusCode, Json<serde_json::Value>)>((
StatusCode::CREATED, StatusCode::CREATED,
@ -92,40 +116,63 @@ pub async fn login_handler(
State(state): State<AppState>, State(state): State<AppState>,
Json(payload): Json<LoginRequest>, Json(payload): Json<LoginRequest>,
) -> impl IntoResponse { ) -> impl IntoResponse {
info!("🔑 LOGIN REQUEST"); // ← ADD
debug!(" bank_user_id: {}", payload.bank_user_id); // ← ADD
debug!(" password length: {} chars", payload.password.len()); // ← ADD
// Look up user by bank_user_id // Look up user by bank_user_id
let user_data = db::users::get_user_by_bank_user_id(&state.db_pool, &payload.bank_user_id) let user_data = db::users::get_user_by_bank_user_id(&state.db_pool, &payload.bank_user_id)
.await .await
.map_err(|_| ( .map_err(|e| {
StatusCode::INTERNAL_SERVER_ERROR, error!("❌ Database error: {}", e); // ← ADD
Json(json!({ "error": "Database error" })) (
))? StatusCode::INTERNAL_SERVER_ERROR,
.ok_or_else(|| ( Json(json!({ "error": "Database error" }))
StatusCode::UNAUTHORIZED, )
Json(json!({ "error": "Invalid bank_user_id or password" })) })?
))?; .ok_or_else(|| {
warn!("⚠️ User not found: {}", payload.bank_user_id); // ← ADD
(
StatusCode::UNAUTHORIZED,
Json(json!({ "error": "Invalid bank_user_id or password" }))
)
})?;
let (user_id, password_hash) = user_data; let (user_id, password_hash) = user_data;
info!("✅ User found in database (ID: {})", user_id); // ← ADD
// Verify password // Verify password
let is_valid = verify_password(&payload.password, &password_hash) let is_valid = verify_password(&payload.password, &password_hash)
.map_err(|_| ( .map_err(|e| {
StatusCode::INTERNAL_SERVER_ERROR, error!("❌ Password verification error: {}", e); // ← ADD
Json(json!({ "error": "Password verification failed" })) (
))?; StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({ "error": "Password verification failed" }))
)
})?;
if !is_valid { if !is_valid {
warn!("⚠️ Invalid password for user: {}", payload.bank_user_id); // ← ADD
return Err(( return Err((
StatusCode::UNAUTHORIZED, StatusCode::UNAUTHORIZED,
Json(json!({ "error": "Invalid bank_user_id or password" })) Json(json!({ "error": "Invalid bank_user_id or password" }))
)); ));
} }
info!("✅ Password verified"); // ← ADD
// Generate JWT token // Generate JWT token
let token = generate_token(user_id, &payload.bank_user_id) let token = generate_token(user_id, &payload.bank_user_id)
.map_err(|_| ( .map_err(|e| {
StatusCode::INTERNAL_SERVER_ERROR, error!("❌ JWT generation failed: {}", e); // ← ADD
Json(json!({ "error": "Failed to generate token" })) (
))?; StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({ "error": "Failed to generate token" }))
)
})?;
info!("✅ JWT token generated"); // ← ADD
info!("🎉 LOGIN SUCCESSFUL for user: {}", payload.bank_user_id); // ← ADD
Ok::<_, (StatusCode, Json<serde_json::Value>)>(( Ok::<_, (StatusCode, Json<serde_json::Value>)>((
StatusCode::OK, StatusCode::OK,
@ -139,6 +186,8 @@ pub async fn login_handler(
pub async fn me_handler( pub async fn me_handler(
axum::extract::Extension(claims): axum::extract::Extension<super::jwt::Claims>, axum::extract::Extension(claims): axum::extract::Extension<super::jwt::Claims>,
) -> impl IntoResponse { ) -> impl IntoResponse {
info!("👤 GET ME REQUEST from user: {}", claims.bank_user_id); // ← ADD
( (
StatusCode::OK, StatusCode::OK,
Json(json!({ Json(json!({

View file

@ -1,34 +1,50 @@
// src/main.rs // src/main.rs
mod route;
mod db; use tower_http::trace::{TraceLayer, DefaultMakeSpan, DefaultOnResponse};
mod state; use tower_http::cors::{CorsLayer, Any}; // ← Add CORS
use tracing::Level;
mod api; mod api;
mod auth; mod auth;
mod db;
mod route;
mod state; // ← Already have this
use crate::state::AppState; use state::AppState;
/// Общее состояние приложения, передаётся во все handlers
/// Это паттерн Axum: State<AppState> используется в extract'ах
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();
// Initialize tracing // Initialize tracing with detailed logging
tracing_subscriber::fmt() tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG) .with_max_level(tracing::Level::DEBUG)
.with_target(true)
.with_thread_ids(true)
.with_line_number(true)
.init(); .init();
tracing::info!("🚀 Starting Multiberry Backend Server"); tracing::info!("🚀 Starting Multiberry Backend Server");
let app_state = AppState::new().await; let app_state = AppState::new().await;
let app = route::router(app_state);
// ✨ Add CORS layer
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
let app = route::router(app_state)
.layer(cors) // ← Add CORS before TraceLayer
.layer(
TraceLayer::new_for_http()
.make_span_with(DefaultMakeSpan::new().level(Level::INFO))
.on_response(DefaultOnResponse::new().level(Level::INFO))
);
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], 3000)); let addr = std::net::SocketAddr::from(([0, 0, 0, 0], 3000));
println!("🚀 Server listening on {}", addr); tracing::info!("🚀 Server listening on {}", addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
} }

View file

@ -10,7 +10,7 @@ use axum::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use crate::{ use crate::{
state::AppState, AppState,
api::{client::Bank, BankingError}, api::{client::Bank, BankingError},
db, db,
}; };