Files
msgs/src/api/register.rs
T
wt 9f0681563e
Continuous integration / Check (push) Successful in 42s
Continuous integration / Rustfmt (push) Successful in 34s
Continuous integration / build (push) Successful in 43s
Continuous integration / build-release (push) Successful in 1m12s
added swagget-ui
2026-02-15 18:04:11 +07:00

127 lines
3.7 KiB
Rust

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<Arc<state::AppState>>,
Json(payload): Json<model::RegisterRequest>,
) -> Json<model::AuthResponse> {
// 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,
})
}
}
}