93 lines
2.8 KiB
Rust
93 lines
2.8 KiB
Rust
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 = "<input>")]
|
|
pub async fn register(pool: &State<PgPool>, input: Json<RegisterInput>) -> Result<Created<Json<TokenResponse>>, Custom<String>> {
|
|
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 = "<input>")]
|
|
pub async fn login(pool: &State<PgPool>, input: Json<LoginInput>) -> Result<Json<TokenResponse>, Custom<String>> {
|
|
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<AuthenticatedUser> {
|
|
Json(user)
|
|
}
|
|
|
|
#[delete("/me")]
|
|
pub async fn delete_user(pool: &State<PgPool>, user: AuthenticatedUser) -> Status {
|
|
let _ = sqlx::query!(
|
|
"DELETE FROM users WHERE id = $1",
|
|
user.user_id
|
|
)
|
|
.execute(pool.inner())
|
|
.await;
|
|
Status::NoContent
|
|
}
|