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}
48
49impl ApiError {
50 fn status_code(&self) -> StatusCode {
51 match self {
52 Self::InternalError | Self::DatabaseError | Self::UpstreamFailure => {
53 StatusCode::INTERNAL_SERVER_ERROR
54 }
55 Self::AuthenticationRequired
56 | Self::AuthenticationFailed
57 | Self::AuthenticationFailedMsg(_)
58 | Self::InvalidToken
59 | Self::ExpiredToken
60 | Self::ExpiredTokenMsg(_)
61 | Self::TokenRequired
62 | Self::AccountDeactivated
63 | Self::AccountTakedown => StatusCode::UNAUTHORIZED,
64 Self::Forbidden | Self::InvitesDisabled => StatusCode::FORBIDDEN,
65 Self::AccountNotFound
66 | Self::RepoNotFound
67 | Self::RepoNotFoundMsg(_)
68 | Self::RecordNotFound
69 | Self::BlobNotFound
70 | Self::AppPasswordNotFound => StatusCode::NOT_FOUND,
71 Self::InvalidRequest(_)
72 | Self::InvalidHandle
73 | Self::HandleNotAvailable
74 | Self::HandleTaken
75 | Self::InvalidEmail
76 | Self::EmailTaken
77 | Self::InvalidInviteCode
78 | Self::DuplicateCreate
79 | Self::DuplicateAppPassword
80 | Self::InvalidSwap => StatusCode::BAD_REQUEST,
81 }
82 }
83
84 fn error_name(&self) -> &'static str {
85 match self {
86 Self::InternalError | Self::DatabaseError | Self::UpstreamFailure => "InternalError",
87 Self::AuthenticationRequired => "AuthenticationRequired",
88 Self::AuthenticationFailed | Self::AuthenticationFailedMsg(_) => "AuthenticationFailed",
89 Self::InvalidToken => "InvalidToken",
90 Self::ExpiredToken | Self::ExpiredTokenMsg(_) => "ExpiredToken",
91 Self::TokenRequired => "TokenRequired",
92 Self::AccountDeactivated => "AccountDeactivated",
93 Self::AccountTakedown => "AccountTakedown",
94 Self::Forbidden => "Forbidden",
95 Self::InvitesDisabled => "InvitesDisabled",
96 Self::AccountNotFound => "AccountNotFound",
97 Self::RepoNotFound | Self::RepoNotFoundMsg(_) => "RepoNotFound",
98 Self::RecordNotFound => "RecordNotFound",
99 Self::BlobNotFound => "BlobNotFound",
100 Self::AppPasswordNotFound => "AppPasswordNotFound",
101 Self::InvalidRequest(_) => "InvalidRequest",
102 Self::InvalidHandle => "InvalidHandle",
103 Self::HandleNotAvailable => "HandleNotAvailable",
104 Self::HandleTaken => "HandleTaken",
105 Self::InvalidEmail => "InvalidEmail",
106 Self::EmailTaken => "EmailTaken",
107 Self::InvalidInviteCode => "InvalidInviteCode",
108 Self::DuplicateCreate => "DuplicateCreate",
109 Self::DuplicateAppPassword => "DuplicateAppPassword",
110 Self::InvalidSwap => "InvalidSwap",
111 }
112 }
113
114 fn message(&self) -> Option<String> {
115 match self {
116 Self::AuthenticationFailedMsg(msg)
117 | Self::ExpiredTokenMsg(msg)
118 | Self::InvalidRequest(msg)
119 | Self::RepoNotFoundMsg(msg) => Some(msg.clone()),
120 _ => None,
121 }
122 }
123}
124
125impl IntoResponse for ApiError {
126 fn into_response(self) -> Response {
127 let body = ErrorBody {
128 error: self.error_name(),
129 message: self.message(),
130 };
131 (self.status_code(), Json(body)).into_response()
132 }
133}
134
135impl From<sqlx::Error> for ApiError {
136 fn from(e: sqlx::Error) -> Self {
137 tracing::error!("Database error: {:?}", e);
138 Self::DatabaseError
139 }
140}
141
142impl From<crate::auth::TokenValidationError> for ApiError {
143 fn from(e: crate::auth::TokenValidationError) -> Self {
144 match e {
145 crate::auth::TokenValidationError::AccountDeactivated => Self::AccountDeactivated,
146 crate::auth::TokenValidationError::AccountTakedown => Self::AccountTakedown,
147 crate::auth::TokenValidationError::KeyDecryptionFailed => Self::InternalError,
148 crate::auth::TokenValidationError::AuthenticationFailed => Self::AuthenticationFailed,
149 }
150 }
151}
152
153impl From<crate::util::DbLookupError> for ApiError {
154 fn from(e: crate::util::DbLookupError) -> Self {
155 match e {
156 crate::util::DbLookupError::NotFound => Self::AccountNotFound,
157 crate::util::DbLookupError::DatabaseError(db_err) => {
158 tracing::error!("Database error: {:?}", db_err);
159 Self::DatabaseError
160 }
161 }
162 }
163}