diff options
| author | Santo Cariotti <santo@dcariotti.me> | 2022-09-25 13:47:49 +0000 |
|---|---|---|
| committer | Santo Cariotti <santo@dcariotti.me> | 2022-09-25 13:47:49 +0000 |
| commit | 3a14419097ed710fb8b062d20078523c03b5b428 (patch) | |
| tree | 20870be7ece4816723e37d2d2ccae979d82f8507 /src | |
| parent | 159f5eda5e6937c4aaeae61cbca703761ee32003 (diff) | |
Edit user
Diffstat (limited to 'src')
| -rw-r--r-- | src/models/user.rs | 48 | ||||
| -rw-r--r-- | src/routes/auth.rs | 7 | ||||
| -rw-r--r-- | src/routes/model.rs | 2 | ||||
| -rw-r--r-- | src/routes/user.rs | 50 |
4 files changed, 99 insertions, 8 deletions
diff --git a/src/models/user.rs b/src/models/user.rs index d61795b..3f7a172 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -25,14 +25,24 @@ pub struct User { avatar: Option<String>, } +/// Paylod used for user editing +#[derive(Deserialize)] +pub struct UserEdit { + pub name: String, + pub email: String, + pub username: String, +} + /// Response used to print a user (or a users list) #[serde_as] -#[derive(Deserialize, Serialize, sqlx::FromRow)] +#[derive(Deserialize, Serialize, sqlx::FromRow, Validate)] pub struct UserList { pub id: i32, - name: String, - email: String, - username: String, + pub name: String, + #[validate(length(min = 4, message = "Can not be empty"))] + pub email: String, + #[validate(length(min = 2, message = "Can not be empty"))] + pub username: String, pub is_staff: Option<bool>, #[serde_as(as = "NoneAsEmptyString")] pub avatar: Option<String>, @@ -178,7 +188,7 @@ impl User { } impl UserList { - /// Edit an user + /// Edit an user avatar pub async fn edit_avatar(&mut self, avatar: Option<String>) -> Result<(), AppError> { let pool = unsafe { get_client() }; sqlx::query( @@ -196,6 +206,34 @@ impl UserList { Ok(()) } + /// Edit an user + pub async fn edit(&mut self, payload: UserEdit) -> Result<(), AppError> { + let pool = unsafe { get_client() }; + + // Make assignments before the `sqlx::query()` so to perform validation. + // If the `AppError::BadRequest` is raised, the query (and then the update) will be skipped + self.name = payload.name.clone(); + self.username = payload.username.clone(); + self.email = payload.email.clone(); + + self.validate() + .map_err(|error| AppError::BadRequest(error.to_string()))?; + + sqlx::query( + r#" + UPDATE users SET name = $1, username = $2, email = $3 WHERE id = $4 + "#, + ) + .bind(&payload.name) + .bind(&payload.username) + .bind(&payload.email) + .bind(self.id) + .execute(pool) + .await?; + + Ok(()) + } + /// Get all models created by an user pub async fn get_models(&self, page: i64) -> Result<Vec<ModelUser>, AppError> { Model::list_from_author(page, self.id).await diff --git a/src/routes/auth.rs b/src/routes/auth.rs index a7191e2..0c459f5 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -18,7 +18,12 @@ pub fn create_route() -> Router { /// Make login. Check if a user with the email and password passed in request body exists into the /// database async fn make_login(Json(payload): Json<LoginCredentials>) -> Result<Json<AuthBody>, AppError> { - let user = User::new(String::new(), String::new(), payload.username, payload.password); + let user = User::new( + String::new(), + String::new(), + payload.username, + payload.password, + ); match User::find(user).await { Ok(user) => { let claims = Claims::new(user.id); diff --git a/src/routes/model.rs b/src/routes/model.rs index 8c2e656..5fe75a1 100644 --- a/src/routes/model.rs +++ b/src/routes/model.rs @@ -119,6 +119,8 @@ async fn edit_model( payload.material, claims.user_id, ); + + // NOTE: can we edit this as same as `user.edit_avatar()`? Model::edit(model.id, model_body).await?; Ok(Json(model)) } diff --git a/src/routes/user.rs b/src/routes/user.rs index bc87bf5..5dc2c37 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -3,7 +3,7 @@ use crate::{ files::{delete_upload, upload}, models::{ auth::Claims, - user::{User, UserList}, + user::{User, UserEdit, UserList}, }, pagination::{ModelPagination, Pagination, UserPagination}, }; @@ -19,7 +19,7 @@ pub fn create_route() -> Router { .route("/", get(list_users)) .route("/me", get(get_me)) .route("/me/avatar", put(edit_my_avatar).delete(delete_my_avatar)) - .route("/:id", get(get_user)) + .route("/:id", get(get_user).put(edit_user)) .route("/:id/models", get(get_user_models)) } @@ -103,6 +103,52 @@ async fn get_user(Path(user_id): Path<i32>) -> Result<Json<UserList>, AppError> } } +/// Edit an user with id = `user_id`. Only staffers and owner of that account can perform this +/// action +async fn edit_user( + Path(user_id): Path<i32>, + Json(payload): Json<UserEdit>, + claims: Claims, +) -> Result<Json<UserList>, AppError> { + let mut user = match User::find_by_id(user_id).await { + Ok(user) => user, + Err(_) => { + return Err(AppError::NotFound("User not found".to_string())); + } + }; + + let claimed = match User::find_by_id(claims.user_id).await { + Ok(user) => user, + Err(_) => { + return Err(AppError::NotFound("User not found".to_string())); + } + }; + + if !(claimed.id == user.id || claimed.is_staff.unwrap()) { + return Err(AppError::Unauthorized); + } + + if user.email != payload.email { + if User::email_has_taken(&payload.email).await? { + return Err(AppError::BadRequest( + "An user with this email already exists".to_string(), + )); + } + } + + if user.username != payload.username { + if User::username_has_taken(&payload.username).await? { + return Err(AppError::BadRequest( + "An user with this username already exists".to_string(), + )); + } + } + + user.edit(payload).await?; + + Ok(Json(user)) +} + /// Get user models list async fn get_user_models( Path(user_id): Path<i32>, |
