User listing, deleting and updating
This commit is contained in:
@@ -6,7 +6,7 @@ use argon2::{
|
||||
};
|
||||
use axum::{
|
||||
Extension, Json,
|
||||
extract::State,
|
||||
extract::{Path, State},
|
||||
http::{Response, StatusCode, header},
|
||||
response::IntoResponse,
|
||||
};
|
||||
@@ -18,7 +18,7 @@ use serde_json::json;
|
||||
use crate::{
|
||||
AppState,
|
||||
cookie::jwt::encode_token,
|
||||
models::{FilteredUser, LoginModel, LoginScheme, UserCreateScheme},
|
||||
models::{FilteredUser, LoginScheme, User, UserCreateScheme, UserUpdateScheme},
|
||||
};
|
||||
|
||||
pub async fn create_user(
|
||||
@@ -88,7 +88,7 @@ pub async fn login(
|
||||
State(data): State<Arc<AppState>>,
|
||||
Json(request): Json<LoginScheme>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
let user = sqlx::query_as::<_, LoginModel>(r#"SELECT * FROM users WHERE username = $1"#)
|
||||
let user = sqlx::query_as::<_, User>(r#"SELECT * FROM users WHERE username = $1"#)
|
||||
.bind(request.username)
|
||||
.fetch_optional(&data.db)
|
||||
.await
|
||||
@@ -131,7 +131,7 @@ pub async fn login(
|
||||
.http_only(true);
|
||||
|
||||
let mut response = Response::new(
|
||||
json!({"status": "success", "token": token, "user": filter_users(&user)}).to_string(),
|
||||
json!({"status": "success", "token": token, "user": filter_user(&user)}).to_string(),
|
||||
);
|
||||
response
|
||||
.headers_mut()
|
||||
@@ -156,24 +156,158 @@ pub async fn logout() -> Result<impl IntoResponse, (StatusCode, Json<serde_json:
|
||||
}
|
||||
|
||||
pub async fn get_current_user(
|
||||
Extension(state): Extension<LoginModel>,
|
||||
Extension(state): Extension<User>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
let response = json!({
|
||||
"status": "success",
|
||||
"data": json!({
|
||||
"first_name": filter_users(&state).first_name,
|
||||
"last_name": filter_users(&state).last_name
|
||||
"first_name": filter_user(&state).first_name,
|
||||
"last_name": filter_user(&state).last_name
|
||||
})
|
||||
});
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
fn filter_users(user: &LoginModel) -> FilteredUser {
|
||||
pub async fn delete_user(
|
||||
Path(id): Path<i32>,
|
||||
State(data): State<Arc<AppState>>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
let query = sqlx::query(r#"DELETE FROM users WHERE id = $1"#)
|
||||
.bind(id)
|
||||
.execute(&data.db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"status": "error", "message": format!("{:?}", e)})),
|
||||
)
|
||||
})?;
|
||||
|
||||
if query.rows_affected() == 0 {
|
||||
let error = json!({
|
||||
"status": "error",
|
||||
"message": format!("User with ID {} not found", id)
|
||||
});
|
||||
return Err((StatusCode::NOT_FOUND, Json(error)));
|
||||
}
|
||||
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
pub async fn get_users(
|
||||
State(data): State<Arc<AppState>>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
let users = sqlx::query_as::<_, User>(r#"SELECT * FROM users ORDER BY last_name ASC"#)
|
||||
.fetch_all(&data.db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let error = json!({
|
||||
"status": "error",
|
||||
"message": format!("{:?}", e)
|
||||
});
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, Json(error))
|
||||
})?;
|
||||
|
||||
let response = users
|
||||
.iter()
|
||||
.map(|user| filter_user(&user))
|
||||
.collect::<Vec<FilteredUser>>();
|
||||
let json_respnse = json!(response);
|
||||
Ok(Json(json_respnse))
|
||||
}
|
||||
|
||||
pub async fn get_user_by_id(
|
||||
Path(id): Path<i32>,
|
||||
State(data): State<Arc<AppState>>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
let query = sqlx::query_as::<_, User>(r#"SELECT * FROM users WHERE id = $1"#)
|
||||
.bind(id)
|
||||
.fetch_one(&data.db)
|
||||
.await;
|
||||
|
||||
match query {
|
||||
Ok(user) => {
|
||||
let response = serde_json::json!(filter_user(&user));
|
||||
return Ok(Json(response));
|
||||
}
|
||||
Err(sqlx::Error::RowNotFound) => {
|
||||
let error_response = serde_json::json!({
|
||||
"status": "fail",
|
||||
"message": format!("User with ID {} not found", id)
|
||||
});
|
||||
return Err((StatusCode::NOT_FOUND, Json(error_response)));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"status": "error", "message": format!("{:?}", e)})),
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn update_user(
|
||||
Path(id): Path<i32>,
|
||||
State(data): State<Arc<AppState>>,
|
||||
Json(body): Json<UserUpdateScheme>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
let argon = Argon2::default();
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let hashed_pwd = match argon.hash_password(body.new_pwd.clone().as_bytes(), &salt) {
|
||||
Ok(h) => h.to_string(),
|
||||
Err(e) => panic!("Error hashing {:}", e),
|
||||
};
|
||||
|
||||
let update_result = sqlx::query(r#"UPDATE users SET first_name = $1, last_name = $2, username = $3, pwd = $4, is_admin = $5 WHERE id = $6"#)
|
||||
.bind(body.first_name.to_owned())
|
||||
.bind(body.last_name.to_owned())
|
||||
.bind(body.username.to_owned())
|
||||
.bind(hashed_pwd)
|
||||
.bind(body.make_admin.to_owned())
|
||||
.bind(id)
|
||||
.execute(&data.db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"status": "error", "message": format!("{:?}", e)})),
|
||||
)
|
||||
})?;
|
||||
|
||||
if update_result.rows_affected() == 0 {
|
||||
let error_response = serde_json::json!({
|
||||
"status": "error",
|
||||
"message": format!("User with ID {} not found", id)
|
||||
});
|
||||
return Err((StatusCode::INTERNAL_SERVER_ERROR, Json(error_response)));
|
||||
}
|
||||
|
||||
let updated_user = sqlx::query_as::<_, User>(r#"SELECT * FROM users WHERE id = $1"#)
|
||||
.bind(id)
|
||||
.fetch_one(&data.db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"status": "error", "message": format!("{:?}", e)})),
|
||||
)
|
||||
})?;
|
||||
|
||||
let response = serde_json::json!({
|
||||
"user": filter_user(&updated_user),
|
||||
"status": "success"
|
||||
});
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
fn filter_user(user: &User) -> FilteredUser {
|
||||
FilteredUser {
|
||||
id: user.id,
|
||||
first_name: user.first_name.clone(),
|
||||
last_name: user.last_name.clone(),
|
||||
username: user.username.clone(),
|
||||
is_admin: user.is_admin.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,12 @@ pub struct TicketResponse {
|
||||
pub user_id: i16,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone, sqlx::FromRow)]
|
||||
pub struct User {
|
||||
pub id: i16,
|
||||
pub last_name: String,
|
||||
pub first_name: String,
|
||||
pub username: String,
|
||||
pub is_admin: bool,
|
||||
pub pwd: String,
|
||||
}
|
||||
@@ -49,6 +50,16 @@ pub struct TicketUpdateScheme {
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct UserUpdateScheme {
|
||||
pub id: i16,
|
||||
pub first_name: String,
|
||||
pub last_name: String,
|
||||
pub username: String,
|
||||
pub make_admin: bool,
|
||||
pub new_pwd: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, sqlx::FromRow)]
|
||||
pub struct UserCreateScheme {
|
||||
pub first_name: String,
|
||||
@@ -64,20 +75,12 @@ pub struct LoginScheme {
|
||||
pub pwd: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone, sqlx::FromRow)]
|
||||
pub struct LoginModel {
|
||||
pub id: i16,
|
||||
pub last_name: String,
|
||||
pub first_name: String,
|
||||
pub is_admin: bool,
|
||||
pub pwd: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct FilteredUser {
|
||||
pub id: i16,
|
||||
pub first_name: String,
|
||||
pub last_name: String,
|
||||
pub username: String,
|
||||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@ use axum::{
|
||||
use crate::{
|
||||
AppState,
|
||||
handlers::{
|
||||
auth::{create_user, get_current_user, login, logout},
|
||||
auth::{
|
||||
create_user, delete_user, get_current_user, get_user_by_id, get_users, login, logout,
|
||||
update_user,
|
||||
},
|
||||
ticket::{create_ticket, delete_ticket, edit_ticket, get_ticket_by_id, get_tickets},
|
||||
},
|
||||
};
|
||||
@@ -26,6 +29,11 @@ pub fn create_router(state: Arc<AppState>) -> Router {
|
||||
.route("/api/register", post(create_user))
|
||||
.route("/api/login", post(login))
|
||||
.route("/api/logout", get(logout))
|
||||
.route("/api/current_user", get(get_current_user))
|
||||
.route("/api/users", get(get_users))
|
||||
.route("/api/users/current", get(get_current_user))
|
||||
.route(
|
||||
"/api/users/{id}",
|
||||
get(get_user_by_id).delete(delete_user).patch(update_user),
|
||||
)
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user