1use std::num::{ParseFloatError, ParseIntError};
2
3use axum::{extract::ws::Message, http::StatusCode, response::IntoResponse, Json};
4use common::v1::types::error::Error as ApiError;
5use common::v1::types::{MessageEnvelope, MessagePayload};
6use opentelemetry_otlp::ExporterBuildError;
7use serde_json::json;
8use tracing::error;
9
10use crate::types::MessageSync;
11
12#[derive(thiserror::Error, Debug)]
13pub enum Error {
15 #[error("sqlx error: {0}")]
16 Sqlx(sqlx::Error),
17 #[error("blocked by other user")]
18 Blocked,
19 #[error("missing authentication (not provided or invalid/expired session)")]
20 MissingAuth,
21 #[error("bad header")]
22 BadHeader,
23 #[error("session not yet authenticated")]
24 UnauthSession,
25 #[error("not found")]
26 NotFound,
27 #[error("forbidden")]
28 MissingPermissions,
29 #[error("bad request: {0}")]
30 BadStatic(&'static str),
31 #[error("bad request: {0}")]
32 BadRequest(String),
33 #[error("too big :(")]
34 TooBig,
35 #[error("internal error: {0}")]
36 Internal(String),
37 #[error("internal error: {0}")]
38 IoError(#[from] std::io::Error),
39 #[error("can't overwrite already uploaded data!")]
40 CantOverwrite,
41 #[error("internal error: {0}")]
42 Tempfile(#[from] async_tempfile::Error),
43 #[error("serde error: {0}")]
44 Serde(#[from] serde_json::Error),
45 #[error("axum error")]
46 Axum(#[from] axum::Error),
47 #[error("sushi send error: {0}")]
48 SushiSend(#[from] tokio::sync::broadcast::error::SendError<MessageSync>),
49 #[error("parse int error: {0}")]
50 ParseInt(#[from] ParseIntError),
51 #[error("parse float error: {0}")]
52 ParseFloat(#[from] ParseFloatError),
53 #[error("opendal error: {0}")]
54 Opendal(#[from] opendal::Error),
55 #[error("migrate error: {0}")]
56 SqlxMigrate(#[from] sqlx::migrate::MigrateError),
57 #[error("tracing subscriber error: {0}")]
58 TracingSubscriber(#[from] tracing::subscriber::SetGlobalDefaultError),
59 #[error("log format parse error: {0}")]
60 LogFormatParse(#[from] tracing_subscriber::filter::ParseError),
61 #[error("reqwest error: {0}")]
62 Reqwest(#[from] reqwest::Error),
63 #[error("figment error: {0}")]
64 Figment(#[from] figment::Error),
65 #[error("url parse error: {0}")]
66 UrlParseError(#[from] url::ParseError),
67
68 #[error("unmodified")]
69 NotModified,
71
72 #[error("ffmpeg or ffprobe didn't like seem to like that very much")]
73 Ffmpeg,
74
75 #[error("media type error: {0}")]
76 Media(#[from] mediatype::MediaTypeError),
77
78 #[error("not yet implemented...")]
79 Unimplemented,
80
81 #[error("image error: {0}")]
82 ImageError(#[from] image::ImageError),
83
84 #[error("unknown image format")]
85 UnknownImageFormat,
86
87 #[error("url embed io error: {0}")]
88 UrlEmbed(std::io::Error),
89
90 #[error("url embed error: {0}")]
91 UrlEmbedOther(String),
92
93 #[error("validation error: {0}")]
94 Validation(#[from] validator::ValidationErrors),
95
96 #[error("lettre error: {0}")]
97 Lettre(#[from] lettre::error::Error),
98
99 #[error("invalid credentials")]
100 InvalidCredentials,
101
102 #[error("email address already exists for this user")]
103 EmailAlreadyExists,
104
105 #[error("generic error: {0}")]
106 GenericError(String),
107
108 #[error("OtelExporterBuildError: {0}")]
109 OtelExporterBuildError(#[from] ExporterBuildError),
110
111 #[error("{0}")]
112 ApiError(ApiError),
113}
114
115impl From<sqlx::Error> for Error {
116 fn from(value: sqlx::Error) -> Self {
117 match value {
118 sqlx::Error::RowNotFound => Error::NotFound,
119 err => Error::Sqlx(err),
120 }
121 }
122}
123
124impl From<axum::http::header::ToStrError> for Error {
125 fn from(_value: axum::http::header::ToStrError) -> Self {
126 Error::BadHeader
127 }
128}
129
130impl Error {
131 fn get_status(&self) -> StatusCode {
132 match self {
133 Error::Blocked => StatusCode::FORBIDDEN,
134 Error::NotFound => StatusCode::NOT_FOUND,
135 Error::BadHeader => StatusCode::BAD_REQUEST,
136 Error::BadStatic(_) => StatusCode::BAD_REQUEST,
137 Error::BadRequest(_) => StatusCode::BAD_REQUEST,
138 Error::Serde(_) => StatusCode::BAD_REQUEST,
139 Error::MissingAuth => StatusCode::UNAUTHORIZED,
140 Error::UnauthSession => StatusCode::UNAUTHORIZED,
141 Error::TooBig => StatusCode::PAYLOAD_TOO_LARGE,
142 Error::MissingPermissions => StatusCode::FORBIDDEN,
143 Error::CantOverwrite => StatusCode::CONFLICT,
144 Error::ParseInt(_) => StatusCode::BAD_REQUEST,
145 Error::ParseFloat(_) => StatusCode::BAD_REQUEST,
146 Error::Unimplemented => StatusCode::NOT_IMPLEMENTED,
147 Error::NotModified => StatusCode::NOT_MODIFIED,
148 Error::Validation(_) => StatusCode::BAD_REQUEST,
149 Error::ApiError(err) => match err {
150 ApiError::UserSuspended => StatusCode::FORBIDDEN,
151 },
152 _ => StatusCode::INTERNAL_SERVER_ERROR,
153 }
154 }
155
156 pub fn fake_clone(&self) -> Error {
157 match self {
158 Error::Blocked => Error::Blocked,
159 Error::MissingAuth => Error::MissingAuth,
160 Error::BadHeader => Error::BadHeader,
161 Error::UnauthSession => Error::UnauthSession,
162 Error::NotFound => Error::NotFound,
163 Error::MissingPermissions => Error::MissingPermissions,
164 Error::ApiError(err) => Error::ApiError(err.clone()),
165 Error::BadStatic(s) => Error::BadStatic(s),
166 Error::BadRequest(s) => Error::BadRequest(s.clone()),
167 Error::TooBig => Error::TooBig,
168 Error::Internal(s) => Error::Internal(s.clone()),
169 Error::CantOverwrite => Error::CantOverwrite,
170 Error::ParseInt(parse_int_error) => Error::ParseInt(parse_int_error.clone()),
171 Error::ParseFloat(parse_float_error) => Error::ParseFloat(parse_float_error.clone()),
172 Error::Figment(error) => Error::Figment(error.clone()),
173 Error::UrlParseError(parse_error) => Error::UrlParseError(*parse_error),
174 Error::NotModified => Error::NotModified,
175 Error::Ffmpeg => Error::Ffmpeg,
176 Error::Media(media_type_error) => Error::Media(*media_type_error),
177 Error::Unimplemented => Error::Unimplemented,
178 Error::UnknownImageFormat => Error::UnknownImageFormat,
179 Error::UrlEmbedOther(s) => Error::UrlEmbedOther(s.to_string()),
180 Error::Validation(validation_errors) => Error::Validation(validation_errors.clone()),
181 _ => Error::GenericError(self.to_string()),
182 }
183 }
184}
185
186impl IntoResponse for Error {
187 fn into_response(self) -> axum::response::Response {
188 if let Error::NotModified = self {
189 return self.get_status().into_response();
190 };
191 error!(
192 "Response error: status {}, message {:?}",
193 self.get_status(),
194 self
195 );
196 (
197 self.get_status(),
198 Json(json!({ "error": self.to_string() })),
199 )
200 .into_response()
201 }
202}
203
204impl From<Error> for Message {
205 fn from(val: Error) -> Self {
206 Message::text(
207 serde_json::to_string(&MessageEnvelope {
208 payload: MessagePayload::Error {
209 error: val.to_string(),
210 },
211 })
212 .expect("error should always be able to be serialized"),
213 )
214 }
215}
216
217impl From<ApiError> for Error {
218 fn from(err: ApiError) -> Self {
219 Self::ApiError(err)
220 }
221}
222
223pub type Result<T> = std::result::Result<T, Error>;