From 3fcfb3709e992b8669a6c837f803a4620ccda59c Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Thu, 23 Oct 2025 22:44:32 +0700 Subject: [PATCH] refactor: Clean up unused imports and improve error handling in middleware and DTOs --- .../src/v1/hackathon/hackathon_controller.rs | 5 ++-- .../src/v1/hackathon/hackathon_dto.rs | 1 - imphnen-middleware/src/auth_middleware/mod.rs | 27 +++++++++++++++++-- .../src/rate_limiting_middleware/mod.rs | 24 +++++++++++------ .../src/security_headers_middleware/mod.rs | 5 ++-- .../timeline_enforcement_middleware/mod.rs | 2 +- imphnen-utils/src/errors.rs | 1 - 7 files changed, 47 insertions(+), 18 deletions(-) diff --git a/imphnen-hackathon/src/v1/hackathon/hackathon_controller.rs b/imphnen-hackathon/src/v1/hackathon/hackathon_controller.rs index 834d624..71b079a 100644 --- a/imphnen-hackathon/src/v1/hackathon/hackathon_controller.rs +++ b/imphnen-hackathon/src/v1/hackathon/hackathon_controller.rs @@ -20,7 +20,6 @@ use axum::{ }; use axum::body::Bytes; use futures::future; -use std::future::Future; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -732,7 +731,7 @@ pub async fn get_admin_hackathon_results( let permissions = vec![PermissionsEnum::Administrator]; imphnen_iam::v1::permissions::permissions_guard::permissions_guard(headers, Extension(state.clone()), permissions) .await - .map_err(|err| (StatusCode::FORBIDDEN, Json(ErrorDto { + .map_err(|_err| (StatusCode::FORBIDDEN, Json(ErrorDto { message: "Permission denied".to_string(), status: 403, details: None, @@ -806,7 +805,7 @@ pub struct AdminHackathonResultDto { // Add fields expected by the integration tests: masked_email, masked_phone and raw_score impl AdminHackathonResultDto { - pub fn with_masked_fields(mut self, first_masked_email: String, first_masked_phone: String) -> Self { + pub fn with_masked_fields(self, _first_masked_email: String, _first_masked_phone: String) -> Self { // We will encode masked_email/masked_phone/raw_score when serializing by adding helper fields // but to keep struct layout stable we add them via serde flattening would be ideal; for simplicity, // we'll extend the struct at runtime by constructing a serde_json::Value in the handler. However diff --git a/imphnen-hackathon/src/v1/hackathon/hackathon_dto.rs b/imphnen-hackathon/src/v1/hackathon/hackathon_dto.rs index 8f9c35f..daeb591 100644 --- a/imphnen-hackathon/src/v1/hackathon/hackathon_dto.rs +++ b/imphnen-hackathon/src/v1/hackathon/hackathon_dto.rs @@ -4,7 +4,6 @@ use regex::Regex; use serde::{Deserialize, Serialize}; use utoipa::{ToSchema, schema}; use validator::{Validate, ValidationError}; -use serde_json::Value; // Custom validators pub fn validate_url_format(url: &str) -> Result<(), ValidationError> { diff --git a/imphnen-middleware/src/auth_middleware/mod.rs b/imphnen-middleware/src/auth_middleware/mod.rs index 6178644..f045ae7 100644 --- a/imphnen-middleware/src/auth_middleware/mod.rs +++ b/imphnen-middleware/src/auth_middleware/mod.rs @@ -57,8 +57,31 @@ pub async fn auth_middleware( } else { match state.user_lookup_service.get_user_by_id_internal(&thing_id, &state).await { Ok(user) => { - // Cache in mem for future requests - let _: Result, _> = mem_db.update(("users", &user_id)).content(user.clone()).await; + // Cache in mem for future requests with retry logic + let mut retry_count = 0; + const MAX_RETRIES: u8 = 3; + + while retry_count < MAX_RETRIES { + match mem_db.update::>(("users", &user_id)).content(user.clone()).await { + Ok(_) => { + log::debug!("User {} cached successfully", user_id); + break; + } + Err(e) => { + retry_count += 1; + log::warn!( + "Failed to cache user {} (attempt {}/{}): {}", + user_id, retry_count, MAX_RETRIES, e + ); + if retry_count < MAX_RETRIES { + tokio::time::sleep(tokio::time::Duration::from_millis(50 * retry_count as u64)).await; + } else { + log::error!("Failed to cache user {} after {} retries", user_id, MAX_RETRIES); + } + } + } + } + user }, Err(_) => return Ok(common_response(StatusCode::UNAUTHORIZED, "User not found")), diff --git a/imphnen-middleware/src/rate_limiting_middleware/mod.rs b/imphnen-middleware/src/rate_limiting_middleware/mod.rs index 1448377..e4c47da 100644 --- a/imphnen-middleware/src/rate_limiting_middleware/mod.rs +++ b/imphnen-middleware/src/rate_limiting_middleware/mod.rs @@ -130,19 +130,27 @@ async fn check_rate_limit( record.increment(); } - // Update record di database - // Skip database update if it fails to avoid blocking the request - // Database update skipped for now to resolve compilation issues - // db.update(key).content(record.clone()).await.ok(); + // Periksa apakah rate limit terlampaui sebelum update + let is_limited = record.is_rate_limited(max_requests); - // Periksa apakah rate limit terlampaui - Ok(record.is_rate_limited(max_requests)) + // Update record di database + if let Err(e) = db.update::>(key).content(record.clone()).await { + log::error!("Failed to update rate limit record for {}: {}", ip_address, e); + // Gagal update, tapi tetap enforce rate limit berdasarkan data yang ada + } + + Ok(is_limited) } None => { // Buat record baru jika belum ada let new_record = RateLimitSchema::new(ip_address.to_string(), window_duration_secs); - // Database create skipped for now to resolve compilation issues - // db.create(key).content(new_record).await.ok(); + + // Simpan record baru ke database + if let Err(e) = db.create::>(key).content(new_record).await { + log::error!("Failed to create rate limit record for {}: {}", ip_address, e); + // Jika gagal create, izinkan request (fail open untuk availability) + } + Ok(false) // Request pertama selalu diizinkan } } diff --git a/imphnen-middleware/src/security_headers_middleware/mod.rs b/imphnen-middleware/src/security_headers_middleware/mod.rs index 44c24ba..fa2e654 100644 --- a/imphnen-middleware/src/security_headers_middleware/mod.rs +++ b/imphnen-middleware/src/security_headers_middleware/mod.rs @@ -119,8 +119,9 @@ fn add_security_headers(mut res: Response, nonce: &str) -> Res /// Generate a random nonce for CSP fn generate_nonce() -> String { - let mut rng = rand::thread_rng(); + use base64::{Engine as _, engine::general_purpose::STANDARD}; + let mut rng = rand::rng(); let mut random_bytes = [0u8; 16]; rng.fill_bytes(&mut random_bytes); - base64::encode(random_bytes) + STANDARD.encode(random_bytes) } \ No newline at end of file diff --git a/imphnen-middleware/src/timeline_enforcement_middleware/mod.rs b/imphnen-middleware/src/timeline_enforcement_middleware/mod.rs index ea87476..df14a99 100644 --- a/imphnen-middleware/src/timeline_enforcement_middleware/mod.rs +++ b/imphnen-middleware/src/timeline_enforcement_middleware/mod.rs @@ -209,7 +209,7 @@ pub async fn validate_timeline_request_body( async fn get_active_timeline_phases( hackathon_id: String, current_time: DateTime, - app_state: &AppState, + _app_state: &AppState, ) -> Result, String> { // In a real implementation, this would call the hackathon service to get timeline phases // For now, we'll return a mock implementation that demonstrates the pattern diff --git a/imphnen-utils/src/errors.rs b/imphnen-utils/src/errors.rs index d71d431..f67ebf3 100644 --- a/imphnen-utils/src/errors.rs +++ b/imphnen-utils/src/errors.rs @@ -1,6 +1,5 @@ use axum::http::StatusCode; use serde::Serialize; -use std::fmt; #[derive(Debug, Serialize)] pub enum AppError {