use crate::model; use crate::state; use crate::utils; use axum::extract::{Json, State}; use bcrypt::{DEFAULT_COST, hash}; use chrono::{Duration, Utc}; use std::sync::Arc; #[utoipa::path( post, path = "/api/register", request_body = RegisterRequest, responses( (status = 200, description = "Registration successful", body = AuthResponse), (status = 400, description = "Registration failed", body = AuthResponse) ) )] pub async fn register_handler( State(state): State>, Json(payload): Json, ) -> Json { // Validate input if payload.phone.len() < 5 { return Json(model::AuthResponse { success: false, message: "Phone number is too short".to_string(), username: None, token: None, }); } if payload.username.len() < 2 || payload.username.len() > state.config.limits.max_username_length { return Json(model::AuthResponse { success: false, message: format!( "Username must be between 3 and {} characters", state.config.limits.max_username_length ), username: None, token: None, }); } if payload.password.len() < state.config.limits.min_password_length { return Json(model::AuthResponse { success: false, message: format!( "Password must be at least {} characters", state.config.limits.min_password_length ), username: None, token: None, }); } // Hash password let password_hash = match hash(&payload.password, DEFAULT_COST) { Ok(hash) => hash, Err(_) => { return Json(model::AuthResponse { success: false, message: "Error hashing password".to_string(), username: None, token: None, }); } }; // Insert user into database let result = sqlx::query("INSERT INTO users (phone, username, password_hash) VALUES (?, ?, ?)") .bind(&payload.phone) .bind(&payload.username) .bind(&password_hash) .execute(&state.db_pool) .await; match result { Ok(_) => { // Generate session token let token = utils::generate_token(); let expires_at = Utc::now() + Duration::days(30); // Store session let _ = sqlx::query( "INSERT INTO sessions (token, username, phone, expires_at) VALUES (?, ?, ?, ?)", ) .bind(&token) .bind(&payload.username) .bind(&payload.phone) .bind(expires_at.naive_utc()) .execute(&state.db_pool) .await; Json(model::AuthResponse { success: true, message: "Registration successful".to_string(), username: Some(payload.username), token: Some(token), }) } Err(e) => { let error_message = if e .to_string() .contains("UNIQUE constraint failed: users.phone") { "Phone number already registered".to_string() } else if e .to_string() .contains("UNIQUE constraint failed: users.username") { "Username already taken".to_string() } else { "Registration failed".to_string() }; Json(model::AuthResponse { success: false, message: error_message, username: None, token: None, }) } } }