diff --git a/server/src/auth.rs b/server/src/auth.rs index fc9f16b..5ff316d 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -51,7 +51,7 @@ where }, }; - Err(AuthError::Unexpected.into()) + Err(AuthError::Unauthorized.into()) } } diff --git a/server/src/auth/error.rs b/server/src/auth/error.rs index 4f50157..1c782fd 100644 --- a/server/src/auth/error.rs +++ b/server/src/auth/error.rs @@ -6,6 +6,7 @@ pub enum AuthError { InvalidToken, Unexpected, InvalidPassword, + Unauthorized, } impl Display for AuthError { @@ -15,6 +16,7 @@ impl Display for AuthError { Self::InvalidToken => write!(f, "{}", "Invalid token"), Self::Unexpected => write!(f, "{}", "Unexpected error"), Self::InvalidPassword => write!(f, "{}", "Password is incorrect"), + Self::Unauthorized => write!(f, "{}", "Authentication is required"), } } } diff --git a/server/src/routes/member/migrate.rs b/server/src/routes/member/migrate.rs index a2a3b52..277cfd8 100644 --- a/server/src/routes/member/migrate.rs +++ b/server/src/routes/member/migrate.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; -use axum::{extract::State, Json}; +use axum::{ + extract::{FromRef, State}, + Json, +}; use itertools::Itertools; use sqlx::PgPool; @@ -53,10 +56,13 @@ pub async fn migrate_confirm<'a>( tracing::info!("Migration is confirmed"); - // TODO: Implement better error naming let count = match body.trim().parse::() { Ok(c) => c, - Err(_) => return Err(crate::Error::NotFound), + Err(_) => { + return Err(crate::Error::BadRequest { + expected: String::from("u32"), + }) + } }; let mut store = state.migration_store.lock().await; diff --git a/server/src/util/error.rs b/server/src/util/error.rs index 9293aa9..357c61e 100644 --- a/server/src/util/error.rs +++ b/server/src/util/error.rs @@ -7,50 +7,71 @@ use axum::{ #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("CSV error: {0}")] + #[error(transparent)] Csv(#[from] csv::Error), - #[error("Auth error: {0}")] + #[error(transparent)] Auth(#[from] crate::auth::AuthError), - #[error("Database error: {0}")] - Database(#[from] sqlx::Error), + #[error(transparent)] + Sqlx(#[from] sqlx::Error), - #[error("Resource not found")] + #[error("resource not found")] NotFound, + + #[error("Invalid request, expected {expected:?}")] + BadRequest { expected: String }, +} + +#[derive(serde::Serialize)] +struct ErrorResponse { + code: &'static str, + description: String, +} + +impl ErrorResponse { + fn new(code: &'static str, description: String) -> Self { + Self { code, description } + } } impl IntoResponse for Error { fn into_response(self) -> Response { - tracing::error!("Error... {:?}", self); + let (status_code, code) = match self { + Self::Sqlx(ref err_kind) => match err_kind { + sqlx::Error::RowNotFound => (StatusCode::NOT_FOUND, "DATABSE_ROW_NOT_FOUND"), + _ => (StatusCode::INTERNAL_SERVER_ERROR, "DATABASE_ERROR"), + }, - let (status, error_message) = match self { - Error::Auth(AuthError::NoPermssions) => { - (StatusCode::UNAUTHORIZED, String::from("No permissions")) - } - Error::Auth(AuthError::InvalidToken) => { - (StatusCode::BAD_REQUEST, String::from("Invalid token")) - } - Error::Auth(AuthError::Unexpected) => ( - StatusCode::INTERNAL_SERVER_ERROR, - String::from("Unexpected error occured"), - ), - Error::Auth(AuthError::InvalidPassword) => ( - StatusCode::INTERNAL_SERVER_ERROR, - String::from("Invalid password"), - ), - Error::Csv(err) => (StatusCode::BAD_REQUEST, err.to_string()), - Error::NotFound => ( - StatusCode::BAD_REQUEST, - String::from("Could not find resource"), - ), - Error::Database(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()), + Self::Csv(ref err_kind) => match err_kind.kind() { + csv::ErrorKind::UnequalLengths { .. } => { + (StatusCode::BAD_REQUEST, "CSV_INPUT_INVALID") + } + _ => (StatusCode::INTERNAL_SERVER_ERROR, "CSV_ERROR"), + }, + + Self::Auth(ref err_kind) => match err_kind { + crate::auth::AuthError::NoPermssions => { + (StatusCode::FORBIDDEN, "AUTH_NO_PERMISSIONS") + } + crate::auth::AuthError::Unauthorized => { + (StatusCode::UNAUTHORIZED, "AUTH_UNAUTHORIZED") + } + crate::auth::AuthError::InvalidToken => { + (StatusCode::BAD_REQUEST, "AUTH_INVALID_TOKEN") + } + crate::auth::AuthError::InvalidPassword => { + (StatusCode::BAD_REQUEST, "AUTH_INVALID_PASSWORD") + } + _ => (StatusCode::INTERNAL_SERVER_ERROR, "AUTH_ERROR"), + }, + + Self::BadRequest { .. } => (StatusCode::BAD_REQUEST, "INVALID_BODY"), + Self::NotFound => (StatusCode::NOT_FOUND, "NOT_FOUND"), }; - let body = Json(serde_json::json!({ - "error": error_message - })); + let body = ErrorResponse::new(code, self.to_string()); - (status, body).into_response() + (status_code, Json(body)).into_response() } }