this repo has no description
1use axum::{ 2 Json, 3 http::StatusCode, 4 response::{IntoResponse, Response}, 5}; 6use serde::Serialize; 7 8#[derive(Debug, Serialize)] 9struct ErrorBody { 10 error: &'static str, 11 #[serde(skip_serializing_if = "Option::is_none")] 12 message: Option<String>, 13} 14 15#[derive(Debug)] 16pub enum ApiError { 17 InternalError, 18 AuthenticationRequired, 19 AuthenticationFailed, 20 AuthenticationFailedMsg(String), 21 InvalidRequest(String), 22 InvalidToken, 23 ExpiredToken, 24 ExpiredTokenMsg(String), 25 TokenRequired, 26 AccountDeactivated, 27 AccountTakedown, 28 AccountNotFound, 29 RepoNotFound, 30 RepoNotFoundMsg(String), 31 RecordNotFound, 32 BlobNotFound, 33 InvalidHandle, 34 HandleNotAvailable, 35 HandleTaken, 36 InvalidEmail, 37 EmailTaken, 38 InvalidInviteCode, 39 DuplicateCreate, 40 DuplicateAppPassword, 41 AppPasswordNotFound, 42 InvalidSwap, 43 Forbidden, 44 InvitesDisabled, 45 DatabaseError, 46 UpstreamFailure, 47 UpstreamTimeout, 48 UpstreamUnavailable(String), 49 UpstreamError { status: u16, error: Option<String>, message: Option<String> }, 50} 51 52impl ApiError { 53 fn status_code(&self) -> StatusCode { 54 match self { 55 Self::InternalError | Self::DatabaseError => StatusCode::INTERNAL_SERVER_ERROR, 56 Self::UpstreamFailure | Self::UpstreamUnavailable(_) => StatusCode::BAD_GATEWAY, 57 Self::UpstreamTimeout => StatusCode::GATEWAY_TIMEOUT, 58 Self::UpstreamError { status, .. } => { 59 StatusCode::from_u16(*status).unwrap_or(StatusCode::BAD_GATEWAY) 60 } 61 Self::AuthenticationRequired 62 | Self::AuthenticationFailed 63 | Self::AuthenticationFailedMsg(_) 64 | Self::InvalidToken 65 | Self::ExpiredToken 66 | Self::ExpiredTokenMsg(_) 67 | Self::TokenRequired 68 | Self::AccountDeactivated 69 | Self::AccountTakedown => StatusCode::UNAUTHORIZED, 70 Self::Forbidden | Self::InvitesDisabled => StatusCode::FORBIDDEN, 71 Self::AccountNotFound 72 | Self::RepoNotFound 73 | Self::RepoNotFoundMsg(_) 74 | Self::RecordNotFound 75 | Self::BlobNotFound 76 | Self::AppPasswordNotFound => StatusCode::NOT_FOUND, 77 Self::InvalidRequest(_) 78 | Self::InvalidHandle 79 | Self::HandleNotAvailable 80 | Self::HandleTaken 81 | Self::InvalidEmail 82 | Self::EmailTaken 83 | Self::InvalidInviteCode 84 | Self::DuplicateCreate 85 | Self::DuplicateAppPassword 86 | Self::InvalidSwap => StatusCode::BAD_REQUEST, 87 } 88 } 89 90 fn error_name(&self) -> &'static str { 91 match self { 92 Self::InternalError | Self::DatabaseError => "InternalError", 93 Self::UpstreamFailure | Self::UpstreamUnavailable(_) => "UpstreamFailure", 94 Self::UpstreamTimeout => "UpstreamTimeout", 95 Self::UpstreamError { error, .. } => { 96 if let Some(e) = error { 97 return Box::leak(e.clone().into_boxed_str()); 98 } 99 "UpstreamError" 100 } 101 Self::AuthenticationRequired => "AuthenticationRequired", 102 Self::AuthenticationFailed | Self::AuthenticationFailedMsg(_) => "AuthenticationFailed", 103 Self::InvalidToken => "InvalidToken", 104 Self::ExpiredToken | Self::ExpiredTokenMsg(_) => "ExpiredToken", 105 Self::TokenRequired => "TokenRequired", 106 Self::AccountDeactivated => "AccountDeactivated", 107 Self::AccountTakedown => "AccountTakedown", 108 Self::Forbidden => "Forbidden", 109 Self::InvitesDisabled => "InvitesDisabled", 110 Self::AccountNotFound => "AccountNotFound", 111 Self::RepoNotFound | Self::RepoNotFoundMsg(_) => "RepoNotFound", 112 Self::RecordNotFound => "RecordNotFound", 113 Self::BlobNotFound => "BlobNotFound", 114 Self::AppPasswordNotFound => "AppPasswordNotFound", 115 Self::InvalidRequest(_) => "InvalidRequest", 116 Self::InvalidHandle => "InvalidHandle", 117 Self::HandleNotAvailable => "HandleNotAvailable", 118 Self::HandleTaken => "HandleTaken", 119 Self::InvalidEmail => "InvalidEmail", 120 Self::EmailTaken => "EmailTaken", 121 Self::InvalidInviteCode => "InvalidInviteCode", 122 Self::DuplicateCreate => "DuplicateCreate", 123 Self::DuplicateAppPassword => "DuplicateAppPassword", 124 Self::InvalidSwap => "InvalidSwap", 125 } 126 } 127 128 fn message(&self) -> Option<String> { 129 match self { 130 Self::AuthenticationFailedMsg(msg) 131 | Self::ExpiredTokenMsg(msg) 132 | Self::InvalidRequest(msg) 133 | Self::RepoNotFoundMsg(msg) 134 | Self::UpstreamUnavailable(msg) => Some(msg.clone()), 135 Self::UpstreamError { message, .. } => message.clone(), 136 Self::UpstreamTimeout => Some("Upstream service timed out".to_string()), 137 _ => None, 138 } 139 } 140 141 pub fn from_upstream_response( 142 status: u16, 143 body: &[u8], 144 ) -> Self { 145 if let Ok(parsed) = serde_json::from_slice::<serde_json::Value>(body) { 146 let error = parsed.get("error").and_then(|v| v.as_str()).map(String::from); 147 let message = parsed.get("message").and_then(|v| v.as_str()).map(String::from); 148 return Self::UpstreamError { status, error, message }; 149 } 150 Self::UpstreamError { status, error: None, message: None } 151 } 152} 153 154impl IntoResponse for ApiError { 155 fn into_response(self) -> Response { 156 let body = ErrorBody { 157 error: self.error_name(), 158 message: self.message(), 159 }; 160 (self.status_code(), Json(body)).into_response() 161 } 162} 163 164impl From<sqlx::Error> for ApiError { 165 fn from(e: sqlx::Error) -> Self { 166 tracing::error!("Database error: {:?}", e); 167 Self::DatabaseError 168 } 169} 170 171impl From<crate::auth::TokenValidationError> for ApiError { 172 fn from(e: crate::auth::TokenValidationError) -> Self { 173 match e { 174 crate::auth::TokenValidationError::AccountDeactivated => Self::AccountDeactivated, 175 crate::auth::TokenValidationError::AccountTakedown => Self::AccountTakedown, 176 crate::auth::TokenValidationError::KeyDecryptionFailed => Self::InternalError, 177 crate::auth::TokenValidationError::AuthenticationFailed => Self::AuthenticationFailed, 178 } 179 } 180} 181 182impl From<crate::util::DbLookupError> for ApiError { 183 fn from(e: crate::util::DbLookupError) -> Self { 184 match e { 185 crate::util::DbLookupError::NotFound => Self::AccountNotFound, 186 crate::util::DbLookupError::DatabaseError(db_err) => { 187 tracing::error!("Database error: {:?}", db_err); 188 Self::DatabaseError 189 } 190 } 191 } 192}