use actix_web::http::{header, StatusCode}; use actix_web::{post, web, HttpResponse, ResponseError, Scope}; use raise::yeet; use serde::Deserialize; use sqlx::MySqlPool; use thiserror::Error; use crate::models::User; use crate::services::crypto::PasswordHash; use crate::services::db::{new_user, username_is_used}; use crate::services::id::new_user_id; #[derive(Clone, Deserialize)] struct CreateUser { username: Box, password: Box, } #[derive(Debug, Clone, Hash, Error)] #[error("An account with the given username already exists.")] struct CreateUserError { username: Box, } impl ResponseError for CreateUserError { 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 = new_user_id(conn).await.unwrap(); let username = body.username.clone(); let password = PasswordHash::new(&body.password).unwrap(); if username_is_used(conn, &body.username).await.unwrap() { yeet!(CreateUserError { username }); } let user = User { user_id, username, password, }; new_user(conn, user).await.unwrap(); let response = HttpResponse::Created() .insert_header((header::LOCATION, format!("users/{user_id}"))) .finish(); Ok(response) } pub fn service() -> Scope { web::scope("users").service(create_user) }