This commit is contained in:
2026-05-25 17:43:07 +02:00
parent d1576ae8fa
commit e2cfb61caa
11 changed files with 56 additions and 85 deletions

View File

@@ -43,7 +43,7 @@ pub fn encode_token(header: &Header, id: String, key: &EncodingKey) -> String {
expires: expires as usize,
};
let token = encode(header, &claims, key);
return token.expect("token return failed");
token.expect("token return failed")
}
/// Decodes and validates a JSON Web Token (JWT).
@@ -77,5 +77,5 @@ pub fn decode_token(token: String, key: &DecodingKey) -> Result<Claims, (StatusC
(StatusCode::UNAUTHORIZED, Json(error))
})?
.claims;
return Ok(claims);
Ok(claims)
}

View File

@@ -48,13 +48,7 @@ pub async fn validate_token(
.headers()
.get(header::AUTHORIZATION)
.and_then(|header| header.to_str().ok())
.and_then(|value| {
if value.starts_with("Bearer ") {
Some(value[7..].to_owned())
} else {
None
}
})
.and_then(|value| value.strip_prefix("Bearer ").map(|s| s.to_owned()))
});
let token = token.ok_or_else(|| {
@@ -77,7 +71,7 @@ pub async fn validate_token(
(status, Json(error))
})?;
let uuid = (&claims.sub).parse::<i16>().map_err(|_| {
let uuid = claims.sub.parse::<i16>().map_err(|_| {
let error = json!({
"status": "error",
"message": "Invalid user id"
@@ -143,13 +137,7 @@ pub async fn validate_admin(
.headers()
.get(header::AUTHORIZATION)
.and_then(|header| header.to_str().ok())
.and_then(|value| {
if value.starts_with("Bearer ") {
Some(value[7..].to_owned())
} else {
None
}
})
.and_then(|value| value.strip_prefix("Bearer ").map(|s| s.to_owned()))
});
let token = token.ok_or_else(|| {
@@ -172,7 +160,7 @@ pub async fn validate_admin(
(status, Json(error))
})?;
let uuid = (&claims.sub).parse::<i16>().map_err(|_| {
let uuid = claims.sub.parse::<i16>().map_err(|_| {
let error = json!({
"status": "error",
"message": "Invalid user id"

View File

@@ -60,7 +60,7 @@ pub async fn create_user(
)
})?;
if let Some(_) = exist_check {
if exist_check.is_some() {
return Err((
StatusCode::BAD_REQUEST,
Json(json!({"status": "error", "message": "user already exists"})),
@@ -90,10 +90,10 @@ pub async fn create_user(
})?;
if user.rows_affected() < 1 {
return Err((
Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": "Error creating user"})),
));
))
} else {
Ok(Json(json!({"status": "success", "result": "User created"})))
}
@@ -151,7 +151,7 @@ pub async fn login(
let pwd_hash = PasswordHash::new(&user.pwd);
let valid_pwd = Argon2::default()
.verify_password(&request.pwd.as_bytes(), &pwd_hash.unwrap())
.verify_password(request.pwd.as_bytes(), &pwd_hash.unwrap())
.is_ok();
if !valid_pwd {
@@ -340,7 +340,7 @@ pub async fn get_users(
let response = users
.iter()
.map(|user| filter_user(&user))
.map(filter_user)
.collect::<Vec<FilteredUser>>();
let json_respnse = json!(response);
Ok(Json(json_respnse))
@@ -372,22 +372,22 @@ pub async fn get_user_by_id(
match query {
Ok(user) => {
let response = serde_json::json!(filter_user(&user));
return Ok(Json(response));
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((StatusCode::NOT_FOUND, Json(error_response)))
}
Err(e) => {
return Err((
Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": format!("{:?}", e)})),
));
))
}
};
}
}
/// Updates an existing user's information.
@@ -409,7 +409,7 @@ pub async fn get_user_by_id(
/// # Security Note
/// - Passwords are hashed using Argon2 before storage.
/// - This endpoint requires admin privileges (enforced by middleware via
/// [`validate_admin`](crate::cookie::validation::validate_admin)).
/// [`validate_admin`](crate::cookie::validation::validate_admin)).
pub async fn update_user(
Path(id): Path<i32>,
State(data): State<Arc<AppState>>,
@@ -572,10 +572,10 @@ pub async fn setup_initial_admin(
})?;
if user.rows_affected() < 1 {
return Err((
Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": "Error creating admin user"})),
));
))
} else {
Ok(Json(
json!({"status": "success", "result": "Admin user created"}),
@@ -616,6 +616,6 @@ pub fn filter_user(user: &User) -> FilteredUser {
first_name: user.first_name.clone(),
last_name: user.last_name.clone(),
username: user.username.clone(),
is_admin: user.is_admin.clone(),
is_admin: user.is_admin,
}
}

View File

@@ -230,22 +230,22 @@ pub async fn get_ticket_by_id(
user_last_name: row.get("last_name"),
};
let response = serde_json::json!(ticket_response);
return Ok(Json(response));
Ok(Json(response))
}
Err(sqlx::Error::RowNotFound) => {
let error_response = serde_json::json!({
"status": "fail",
"message": format!("Ticket with ID {} not found", id)
});
return Err((StatusCode::NOT_FOUND, Json(error_response)));
Err((StatusCode::NOT_FOUND, Json(error_response)))
}
Err(e) => {
return Err((
Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": format!("{:?}", e)})),
));
))
}
};
}
}
/// Updates a ticket's status.

View File

@@ -64,7 +64,7 @@ async fn main() {
let database_url = &env.db_url;
// Establish connection pool to PostgreSQL
let pool = match PgPoolOptions::new().connect(&database_url).await {
let pool = match PgPoolOptions::new().connect(database_url).await {
Ok(pool) => {
println!("Database connection successful");
pool

View File

@@ -8,7 +8,7 @@ services:
environment:
- POSTGRES_PASSWORD=tickets
volumes:
- pg_data:/var/lib/postregsql/pg_data
- pg_data:/var/lib/postgresql/pg_data
volumes:
pg_data:

View File

@@ -67,7 +67,7 @@ pub fn initial_admin_setup() -> Html {
let username = use_state(|| "".to_string());
let pwd = use_state(|| "".to_string());
let pwd_confirm = use_state(|| "".to_string());
let error = use_state(|| String::new());
let error = use_state(String::new);
let success = use_state(|| false);
let loading = use_state(|| false);
let admin_check_done = use_state(|| false);

View File

@@ -133,7 +133,7 @@ pub fn sidebar_state_provider(props: &SidebarProps) -> Html {
Callback::from(move |v: bool| {
state.set(SidebarExpandState {
ticket_open: v,
users_open: (*state).users_open,
users_open: state.users_open,
})
})
};
@@ -141,10 +141,10 @@ pub fn sidebar_state_provider(props: &SidebarProps) -> Html {
let toggle_tickets = {
let state = state.clone();
Callback::from(move |_| {
let current = (*state).ticket_open;
let current = state.ticket_open;
state.set(SidebarExpandState {
ticket_open: !current,
users_open: (*state).users_open,
users_open: state.users_open,
});
})
};
@@ -153,7 +153,7 @@ pub fn sidebar_state_provider(props: &SidebarProps) -> Html {
let state = state.clone();
Callback::from(move |v: bool| {
state.set(SidebarExpandState {
ticket_open: (*state).ticket_open,
ticket_open: state.ticket_open,
users_open: v,
})
})
@@ -162,9 +162,9 @@ pub fn sidebar_state_provider(props: &SidebarProps) -> Html {
let toggle_users = {
let state = state.clone();
Callback::from(move |_| {
let current = (*state).users_open;
let current = state.users_open;
state.set(SidebarExpandState {
ticket_open: (*state).ticket_open,
ticket_open: state.ticket_open,
users_open: !current,
});
})

View File

@@ -265,10 +265,7 @@ pub fn submit_ticket_component() -> Html {
Err(_) => None,
}
} else {
match raw_trim.parse::<i16>() {
Ok(n) => Some(n),
Err(_) => None,
}
raw_trim.parse::<i16>().ok()
}
};
@@ -421,7 +418,6 @@ pub fn ticket_by_id_component(props: &TicketProps) -> Html {
}
let onsubmit = {
let status = status.clone();
let id = id.clone();
let error = error.clone();
Callback::from(move |e: SubmitEvent| {
@@ -437,7 +433,6 @@ pub fn ticket_by_id_component(props: &TicketProps) -> Html {
.unwrap_or_else(|| (*status).clone());
status.set(new_status.clone());
let id = id.clone();
let error = error.clone();
spawn_local(async move {
@@ -464,7 +459,6 @@ pub fn ticket_by_id_component(props: &TicketProps) -> Html {
let deleting = deleting.clone();
let delete_error = delete_error.clone();
let ticket_state = ticket.clone();
let id = id;
Callback::from(move |e: MouseEvent| {
e.prevent_default();
@@ -589,7 +583,7 @@ pub fn ticket_by_id_component(props: &TicketProps) -> Html {
/// ```
#[component(AllTickets)]
pub fn all_tickets_component() -> Html {
let tickets = use_state(|| Vec::<Ticket>::new());
let tickets = use_state(Vec::<Ticket>::new);
let error = use_state(|| None::<String>);
let loading = use_state(|| false);
let user = use_state(|| ActiveUser {
@@ -605,7 +599,7 @@ pub fn all_tickets_component() -> Html {
use_effect_with((), move |_| {
loading.set(true);
spawn_local(async move {
let url = format!("/api/tickets");
let url = "/api/tickets".to_string();
match Request::get(&url).send().await {
Ok(response) if response.status() == 200 => {
match response.json::<Vec<Ticket>>().await {
@@ -637,9 +631,8 @@ pub fn all_tickets_component() -> Html {
.credentials(web_sys::RequestCredentials::Include)
.send()
.await
{
if response.status() == 200 {
if let Ok(json) = response.json::<serde_json::Value>().await {
&& response.status() == 200
&& let Ok(json) = response.json::<serde_json::Value>().await {
let id = json
.get("data")
.and_then(|d| d.get("id"))
@@ -652,8 +645,6 @@ pub fn all_tickets_component() -> Html {
.unwrap_or(false);
user.set(ActiveUser { id, is_admin });
}
}
}
});
|| ()
});
@@ -729,7 +720,7 @@ pub fn all_tickets_component() -> Html {
/// ```
#[component(ArchivedTickets)]
pub fn archived_tickets_component() -> Html {
let tickets = use_state(|| Vec::<Ticket>::new());
let tickets = use_state(Vec::<Ticket>::new);
let error = use_state(|| None::<String>);
let loading = use_state(|| false);
let user = use_state(|| ActiveUser {
@@ -745,7 +736,7 @@ pub fn archived_tickets_component() -> Html {
use_effect_with((), move |_| {
loading.set(true);
spawn_local(async move {
let url = format!("/api/tickets");
let url = "/api/tickets".to_string();
match Request::get(&url).send().await {
Ok(response) if response.status() == 200 => {
match response.json::<Vec<Ticket>>().await {
@@ -777,9 +768,8 @@ pub fn archived_tickets_component() -> Html {
.credentials(web_sys::RequestCredentials::Include)
.send()
.await
{
if response.status() == 200 {
if let Ok(json) = response.json::<serde_json::Value>().await {
&& response.status() == 200
&& let Ok(json) = response.json::<serde_json::Value>().await {
let id = json
.get("data")
.and_then(|d| d.get("id"))
@@ -792,8 +782,6 @@ pub fn archived_tickets_component() -> Html {
.unwrap_or(false);
user.set(ActiveUser { id, is_admin });
}
}
}
});
|| ()
});

View File

@@ -287,7 +287,7 @@ pub fn login_component() -> Html {
let username = use_state(|| "".to_string());
let pwd = use_state(|| "".to_string());
let loading = use_state(|| false);
let error = use_state(|| String::new());
let error = use_state(String::new);
let success = use_state(|| false);
let navigator = use_navigator().unwrap();
@@ -401,7 +401,7 @@ pub fn login_component() -> Html {
/// ```
#[component(AllUsers)]
pub fn all_users_component() -> Html {
let users = use_state(|| Vec::<FilteredUser>::new());
let users = use_state(Vec::<FilteredUser>::new);
let error = use_state(|| None::<String>);
let loading = use_state(|| false);
@@ -413,7 +413,7 @@ pub fn all_users_component() -> Html {
use_effect_with((), move |_| {
loading.set(true);
spawn_local(async move {
let url = format!("/api/users");
let url = "/api/users".to_string();
match Request::get(&url).send().await {
Ok(response) if response.status() == 200 => {
match response.json::<Vec<FilteredUser>>().await {
@@ -562,7 +562,7 @@ pub fn user_by_id_component(props: &UserProps) -> Html {
let last_name = use_state(|| "".to_string());
let username = use_state(|| "".to_string());
let make_admin = use_state(|| false);
let new_pwd = use_state(|| String::new());
let new_pwd = use_state(String::new);
let saving = use_state(|| false);
let save_error = use_state(|| None::<String>);
let save_success = use_state(|| false);
@@ -612,11 +612,10 @@ pub fn user_by_id_component(props: &UserProps) -> Html {
let save_error = save_error.clone();
let save_success = save_success.clone();
let user_state = user_state.clone();
let id = id;
spawn_local(async move {
let payload = UserUpdateScheme {
id: id,
id,
first_name,
last_name,
username,
@@ -656,7 +655,6 @@ pub fn user_by_id_component(props: &UserProps) -> Html {
let deleting = deleting.clone();
let delete_error = delete_error.clone();
let user_state = user.clone(); // or ticket
let id = id;
Callback::from(move |e: MouseEvent| {
e.prevent_default();

View File

@@ -156,7 +156,7 @@ fn day_counts(partials: &[TicketPartial]) -> [usize; 7] {
chrono::Weekday::Sun => 6,
};
occ[idx] += 1;
current = current + chrono::Duration::days(1);
current += chrono::Duration::days(1);
}
occ
@@ -236,7 +236,7 @@ pub fn diagnostics_component() -> Html {
/// ```
#[component(TicketCount)]
pub fn ticket_count_component() -> Html {
let tickets = use_state(|| Vec::<Ticket>::new());
let tickets = use_state(Vec::<Ticket>::new);
let error = use_state(|| None::<String>);
let loading = use_state(|| false);
let user = use_state(|| ActiveUser {
@@ -252,7 +252,7 @@ pub fn ticket_count_component() -> Html {
use_effect_with((), move |_| {
loading.set(true);
spawn_local(async move {
let url = format!("/api/tickets");
let url = "/api/tickets".to_string();
match Request::get(&url).send().await {
Ok(response) if response.status() == 200 => {
match response.json::<Vec<Ticket>>().await {
@@ -284,9 +284,8 @@ pub fn ticket_count_component() -> Html {
.credentials(web_sys::RequestCredentials::Include)
.send()
.await
{
if response.status() == 200 {
if let Ok(json) = response.json::<serde_json::Value>().await {
&& response.status() == 200
&& let Ok(json) = response.json::<serde_json::Value>().await {
let id = json
.get("data")
.and_then(|d| d.get("id"))
@@ -299,8 +298,6 @@ pub fn ticket_count_component() -> Html {
.unwrap_or(false);
user.set(ActiveUser { id, is_admin });
}
}
}
});
|| ()
});
@@ -316,7 +313,7 @@ pub fn ticket_count_component() -> Html {
.iter()
.filter(|t| {
status_conditions(t)
&& (user.is_admin || user.id.map_or(false, |uid| t.user_id == uid))
&& (user.is_admin || (user.id == Some(t.user_id)))
})
.count();
html! {
@@ -357,8 +354,8 @@ pub fn ticket_count_component() -> Html {
/// ```
#[component(SubmitStats)]
pub fn submit_stats_component() -> Html {
let tickets = use_state(|| Vec::<TicketPartial>::new());
let users = use_state(|| Vec::<UserPartial>::new());
let tickets = use_state(Vec::<TicketPartial>::new);
let users = use_state(Vec::<UserPartial>::new);
let error = use_state(|| None::<String>);
let loading = use_state(|| false);