87 lines
2.8 KiB
Rust
87 lines
2.8 KiB
Rust
use crate::model;
|
|
use crate::state;
|
|
use crate::utils;
|
|
use axum::extract::{Json, State};
|
|
use bcrypt::verify;
|
|
use chrono::{Duration, Utc};
|
|
use sqlx::Row;
|
|
use std::sync::Arc;
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/login",
|
|
request_body = LoginRequest,
|
|
responses(
|
|
(status = 200, description = "Login successful", body = AuthResponse),
|
|
(status = 400, description = "Invalid credentials", body = AuthResponse)
|
|
)
|
|
)]
|
|
pub async fn login_handler(
|
|
State(state): State<Arc<state::AppState>>,
|
|
Json(payload): Json<model::LoginRequest>,
|
|
) -> Json<model::AuthResponse> {
|
|
// Get user from database
|
|
let user_result = sqlx::query("SELECT username, password_hash FROM users WHERE phone = ?")
|
|
.bind(&payload.phone)
|
|
.fetch_optional(&state.db_pool)
|
|
.await;
|
|
|
|
match user_result {
|
|
Ok(Some(row)) => {
|
|
let username: String = row.get("username");
|
|
let password_hash: String = row.get("password_hash");
|
|
|
|
// Verify password
|
|
match verify(&payload.password, &password_hash) {
|
|
Ok(true) => {
|
|
// 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(&username)
|
|
.bind(&payload.phone)
|
|
.bind(expires_at.naive_utc())
|
|
.execute(&state.db_pool)
|
|
.await;
|
|
|
|
Json(model::AuthResponse {
|
|
success: true,
|
|
message: "Login successful".to_string(),
|
|
username: Some(username),
|
|
token: Some(token),
|
|
})
|
|
}
|
|
Ok(false) => Json(model::AuthResponse {
|
|
success: false,
|
|
message: "Invalid password".to_string(),
|
|
username: None,
|
|
token: None,
|
|
}),
|
|
Err(_) => Json(model::AuthResponse {
|
|
success: false,
|
|
message: "Error verifying password".to_string(),
|
|
username: None,
|
|
token: None,
|
|
}),
|
|
}
|
|
}
|
|
Ok(None) => Json(model::AuthResponse {
|
|
success: false,
|
|
message: "Phone number not found".to_string(),
|
|
username: None,
|
|
token: None,
|
|
}),
|
|
Err(_) => Json(model::AuthResponse {
|
|
success: false,
|
|
message: "Login failed".to_string(),
|
|
username: None,
|
|
token: None,
|
|
}),
|
|
}
|
|
}
|