1use axum::{
4 http::StatusCode,
5 response::{IntoResponse, Response},
6 Json,
7};
8use serde::Serialize;
9
10pub type Result<T> = std::result::Result<T, ConsoleError>;
11
12#[derive(Debug, thiserror::Error)]
14pub enum ConsoleError {
15 #[error("Authentication failed: {0}")]
16 Authentication(String),
17
18 #[error("Authorization failed: {0}")]
19 Authorization(String),
20
21 #[error("Database error: {0}")]
22 Database(String),
23
24 #[error("Storage error: {0}")]
25 Storage(String),
26
27 #[error("Server error: {0}")]
28 Server(String),
29
30 #[error("Configuration error: {0}")]
31 Config(String),
32
33 #[error("Invalid input: {0}")]
34 InvalidInput(String),
35
36 #[error("Resource not found: {0}")]
37 NotFound(String),
38
39 #[error("Rate limit exceeded")]
40 RateLimitExceeded,
41
42 #[error("WebSocket error: {0}")]
43 WebSocket(String),
44
45 #[error("Template error: {0}")]
46 Template(String),
47
48 #[error("Internal error: {0}")]
49 Internal(String),
50}
51
52#[derive(Serialize)]
53struct ErrorResponse {
54 error: String,
55 message: String,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 details: Option<String>,
58}
59
60impl IntoResponse for ConsoleError {
61 fn into_response(self) -> Response {
62 let (status, error_type, message) = match &self {
63 ConsoleError::Authentication(_) => (
64 StatusCode::UNAUTHORIZED,
65 "authentication_error",
66 self.to_string(),
67 ),
68 ConsoleError::Authorization(_) => (
69 StatusCode::FORBIDDEN,
70 "authorization_error",
71 self.to_string(),
72 ),
73 ConsoleError::Database(_) | ConsoleError::Storage(_) => (
74 StatusCode::BAD_GATEWAY,
75 "service_error",
76 self.to_string(),
77 ),
78 ConsoleError::InvalidInput(_) => (
79 StatusCode::BAD_REQUEST,
80 "invalid_input",
81 self.to_string(),
82 ),
83 ConsoleError::NotFound(_) => (
84 StatusCode::NOT_FOUND,
85 "not_found",
86 self.to_string(),
87 ),
88 ConsoleError::RateLimitExceeded => (
89 StatusCode::TOO_MANY_REQUESTS,
90 "rate_limit_exceeded",
91 "Too many requests. Please try again later.".to_string(),
92 ),
93 ConsoleError::WebSocket(_) => (
94 StatusCode::BAD_REQUEST,
95 "websocket_error",
96 self.to_string(),
97 ),
98 ConsoleError::Template(_) => (
99 StatusCode::INTERNAL_SERVER_ERROR,
100 "template_error",
101 "Failed to render page".to_string(),
102 ),
103 _ => (
104 StatusCode::INTERNAL_SERVER_ERROR,
105 "internal_error",
106 "An internal error occurred".to_string(),
107 ),
108 };
109
110 let error_response = ErrorResponse {
111 error: error_type.to_string(),
112 message,
113 details: None,
114 };
115
116 (status, Json(error_response)).into_response()
117 }
118}
119
120impl From<anyhow::Error> for ConsoleError {
121 fn from(err: anyhow::Error) -> Self {
122 ConsoleError::Internal(err.to_string())
123 }
124}
125
126impl From<serde_json::Error> for ConsoleError {
127 fn from(err: serde_json::Error) -> Self {
128 ConsoleError::InvalidInput(err.to_string())
129 }
130}