(fix) cors and logging at login and register
This commit is contained in:
parent
eef798df5f
commit
80ed37647b
6 changed files with 111 additions and 42 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -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]]
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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!({
|
||||||
|
|
|
||||||
40
src/main.rs
40
src/main.rs
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue