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 { 50 status: u16, 51 error: Option<String>, 52 message: Option<String>, 53 }, 54} 55 56impl ApiError { 57 fn status_code(&self) -> StatusCode { 58 match self { 59 Self::InternalError | Self::DatabaseError => StatusCode::INTERNAL_SERVER_ERROR, 60 Self::UpstreamFailure | Self::UpstreamUnavailable(_) => StatusCode::BAD_GATEWAY, 61 Self::UpstreamTimeout => StatusCode::GATEWAY_TIMEOUT, 62 Self::UpstreamError { status, .. } => { 63 StatusCode::from_u16(*status).unwrap_or(StatusCode::BAD_GATEWAY) 64 } 65 Self::AuthenticationRequired 66 | Self::AuthenticationFailed 67 | Self::AuthenticationFailedMsg(_) 68 | Self::InvalidToken 69 | Self::ExpiredToken 70 | Self::ExpiredTokenMsg(_) 71 | Self::TokenRequired 72 | Self::AccountDeactivated 73 | Self::AccountTakedown => StatusCode::UNAUTHORIZED, 74 Self::Forbidden | Self::InvitesDisabled => StatusCode::FORBIDDEN, 75 Self::AccountNotFound 76 | Self::RepoNotFound 77 | Self::RepoNotFoundMsg(_) 78 | Self::RecordNotFound 79 | Self::BlobNotFound 80 | Self::AppPasswordNotFound => StatusCode::NOT_FOUND, 81 Self::InvalidRequest(_) 82 | Self::InvalidHandle 83 | Self::HandleNotAvailable 84 | Self::HandleTaken 85 | Self::InvalidEmail 86 | Self::EmailTaken 87 | Self::InvalidInviteCode 88 | Self::DuplicateCreate 89 | Self::DuplicateAppPassword 90 | Self::InvalidSwap => StatusCode::BAD_REQUEST, 91 } 92 } 93 fn error_name(&self) -> &'static str { 94 match self { 95 Self::InternalError | Self::DatabaseError => "InternalError", 96 Self::UpstreamFailure | Self::UpstreamUnavailable(_) => "UpstreamFailure", 97 Self::UpstreamTimeout => "UpstreamTimeout", 98 Self::UpstreamError { error, .. } => { 99 if let Some(e) = error { 100 return Box::leak(e.clone().into_boxed_str()); 101 } 102 "UpstreamError" 103 } 104 Self::AuthenticationRequired => "AuthenticationRequired", 105 Self::AuthenticationFailed | Self::AuthenticationFailedMsg(_) => "AuthenticationFailed", 106 Self::InvalidToken => "InvalidToken", 107 Self::ExpiredToken | Self::ExpiredTokenMsg(_) => "ExpiredToken", 108 Self::TokenRequired => "TokenRequired", 109 Self::AccountDeactivated => "AccountDeactivated", 110 Self::AccountTakedown => "AccountTakedown", 111 Self::Forbidden => "Forbidden", 112 Self::InvitesDisabled => "InvitesDisabled", 113 Self::AccountNotFound => "AccountNotFound", 114 Self::RepoNotFound | Self::RepoNotFoundMsg(_) => "RepoNotFound", 115 Self::RecordNotFound => "RecordNotFound", 116 Self::BlobNotFound => "BlobNotFound", 117 Self::AppPasswordNotFound => "AppPasswordNotFound", 118 Self::InvalidRequest(_) => "InvalidRequest", 119 Self::InvalidHandle => "InvalidHandle", 120 Self::HandleNotAvailable => "HandleNotAvailable", 121 Self::HandleTaken => "HandleTaken", 122 Self::InvalidEmail => "InvalidEmail", 123 Self::EmailTaken => "EmailTaken", 124 Self::InvalidInviteCode => "InvalidInviteCode", 125 Self::DuplicateCreate => "DuplicateCreate", 126 Self::DuplicateAppPassword => "DuplicateAppPassword", 127 Self::InvalidSwap => "InvalidSwap", 128 } 129 } 130 fn message(&self) -> Option<String> { 131 match self { 132 Self::AuthenticationFailedMsg(msg) 133 | Self::ExpiredTokenMsg(msg) 134 | Self::InvalidRequest(msg) 135 | Self::RepoNotFoundMsg(msg) 136 | Self::UpstreamUnavailable(msg) => Some(msg.clone()), 137 Self::UpstreamError { message, .. } => message.clone(), 138 Self::UpstreamTimeout => Some("Upstream service timed out".to_string()), 139 _ => None, 140 } 141 } 142 pub fn from_upstream_response(status: u16, body: &[u8]) -> Self { 143 if let Ok(parsed) = serde_json::from_slice::<serde_json::Value>(body) { 144 let error = parsed 145 .get("error") 146 .and_then(|v| v.as_str()) 147 .map(String::from); 148 let message = parsed 149 .get("message") 150 .and_then(|v| v.as_str()) 151 .map(String::from); 152 return Self::UpstreamError { 153 status, 154 error, 155 message, 156 }; 157 } 158 Self::UpstreamError { 159 status, 160 error: None, 161 message: None, 162 } 163 } 164} 165 166impl IntoResponse for ApiError { 167 fn into_response(self) -> Response { 168 let body = ErrorBody { 169 error: self.error_name(), 170 message: self.message(), 171 }; 172 (self.status_code(), Json(body)).into_response() 173 } 174} 175 176impl From<sqlx::Error> for ApiError { 177 fn from(e: sqlx::Error) -> Self { 178 tracing::error!("Database error: {:?}", e); 179 Self::DatabaseError 180 } 181} 182 183impl From<crate::auth::TokenValidationError> for ApiError { 184 fn from(e: crate::auth::TokenValidationError) -> Self { 185 match e { 186 crate::auth::TokenValidationError::AccountDeactivated => Self::AccountDeactivated, 187 crate::auth::TokenValidationError::AccountTakedown => Self::AccountTakedown, 188 crate::auth::TokenValidationError::KeyDecryptionFailed => Self::InternalError, 189 crate::auth::TokenValidationError::AuthenticationFailed => Self::AuthenticationFailed, 190 } 191 } 192} 193 194impl From<crate::util::DbLookupError> for ApiError { 195 fn from(e: crate::util::DbLookupError) -> Self { 196 match e { 197 crate::util::DbLookupError::NotFound => Self::AccountNotFound, 198 crate::util::DbLookupError::DatabaseError(db_err) => { 199 tracing::error!("Database error: {:?}", db_err); 200 Self::DatabaseError 201 } 202 } 203 } 204}