From 80ed37647b37d674a7a870ccfa03299e55279038 Mon Sep 17 00:00:00 2001 From: Rorik Star Platinum Date: Sat, 8 Nov 2025 17:56:31 +0300 Subject: [PATCH] (fix) cors and logging at login and register --- Cargo.lock | 2 + Cargo.toml | 2 + src/api/accounts.rs | 2 +- src/auth/handlers.rs | 105 +++++++++++++++++++++++++++++++----------- src/main.rs | 40 +++++++++++----- src/route/handlers.rs | 2 +- 6 files changed, 111 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 50ad8ba..f845db3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1294,6 +1294,7 @@ dependencies = [ "sqlx", "thiserror", "tokio", + "tower-http", "tracing", "tracing-subscriber", "url", @@ -2539,6 +2540,7 @@ dependencies = [ "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 00f4ae9..5ec6a17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,8 @@ uuid = { version = "1", features = ["v4", "serde"] } tracing = "0.1" tracing-subscriber = "0.3" +tower-http = { version = "0.6", features = ["trace", "cors", ] } + thiserror = "2.0" anyhow = "1.0" diff --git a/src/api/accounts.rs b/src/api/accounts.rs index 553a738..dbedf0a 100644 --- a/src/api/accounts.rs +++ b/src/api/accounts.rs @@ -1,7 +1,7 @@ // src/api/accounts.rs // 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}; diff --git a/src/auth/handlers.rs b/src/auth/handlers.rs index 5fed2c2..947a754 100644 --- a/src/auth/handlers.rs +++ b/src/auth/handlers.rs @@ -12,6 +12,7 @@ use axum::{ }; use serde::{Deserialize, Serialize}; use serde_json::json; +use tracing::{info, warn, error, debug}; // ← ONLY ADD THIS use crate::{state::AppState, db}; use super::{jwt::generate_token, password::{hash_password, verify_password}}; @@ -38,8 +39,13 @@ pub async fn register_handler( State(state): State, Json(payload): Json, ) -> 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 if payload.password.len() < 3 { + error!("❌ Password too short"); // ← ADD return Err(( StatusCode::BAD_REQUEST, 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) if payload.bank_user_number < 1 || payload.bank_user_number > 10 { + error!("❌ Invalid bank_user_number: {}", payload.bank_user_number); // ← ADD return Err(( StatusCode::BAD_REQUEST, Json(json!({ "error": "bank_user_number must be between 1 and 10" })) @@ -56,28 +63,45 @@ pub async fn register_handler( // Hash password let password_hash = hash_password(&payload.password) - .map_err(|_| ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(json!({ "error": "Failed to hash password" })) - ))?; + .map_err(|e| { + error!("❌ Password hashing failed: {}", e); // ← ADD + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({ "error": "Failed to hash password" })) + ) + })?; + + debug!("βœ… Password hashed successfully"); // ← ADD // Build bank_user_id from environment + 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 let user = db::users::create_user(&state.db_pool, &bank_user_id, &password_hash) .await - .map_err(|e| ( - StatusCode::CONFLICT, - Json(json!({ "error": format!("User already exists or database error: {}", e) })) - ))?; + .map_err(|e| { + error!("❌ Database error during user creation: {}", e); // ← ADD + ( + 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 let token = generate_token(user.id, &user.bank_user_id) - .map_err(|_| ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(json!({ "error": "Failed to generate token" })) - ))?; + .map_err(|e| { + error!("❌ JWT generation failed: {}", e); // ← ADD + ( + 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)>(( StatusCode::CREATED, @@ -92,40 +116,63 @@ pub async fn login_handler( State(state): State, Json(payload): Json, ) -> 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 let user_data = db::users::get_user_by_bank_user_id(&state.db_pool, &payload.bank_user_id) .await - .map_err(|_| ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(json!({ "error": "Database error" })) - ))? - .ok_or_else(|| ( - StatusCode::UNAUTHORIZED, - Json(json!({ "error": "Invalid bank_user_id or password" })) - ))?; + .map_err(|e| { + error!("❌ Database error: {}", e); // ← ADD + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({ "error": "Database error" })) + ) + })? + .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; + info!("βœ… User found in database (ID: {})", user_id); // ← ADD // Verify password let is_valid = verify_password(&payload.password, &password_hash) - .map_err(|_| ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(json!({ "error": "Password verification failed" })) - ))?; + .map_err(|e| { + error!("❌ Password verification error: {}", e); // ← ADD + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({ "error": "Password verification failed" })) + ) + })?; if !is_valid { + warn!("⚠️ Invalid password for user: {}", payload.bank_user_id); // ← ADD return Err(( StatusCode::UNAUTHORIZED, Json(json!({ "error": "Invalid bank_user_id or password" })) )); } + info!("βœ… Password verified"); // ← ADD + // Generate JWT token let token = generate_token(user_id, &payload.bank_user_id) - .map_err(|_| ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(json!({ "error": "Failed to generate token" })) - ))?; + .map_err(|e| { + error!("❌ JWT generation failed: {}", e); // ← ADD + ( + 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)>(( StatusCode::OK, @@ -139,6 +186,8 @@ pub async fn login_handler( pub async fn me_handler( axum::extract::Extension(claims): axum::extract::Extension, ) -> impl IntoResponse { + info!("πŸ‘€ GET ME REQUEST from user: {}", claims.bank_user_id); // ← ADD + ( StatusCode::OK, Json(json!({ diff --git a/src/main.rs b/src/main.rs index df767bf..8088ffd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,34 +1,50 @@ // src/main.rs -mod route; -mod db; -mod state; + +use tower_http::trace::{TraceLayer, DefaultMakeSpan, DefaultOnResponse}; +use tower_http::cors::{CorsLayer, Any}; // ← Add CORS +use tracing::Level; + mod api; mod auth; +mod db; +mod route; +mod state; // ← Already have this -use crate::state::AppState; - - -/// ΠžΠ±Ρ‰Π΅Π΅ состояниС прилоТСния, пСрСдаётся Π²ΠΎ всС handlers -/// Π­Ρ‚ΠΎ ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½ Axum: State ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² extract'Π°Ρ… +use state::AppState; #[tokio::main] async fn main() { dotenvy::dotenv().ok(); - // Initialize tracing + // Initialize tracing with detailed logging tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) + .with_target(true) + .with_thread_ids(true) + .with_line_number(true) .init(); tracing::info!("πŸš€ Starting Multiberry Backend Server"); 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)); - println!("πŸš€ Server listening on {}", addr); + tracing::info!("πŸš€ Server listening on {}", addr); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); axum::serve(listener, app).await.unwrap(); } - diff --git a/src/route/handlers.rs b/src/route/handlers.rs index 8af6cc7..741efda 100644 --- a/src/route/handlers.rs +++ b/src/route/handlers.rs @@ -10,7 +10,7 @@ use axum::{ use serde::{Deserialize, Serialize}; use serde_json::json; use crate::{ - state::AppState, + AppState, api::{client::Bank, BankingError}, db, };