From 3aff5f0123778aa453ab5396cb880a83267ae4ee Mon Sep 17 00:00:00 2001 From: mrw1593 Date: Fri, 12 May 2023 22:54:38 -0400 Subject: Add an update_user endpoint --- src/api/users.rs | 66 ++++++++++++++++++++++++++++++++++++++++++------------ src/services/db.rs | 38 ++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/api/users.rs b/src/api/users.rs index 5e409fd..edade22 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -1,46 +1,46 @@ use actix_web::http::{header, StatusCode}; -use actix_web::{post, web, HttpResponse, ResponseError, Scope}; +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::{new_user, username_is_used}; -use crate::services::id::new_user_id; +use crate::services::{db, id}; #[derive(Clone, Deserialize)] -struct CreateUser { +struct UserRequest { username: Box, password: Box, } #[derive(Debug, Clone, Hash, Error)] #[error("An account with the given username already exists.")] -struct CreateUserError { +struct UsernameTakenError { username: Box, } -impl ResponseError for CreateUserError { +impl ResponseError for UsernameTakenError { fn status_code(&self) -> StatusCode { StatusCode::CONFLICT } } -#[post("")] +#[post("/")] async fn create_user( - body: web::Json, + body: web::Json, conn: web::Data, -) -> Result { +) -> Result { let conn = conn.get_ref(); - let user_id = new_user_id(conn).await.unwrap(); + let user_id = 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 }); + if db::username_is_used(conn, &body.username).await.unwrap() { + yeet!(UsernameTakenError { username }); } let user = User { @@ -49,7 +49,7 @@ async fn create_user( password, }; - new_user(conn, user).await.unwrap(); + db::new_user(conn, &user).await.unwrap(); let response = HttpResponse::Created() .insert_header((header::LOCATION, format!("users/{user_id}"))) @@ -57,6 +57,44 @@ async fn create_user( 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) + web::scope("/users") + .service(create_user) + .service(update_user) } diff --git a/src/services/db.rs b/src/services/db.rs index efa9584..b508e1b 100644 --- a/src/services/db.rs +++ b/src/services/db.rs @@ -1,5 +1,5 @@ use exun::*; -use sqlx::{query, query_scalar, Executor, MySql, MySqlPool}; +use sqlx::{mysql::MySqlQueryResult, query, query_scalar, Executor, MySql, MySqlPool}; use uuid::Uuid; use crate::models::User; @@ -38,10 +38,21 @@ pub async fn username_is_used<'c>( Ok(exists) } +pub async fn get_username<'c>( + conn: impl Executor<'c, Database = MySql>, + user_id: Uuid, +) -> Result, RawUnexpected> { + let username = query_scalar!(r"SELECT username FROM users where user_id = ?", user_id) + .fetch_optional(conn) + .await?; + + Ok(username) +} + pub async fn new_user<'c>( conn: impl Executor<'c, Database = MySql>, - user: User, -) -> Result { + user: &User, +) -> Result { query!( r"INSERT INTO users (user_id, username, password_hash, password_salt, password_version) VALUES (?, ?, ?, ?, ?)", @@ -54,3 +65,24 @@ pub async fn new_user<'c>( .execute(conn) .await } + +pub async fn update_username<'c>( + conn: impl Executor<'c, Database = MySql>, + user: &User, +) -> Result { + query!( + r"UPDATE users SET + username = ?, + password_hash = ?, + password_salt = ?, + password_version = ? + WHERE user_id = ?", + user.username(), + user.password_hash(), + user.password_salt(), + user.password_version(), + user.user_id + ) + .execute(conn) + .await +} -- cgit v1.2.3