Implement rate limiting middleware for authentication endpoints, adding security headers middleware, and comprehensive error handling. Enhance validation tests for various DTOs and ensure proper functionality of gacha credits and rolls. Add unit tests for rate limiting and security headers middleware to validate behavior under different conditions.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
use axum::http::StatusCode;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum AppError {
|
||||
ValidationError(String),
|
||||
AuthenticationError(String),
|
||||
AuthorizationError(String),
|
||||
NotFoundError(String),
|
||||
ConflictError(String),
|
||||
InternalServerError(String),
|
||||
BadRequestError(String),
|
||||
ForbiddenError(String),
|
||||
PaymentRequiredError(String),
|
||||
MethodNotAllowedError(String),
|
||||
NotAcceptableError(String),
|
||||
RequestTimeoutError(String),
|
||||
TooManyRequestsError(String),
|
||||
GatewayTimeoutError(String),
|
||||
ServiceUnavailableError(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AppError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AppError::ValidationError(msg) => write!(f, "Validation error: {}", msg),
|
||||
AppError::AuthenticationError(msg) => write!(f, "Authentication failed: {}", msg),
|
||||
AppError::AuthorizationError(msg) => write!(f, "Authorization failed: {}", msg),
|
||||
AppError::NotFoundError(msg) => write!(f, "Resource not found: {}", msg),
|
||||
AppError::ConflictError(msg) => write!(f, "Conflict error: {}", msg),
|
||||
AppError::InternalServerError(msg) => write!(f, "Internal server error: {}", msg),
|
||||
AppError::BadRequestError(msg) => write!(f, "Bad request: {}", msg),
|
||||
AppError::ForbiddenError(msg) => write!(f, "Forbidden: {}", msg),
|
||||
AppError::PaymentRequiredError(msg) => write!(f, "Payment required: {}", msg),
|
||||
AppError::MethodNotAllowedError(msg) => write!(f, "Method not allowed: {}", msg),
|
||||
AppError::NotAcceptableError(msg) => write!(f, "Not acceptable: {}", msg),
|
||||
AppError::RequestTimeoutError(msg) => write!(f, "Request timeout: {}", msg),
|
||||
AppError::TooManyRequestsError(msg) => write!(f, "Too many requests: {}", msg),
|
||||
AppError::GatewayTimeoutError(msg) => write!(f, "Gateway timeout: {}", msg),
|
||||
AppError::ServiceUnavailableError(msg) => write!(f, "Service unavailable: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
pub fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
AppError::ValidationError(_) => StatusCode::BAD_REQUEST,
|
||||
AppError::AuthenticationError(_) => StatusCode::UNAUTHORIZED,
|
||||
AppError::AuthorizationError(_) => StatusCode::FORBIDDEN,
|
||||
AppError::NotFoundError(_) => StatusCode::NOT_FOUND,
|
||||
AppError::ConflictError(_) => StatusCode::CONFLICT,
|
||||
AppError::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AppError::BadRequestError(_) => StatusCode::BAD_REQUEST,
|
||||
AppError::ForbiddenError(_) => StatusCode::FORBIDDEN,
|
||||
AppError::PaymentRequiredError(_) => StatusCode::PAYMENT_REQUIRED,
|
||||
AppError::MethodNotAllowedError(_) => StatusCode::METHOD_NOT_ALLOWED,
|
||||
AppError::NotAcceptableError(_) => StatusCode::NOT_ACCEPTABLE,
|
||||
AppError::RequestTimeoutError(_) => StatusCode::REQUEST_TIMEOUT,
|
||||
AppError::TooManyRequestsError(_) => StatusCode::TOO_MANY_REQUESTS,
|
||||
AppError::GatewayTimeoutError(_) => StatusCode::GATEWAY_TIMEOUT,
|
||||
AppError::ServiceUnavailableError(_) => StatusCode::SERVICE_UNAVAILABLE,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T, E = AppError> = std::result::Result<T, E>;
|
||||
@@ -15,6 +15,7 @@ pub mod get_id;
|
||||
pub mod logger;
|
||||
pub mod make_thing;
|
||||
pub mod query_builder;
|
||||
pub mod errors;
|
||||
pub mod query_list;
|
||||
pub mod response_format;
|
||||
pub mod serde_helpers;
|
||||
@@ -38,7 +39,8 @@ pub use query_builder::{
|
||||
ListQueryBuilder,
|
||||
};
|
||||
pub use query_list::QueryListBuilder;
|
||||
pub use response_format::{common_response, success_created_response, success_list_response, success_response};
|
||||
pub use errors::AppError;
|
||||
pub use response_format::{common_response, success_created_response, success_list_response, success_response, error_response};
|
||||
pub use serde_helpers::{
|
||||
deserialize_datetime,
|
||||
option_thing_or_string,
|
||||
|
||||
@@ -12,7 +12,7 @@ use axum::{
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{ResponseListSuccessDto, ResponseSuccessDto};
|
||||
use crate::{ResponseListSuccessDto, ResponseSuccessDto, AppError};
|
||||
|
||||
pub fn success_response<T: Serialize>(params: ResponseSuccessDto<T>) -> Response {
|
||||
(
|
||||
@@ -40,14 +40,25 @@ pub fn success_list_response<T: Serialize>(
|
||||
}
|
||||
|
||||
pub fn common_response(status: StatusCode, message: &str) -> Response {
|
||||
(
|
||||
status,
|
||||
Json(json!({
|
||||
"message": message,
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response()
|
||||
(
|
||||
status,
|
||||
Json(json!({
|
||||
"message": message,
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
pub fn error_response(error: AppError) -> Response {
|
||||
(
|
||||
error.status_code(),
|
||||
Json(json!({
|
||||
"error": error.message(),
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
})),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
pub fn success_created_response<T: Serialize>(params: ResponseSuccessDto<T>) -> Response {
|
||||
|
||||
Reference in New Issue
Block a user