summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/models/user.rs48
-rw-r--r--src/routes/auth.rs7
-rw-r--r--src/routes/model.rs2
-rw-r--r--src/routes/user.rs50
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>,