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 }