use rocket::serde::{Deserialize, Serialize, json::Json};
use rocket::State;
use sqlx::PgPool;
use rocket::http::Status;
use rocket::response::status::{Created, Custom};
use crate::models::User;
use crate::auth::{hash_password, verify_password, generate_token, AuthenticatedUser};
#[derive(Debug, Deserialize)]
pub struct RegisterInput {
pub username: String,
pub password: String,
}
#[derive(Debug, Deserialize)]
pub struct LoginInput {
pub username: String,
pub password: String,
}
#[derive(Debug, Serialize)]
pub struct TokenResponse {
pub token: String,
}
#[post("/register", data = "")]
pub async fn register(pool: &State, input: Json) -> Result>, Custom> {
let password_hash = hash_password(&input.password).map_err(|_| Custom(Status::InternalServerError, "Hash error".into()))?;
let existing = sqlx::query_scalar!(
"SELECT id FROM users WHERE name = $1",
input.username
)
.fetch_optional(pool.inner())
.await
.map_err(|_| Custom(Status::InternalServerError, "DB error".into()))?;
if existing.is_some() {
return Err(Custom(Status::Conflict, "Username already exists".into()));
}
let rec = sqlx::query!(
"INSERT INTO users (name, password_hash, role, quota) VALUES ($1, $2, 'user', 104857600) RETURNING id",
input.username,
password_hash,
)
.fetch_one(pool.inner())
.await
.map_err(|_| Custom(Status::InternalServerError, "Insert error".into()))?;
let token = generate_token(rec.id, "user");
Ok(Created::new("/login").body(Json(TokenResponse { token })))
}
#[post("/login", data = "")]
pub async fn login(pool: &State, input: Json) -> Result, Custom> {
let user = sqlx::query_as!(
User,
"SELECT * FROM users WHERE name = $1",
input.username
)
.fetch_optional(pool.inner())
.await
.map_err(|_| Custom(Status::InternalServerError, "DB error".into()))?;
let user = user.ok_or_else(|| Custom(Status::Unauthorized, "Invalid credentials".into()))?;
if !verify_password(&input.password, &user.password_hash) {
return Err(Custom(Status::Unauthorized, "Invalid credentials".into()));
}
let user_id = user.id.ok_or_else(|| Custom(Status::InternalServerError, "User ID missing".into()))?;
let token = generate_token(user_id, &user.role);
Ok(Json(TokenResponse { token }))
}
#[get("/me")]
pub async fn get_user_info(user: AuthenticatedUser) -> Json {
Json(user)
}
#[delete("/me")]
pub async fn delete_user(pool: &State, user: AuthenticatedUser) -> Status {
let _ = sqlx::query!(
"DELETE FROM users WHERE id = $1",
user.user_id
)
.execute(pool.inner())
.await;
Status::NoContent
}