Skip to content

Commit

Permalink
add handlers for updating user's information and password
Browse files Browse the repository at this point in the history
  • Loading branch information
huangcheng committed Nov 27, 2023
1 parent 815a1fb commit cc3c45b
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 6 deletions.
5 changes: 4 additions & 1 deletion src/bin/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ async fn main() -> Result<(), rocket::Error> {

let _rok = rocket::build()
.manage(state)
.mount("/api/user", routes![user::me])
.mount(
"/api/user",
routes![user::me, user::update, user::update_password],
)
.mount("/api/auth", routes![auth::login, auth::logout])
.mount("/api/categories", routes![category::all])
.mount(
Expand Down
89 changes: 87 additions & 2 deletions src/handlers/user.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use bcrypt::{hash, verify, DEFAULT_COST};
use log::error;
use rocket::State;
use sqlx::{query, query_as};

use crate::errors::ServiceError;
use crate::models;
use crate::request::user::{UpdatePassword, UpdateUser};
use crate::response;
use crate::state::AppState;
use rocket::State;
use sqlx::query_as;

pub async fn get_user_by_username(
username: &str,
Expand All @@ -21,3 +25,84 @@ pub async fn get_user_by_username(
email: user.email,
})
}

pub async fn update_user(
name: &'_ str,
user: &UpdateUser<'_>,
state: &State<AppState>,
) -> Result<(), ServiceError> {
let record = query_as::<_, models::user::User>(
"SELECT username, password, email, avatar, nickname FROM user WHERE username = ?",
)
.bind(name)
.fetch_one(&state.pool)
.await?;

if !verify(user.password, &record.password).unwrap() {
return Err(ServiceError::Unauthorized);
}

let username = match user.username {
Some(username) => String::from(username),
None => record.username,
};

let email = match user.email {
Some(email) => String::from(email),
None => record.email,
};

let avatar = match user.avatar {
Some(avatar) => Some(String::from(avatar)),
None => record.avatar,
};

let nickname = match user.nickname {
Some(nickname) => String::from(nickname),
None => record.nickname,
};

query("UPDATE user SET username = ?, email = ?, avatar = ?, nickname = ? WHERE username = ?")
.bind(&username)
.bind(&email)
.bind(&avatar)
.bind(&nickname)
.bind(name)
.execute(&state.pool)
.await?;

Ok(())
}

pub async fn update_user_password(
name: &'_ str,
user: &UpdatePassword<'_>,
state: &State<AppState>,
) -> Result<(), ServiceError> {
let record = query_as::<_, models::user::User>(
"SELECT username, password, email, avatar, nickname FROM user WHERE username = ?",
)
.bind(name)
.fetch_one(&state.pool)
.await?;

let valid = verify(user.password, &record.password).map_err(|e| {
error!("{}", e);

ServiceError::Unauthorized
})?;

if !valid {
return Err(ServiceError::Unauthorized);
}

let hashed_password = hash(user.new_password, DEFAULT_COST).unwrap();

query("UPDATE user SET password = ? WHERE username = ?")
.bind(&hashed_password)
.bind(name)
.execute(&state.pool)
.await?;

Ok(())
}
15 changes: 15 additions & 0 deletions src/request/user.rs
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct UpdateUser<'r> {
pub username: Option<&'r str>,
pub password: &'r str,
pub email: Option<&'r str>,
pub avatar: Option<&'r str>,
pub nickname: Option<&'r str>,
}

#[derive(Debug, Deserialize)]
pub struct UpdatePassword<'r> {
pub password: &'r str,
pub new_password: &'r str,
}
45 changes: 42 additions & 3 deletions src/routes/user.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use log::error;
use rocket::http::Status;
use rocket::serde::json::Json;
use rocket::{get, State};
use rocket::{get, put, State};
use std::ops::Deref;

use crate::handlers::user::get_user_by_username;
use crate::handlers::user::{get_user_by_username, update_user, update_user_password};
use crate::middlewares::JwtMiddleware;
use crate::request::user::{UpdatePassword, UpdateUser};
use crate::response::auth::Logout;
use crate::response::user::User;
use crate::state::AppState;

#[get("/")]
pub async fn me(jwt: JwtMiddleware, state: &State<AppState>) -> Result<Json<User>, Status> {
pub async fn me(state: &State<AppState>, jwt: JwtMiddleware) -> Result<Json<User>, Status> {
let username = jwt.username;

let user = get_user_by_username(&username, state).await.map_err(|e| {
Expand All @@ -20,3 +23,39 @@ pub async fn me(jwt: JwtMiddleware, state: &State<AppState>) -> Result<Json<User

Ok(Json(user))
}

#[put("/<username>", format = "json", data = "<user>")]
pub async fn update<'r>(
username: &'r str,
user: Json<UpdateUser<'r>>,
state: &State<AppState>,
_jwt: JwtMiddleware,
) -> Result<(), Status> {
update_user(username, user.deref(), state)
.await
.map_err(|e| {
error!("{}", e);

e.into()
})?;

Ok(())
}

#[put("/<username>/password", format = "json", data = "<password>")]
pub async fn update_password<'r>(
username: &'r str,
password: Json<UpdatePassword<'r>>,
state: &State<AppState>,
_jwt: JwtMiddleware,
) -> Result<Logout, Status> {
update_user_password(username, password.deref(), state)
.await
.map_err(|e| {
error!("{}", e);

e.into()
})?;

Ok(Logout)
}

0 comments on commit cc3c45b

Please sign in to comment.