use actix_web::http::{header, StatusCode}; use actix_web::{post, put, web, HttpResponse, ResponseError, Scope}; use raise::yeet; use serde::Deserialize; use sqlx::MySqlPool; use thiserror::Error; use uuid::Uuid; use crate::models::User; use crate::services::crypto::PasswordHash; use crate::services::{db, id}; #[derive(Clone, Deserialize)] struct UserRequest { username: Box, password: Box, } #[derive(Debug, Clone, Hash, Error)] #[error("An account with the given username already exists.")] struct UsernameTakenError { username: Box, } impl ResponseError for UsernameTakenError { fn status_code(&self) -> StatusCode { StatusCode::CONFLICT } } #[post("/")] async fn create_user( body: web::Json, conn: web::Data, ) -> Result { let conn = conn.get_ref(); let user_id = id::new_user_id(conn).await.unwrap(); let username = body.username.clone(); let password = PasswordHash::new(&body.password).unwrap(); if db::username_is_used(conn, &body.username).await.unwrap() { yeet!(UsernameTakenError { username }); } let user = User { user_id, username, password, }; db::new_user(conn, &user).await.unwrap(); let response = HttpResponse::Created() .insert_header((header::LOCATION, format!("users/{user_id}"))) .finish(); Ok(response) } #[put("/{user_id}")] async fn update_user( user_id: web::Path, body: web::Json, conn: web::Data, ) -> Result { let conn = conn.get_ref(); let user_id = user_id.to_owned(); let username = body.username.clone(); let password = PasswordHash::new(&body.password).unwrap(); let old_username = db::get_username(conn, user_id) .await .unwrap() .unwrap() .into_boxed_str(); if username != old_username && db::username_is_used(conn, &body.username).await.unwrap() { yeet!(UsernameTakenError { username }) } let user = User { user_id, username, password, }; db::update_username(conn, &user).await.unwrap(); let response = HttpResponse::NoContent() .insert_header((header::LOCATION, format!("users/{user_id}"))) .finish(); Ok(response) } pub fn service() -> Scope { web::scope("/users") .service(create_user) .service(update_user) }