use std::fmt; #[derive(Debug)] pub enum AdminError { BadRequest(String), InvalidCredentials, Unauthorized, Forbidden, NotFound, Conflict(String), RateLimited(String), Internal(String), } impl fmt::Display for AdminError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::BadRequest(message) => write!(f, "{message}"), Self::InvalidCredentials => write!(f, "invalid credentials"), Self::Unauthorized => write!(f, "unauthorized"), Self::Forbidden => write!(f, "forbidden"), Self::NotFound => write!(f, "not found"), Self::Conflict(message) => write!(f, "{message}"), Self::RateLimited(message) => write!(f, "{message}"), Self::Internal(message) => write!(f, "{message}"), } } } impl std::error::Error for AdminError {} export::db_error_conversions!(AdminError); export::storage_error_conversion!(AdminError); export::redis_error_conversion!(AdminError); export::export_error_conversion!(AdminError); export::json_payload_conversion!(AdminError); impl From for AdminError { fn from(error: auth::errors::AuthError) -> Self { match error { auth::errors::AuthError::BadRequest(message) => Self::BadRequest(message), auth::errors::AuthError::InvalidCredentials => Self::InvalidCredentials, auth::errors::AuthError::Unauthorized => Self::Unauthorized, auth::errors::AuthError::Forbidden => Self::Forbidden, auth::errors::AuthError::NotFound => Self::NotFound, auth::errors::AuthError::Conflict(message) => Self::Conflict(message), auth::errors::AuthError::RateLimited => { Self::RateLimited("rate limit exceeded".to_string()) } auth::errors::AuthError::PowFailed => { Self::BadRequest("proof of work failed".to_string()) } auth::errors::AuthError::TwoFactorExpired => { Self::BadRequest("two-factor challenge expired".to_string()) } auth::errors::AuthError::TwoFactorCodeInvalid => { Self::BadRequest("invalid two-factor code".to_string()) } auth::errors::AuthError::RecoveryCodeInvalid => { Self::BadRequest("invalid or used recovery code".to_string()) } auth::errors::AuthError::VerificationCodeExpired => { Self::BadRequest("verification code expired".to_string()) } auth::errors::AuthError::VerificationCodeInvalid => { Self::BadRequest("invalid verification code".to_string()) } auth::errors::AuthError::ResetTokenInvalid => { Self::BadRequest("invalid or expired reset token".to_string()) } auth::errors::AuthError::Internal(message) => Self::Internal(message), } } } #[derive(Debug)] pub struct AdminApiErrorPayload { pub message: String, } #[derive(Debug)] pub enum AdminApiError { BadRequest(AdminApiErrorPayload), Unauthorized(AdminApiErrorPayload), Forbidden(AdminApiErrorPayload), NotFound(AdminApiErrorPayload), Conflict(AdminApiErrorPayload), RateLimited(AdminApiErrorPayload), Internal(AdminApiErrorPayload), } impl AdminApiError { fn payload(message: impl Into) -> AdminApiErrorPayload { AdminApiErrorPayload { message: message.into(), } } pub fn bad_request(message: impl AsRef) -> Self { Self::BadRequest(Self::payload(message.as_ref())) } pub fn unauthorized() -> Self { Self::Unauthorized(Self::payload("unauthorized")) } pub fn forbidden() -> Self { Self::Forbidden(Self::payload("forbidden")) } pub fn not_found(message: &'static str) -> Self { Self::NotFound(Self::payload(message)) } pub fn conflict(message: impl AsRef) -> Self { Self::Conflict(Self::payload(message.as_ref())) } pub fn rate_limited(message: impl Into) -> Self { Self::RateLimited(Self::payload(message)) } pub fn internal(message: impl fmt::Display) -> Self { tracing::warn!(error = %message, "admin handler failed"); Self::Internal(Self::payload("internal server error")) } } impl From for AdminApiError { fn from(error: AdminError) -> Self { match error { AdminError::BadRequest(message) => Self::bad_request(message), AdminError::InvalidCredentials => { Self::Unauthorized(Self::payload("invalid username or password")) } AdminError::Unauthorized => Self::unauthorized(), AdminError::Forbidden => Self::forbidden(), AdminError::NotFound => Self::not_found("admin resource not found"), AdminError::Conflict(message) => Self::conflict(message), AdminError::RateLimited(message) => Self::rate_limited(message), AdminError::Internal(message) => Self::internal(message), } } }