User listing, deleting and updating
This commit is contained in:
@@ -6,7 +6,7 @@ use argon2::{
|
|||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
Extension, Json,
|
Extension, Json,
|
||||||
extract::State,
|
extract::{Path, State},
|
||||||
http::{Response, StatusCode, header},
|
http::{Response, StatusCode, header},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
};
|
};
|
||||||
@@ -18,7 +18,7 @@ use serde_json::json;
|
|||||||
use crate::{
|
use crate::{
|
||||||
AppState,
|
AppState,
|
||||||
cookie::jwt::encode_token,
|
cookie::jwt::encode_token,
|
||||||
models::{FilteredUser, LoginModel, LoginScheme, UserCreateScheme},
|
models::{FilteredUser, LoginScheme, User, UserCreateScheme, UserUpdateScheme},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn create_user(
|
pub async fn create_user(
|
||||||
@@ -88,7 +88,7 @@ pub async fn login(
|
|||||||
State(data): State<Arc<AppState>>,
|
State(data): State<Arc<AppState>>,
|
||||||
Json(request): Json<LoginScheme>,
|
Json(request): Json<LoginScheme>,
|
||||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
) -> 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)
|
.bind(request.username)
|
||||||
.fetch_optional(&data.db)
|
.fetch_optional(&data.db)
|
||||||
.await
|
.await
|
||||||
@@ -131,7 +131,7 @@ pub async fn login(
|
|||||||
.http_only(true);
|
.http_only(true);
|
||||||
|
|
||||||
let mut response = Response::new(
|
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
|
response
|
||||||
.headers_mut()
|
.headers_mut()
|
||||||
@@ -156,24 +156,158 @@ pub async fn logout() -> Result<impl IntoResponse, (StatusCode, Json<serde_json:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_current_user(
|
pub async fn get_current_user(
|
||||||
Extension(state): Extension<LoginModel>,
|
Extension(state): Extension<User>,
|
||||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||||
let response = json!({
|
let response = json!({
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"data": json!({
|
"data": json!({
|
||||||
"first_name": filter_users(&state).first_name,
|
"first_name": filter_user(&state).first_name,
|
||||||
"last_name": filter_users(&state).last_name
|
"last_name": filter_user(&state).last_name
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Json(response))
|
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 {
|
FilteredUser {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
first_name: user.first_name.clone(),
|
first_name: user.first_name.clone(),
|
||||||
last_name: user.last_name.clone(),
|
last_name: user.last_name.clone(),
|
||||||
|
username: user.username.clone(),
|
||||||
is_admin: user.is_admin.clone(),
|
is_admin: user.is_admin.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ pub struct TicketResponse {
|
|||||||
pub user_id: i16,
|
pub user_id: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone, sqlx::FromRow)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: i16,
|
pub id: i16,
|
||||||
pub last_name: String,
|
pub last_name: String,
|
||||||
pub first_name: String,
|
pub first_name: String,
|
||||||
|
pub username: String,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
pub pwd: String,
|
pub pwd: String,
|
||||||
}
|
}
|
||||||
@@ -49,6 +50,16 @@ pub struct TicketUpdateScheme {
|
|||||||
pub status: String,
|
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)]
|
#[derive(Deserialize, Serialize, Debug, sqlx::FromRow)]
|
||||||
pub struct UserCreateScheme {
|
pub struct UserCreateScheme {
|
||||||
pub first_name: String,
|
pub first_name: String,
|
||||||
@@ -64,20 +75,12 @@ pub struct LoginScheme {
|
|||||||
pub pwd: String,
|
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)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct FilteredUser {
|
pub struct FilteredUser {
|
||||||
pub id: i16,
|
pub id: i16,
|
||||||
pub first_name: String,
|
pub first_name: String,
|
||||||
pub last_name: String,
|
pub last_name: String,
|
||||||
|
pub username: String,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use axum::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
AppState,
|
AppState,
|
||||||
handlers::{
|
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},
|
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/register", post(create_user))
|
||||||
.route("/api/login", post(login))
|
.route("/api/login", post(login))
|
||||||
.route("/api/logout", get(logout))
|
.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)
|
.with_state(state)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user