More error handling improvements
This commit is contained in:
		@@ -9,6 +9,7 @@ services:
 | 
				
			|||||||
      - 5432:5432
 | 
					      - 5432:5432
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - postgres_data:/var/lib/postgresql/data
 | 
					      - postgres_data:/var/lib/postgresql/data
 | 
				
			||||||
 | 
					    restart: unless-stopped
 | 
				
			||||||
 | 
					
 | 
				
			||||||
volumes:
 | 
					volumes:
 | 
				
			||||||
  postgres_data:
 | 
					  postgres_data:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										74
									
								
								src/auth.rs
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								src/auth.rs
									
									
									
									
									
								
							@@ -2,8 +2,7 @@ use std::{array::TryFromSliceError, sync::LazyLock};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use axum::{
 | 
					use axum::{
 | 
				
			||||||
    extract::{FromRef, FromRequestParts},
 | 
					    extract::{FromRef, FromRequestParts},
 | 
				
			||||||
    http::{request::Parts, StatusCode},
 | 
					    http::request::Parts,
 | 
				
			||||||
    response::IntoResponse,
 | 
					 | 
				
			||||||
    RequestPartsExt,
 | 
					    RequestPartsExt,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use axum_extra::{
 | 
					use axum_extra::{
 | 
				
			||||||
@@ -16,7 +15,7 @@ use rand::{rngs::OsRng, RngCore};
 | 
				
			|||||||
use serde::{Deserialize, Serialize};
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
use subtle::ConstantTimeEq;
 | 
					use subtle::ConstantTimeEq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{db, Pool};
 | 
					use crate::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const HASH_LENGTH: usize = 64;
 | 
					pub const HASH_LENGTH: usize = 64;
 | 
				
			||||||
pub const SALT_LENGTH: usize = 64;
 | 
					pub const SALT_LENGTH: usize = 64;
 | 
				
			||||||
@@ -56,7 +55,7 @@ fn hash_scrypt(bytes: &[u8], salt: &[u8]) -> [u8; HASH_LENGTH] {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Verifieble scrypt hashed bytes
 | 
					/// Verifieble scrypt hashed bytes
 | 
				
			||||||
#[cfg_attr(test, derive(PartialEq))]
 | 
					#[cfg_attr(test, derive(PartialEq))] // == OPERATOR MUSTN'T BE USED OUTSIZE OF TESTS
 | 
				
			||||||
pub struct HashedBytes {
 | 
					pub struct HashedBytes {
 | 
				
			||||||
    pub hash: [u8; HASH_LENGTH],
 | 
					    pub hash: [u8; HASH_LENGTH],
 | 
				
			||||||
    pub salt: [u8; SALT_LENGTH],
 | 
					    pub salt: [u8; SALT_LENGTH],
 | 
				
			||||||
@@ -122,6 +121,8 @@ pub struct Claims {
 | 
				
			|||||||
    pub exp: i64,
 | 
					    pub exp: i64,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const JWT_ALGORITHM: jsonwebtoken::Algorithm = jsonwebtoken::Algorithm::HS256;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Claims {
 | 
					impl Claims {
 | 
				
			||||||
    pub fn new(user_id: i32) -> Self {
 | 
					    pub fn new(user_id: i32) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
@@ -130,13 +131,9 @@ impl Claims {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn encode(self) -> Result<Token, Error> {
 | 
					    pub fn encode(self) -> Result<Token, GeneralError> {
 | 
				
			||||||
        let access_token = encode(
 | 
					        let access_token = encode(&Header::new(JWT_ALGORITHM), &self, &KEYS.encoding_key)
 | 
				
			||||||
            &Header::new(jsonwebtoken::Algorithm::HS256),
 | 
					            .handle_internal("Token creation error")?;
 | 
				
			||||||
            &self,
 | 
					 | 
				
			||||||
            &KEYS.encoding_key,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .map_err(|_| Error::TokenCreation)?;
 | 
					 | 
				
			||||||
        let token = Token {
 | 
					        let token = Token {
 | 
				
			||||||
            access_token,
 | 
					            access_token,
 | 
				
			||||||
            token_type: "Bearer",
 | 
					            token_type: "Bearer",
 | 
				
			||||||
@@ -145,51 +142,40 @@ impl Claims {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
pub enum Error {
 | 
					 | 
				
			||||||
    WrongCredentials,
 | 
					 | 
				
			||||||
    TokenCreation,
 | 
					 | 
				
			||||||
    Validation,
 | 
					 | 
				
			||||||
    InvalidToken,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl IntoResponse for Error {
 | 
					 | 
				
			||||||
    fn into_response(self) -> axum::response::Response {
 | 
					 | 
				
			||||||
        let (status, error_message) = match self {
 | 
					 | 
				
			||||||
            Error::WrongCredentials => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
 | 
					 | 
				
			||||||
            Error::TokenCreation => (StatusCode::INTERNAL_SERVER_ERROR, "Token creation error"),
 | 
					 | 
				
			||||||
            Error::Validation => (StatusCode::INTERNAL_SERVER_ERROR, "Token validation error"),
 | 
					 | 
				
			||||||
            Error::InvalidToken => (StatusCode::BAD_REQUEST, "Invalid token"),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        (status, error_message).into_response()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[axum::async_trait]
 | 
					#[axum::async_trait]
 | 
				
			||||||
impl<T> FromRequestParts<T> for Claims
 | 
					impl<T> FromRequestParts<T> for Claims
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    Pool: FromRef<T>,
 | 
					    Pool: FromRef<T>,
 | 
				
			||||||
    T: Sync,
 | 
					    T: Sync,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    type Rejection = Error;
 | 
					    type Rejection = GeneralError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn from_request_parts(parts: &mut Parts, state: &T) -> Result<Self, Self::Rejection> {
 | 
					    async fn from_request_parts(parts: &mut Parts, state: &T) -> Result<Self, Self::Rejection> {
 | 
				
			||||||
 | 
					        const INVALID_TOKEN: GeneralError =
 | 
				
			||||||
 | 
					            GeneralError::const_message(StatusCode::UNAUTHORIZED, "Invalid token");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let pool = Pool::from_ref(state);
 | 
					        let pool = Pool::from_ref(state);
 | 
				
			||||||
        let TypedHeader(Authorization(bearer)) = parts
 | 
					        let TypedHeader(Authorization(bearer)) = parts
 | 
				
			||||||
            .extract::<TypedHeader<Authorization<Bearer>>>()
 | 
					            .extract::<TypedHeader<Authorization<Bearer>>>()
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
            .map_err(|_| Error::InvalidToken)?;
 | 
					            .map_err(|_| INVALID_TOKEN)?;
 | 
				
			||||||
        let claims: Claims = decode(bearer.token(), &KEYS.decoding_key, &Validation::default())
 | 
					
 | 
				
			||||||
            .map_err(|_| Error::InvalidToken)?
 | 
					        let claims: Claims = decode(
 | 
				
			||||||
            .claims;
 | 
					            bearer.token(),
 | 
				
			||||||
        match db::users::exists(claims.user_id, &pool).await {
 | 
					            &KEYS.decoding_key,
 | 
				
			||||||
            Ok(true) => Ok(claims),
 | 
					            &Validation::new(JWT_ALGORITHM),
 | 
				
			||||||
            Ok(false) => Err(Error::WrongCredentials),
 | 
					        )
 | 
				
			||||||
            Err(err) => {
 | 
					        .map_err(|_| INVALID_TOKEN)?
 | 
				
			||||||
                tracing::error!(%err);
 | 
					        .claims;
 | 
				
			||||||
                Err(Error::Validation)
 | 
					
 | 
				
			||||||
            }
 | 
					        db::users::exists(claims.user_id, &pool)
 | 
				
			||||||
        }
 | 
					            .await
 | 
				
			||||||
 | 
					            .handle_internal("Token validation error")?
 | 
				
			||||||
 | 
					            .then_some(claims)
 | 
				
			||||||
 | 
					            .ok_or(GeneralError::const_message(
 | 
				
			||||||
 | 
					                StatusCode::UNAUTHORIZED,
 | 
				
			||||||
 | 
					                "Wrong credentials",
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,12 +38,7 @@ impl PermissionType {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn can_read_guard(self) -> GeneralResult<()> {
 | 
					    fn can_read_guard(self) -> GeneralResult<()> {
 | 
				
			||||||
        if !self.can_read() {
 | 
					        self.can_read().then_some(()).item_not_found()?;
 | 
				
			||||||
            return Err(GeneralError::message(
 | 
					 | 
				
			||||||
                StatusCode::NOT_FOUND,
 | 
					 | 
				
			||||||
                "Item not found",
 | 
					 | 
				
			||||||
            ));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ pub async fn download(
 | 
				
			|||||||
    let mut name = db::file::get_name(params.file_id, &state.pool)
 | 
					    let mut name = db::file::get_name(params.file_id, &state.pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .handle_internal("Error getting file info")?
 | 
					        .handle_internal("Error getting file info")?
 | 
				
			||||||
        .ok_or_else(GeneralError::item_not_found)?;
 | 
					        .item_not_found()?;
 | 
				
			||||||
    name = name
 | 
					    name = name
 | 
				
			||||||
        .chars()
 | 
					        .chars()
 | 
				
			||||||
        .fold(String::with_capacity(name.len()), |mut result, char| {
 | 
					        .fold(String::with_capacity(name.len()), |mut result, char| {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ pub async fn modify(
 | 
				
			|||||||
        .write(params.file_id)
 | 
					        .write(params.file_id)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .handle_internal("Error writing to the file")?
 | 
					        .handle_internal("Error writing to the file")?
 | 
				
			||||||
        .ok_or_else(GeneralError::item_not_found)?;
 | 
					        .item_not_found()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let (hash, size) = crate::FileStorage::write_to_file(&mut file, &mut field)
 | 
					    let (hash, size) = crate::FileStorage::write_to_file(&mut file, &mut field)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,12 +29,12 @@ pub async fn structure(
 | 
				
			|||||||
    let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool)
 | 
					    let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .handle_internal("Error processing id")?
 | 
					        .handle_internal("Error processing id")?
 | 
				
			||||||
        .ok_or_else(GeneralError::item_not_found)?;
 | 
					        .item_not_found()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let folder = db::folder::get_by_id(folder_id, &pool)
 | 
					    let folder = db::folder::get_by_id(folder_id, &pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .handle_internal("Error getting folder info")?
 | 
					        .handle_internal("Error getting folder info")?
 | 
				
			||||||
        .ok_or_else(GeneralError::item_not_found)?;
 | 
					        .item_not_found()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut response: FolderStructure = folder.into();
 | 
					    let mut response: FolderStructure = folder.into();
 | 
				
			||||||
    let mut stack = vec![&mut response];
 | 
					    let mut stack = vec![&mut response];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,7 @@ pub async fn set(
 | 
				
			|||||||
    let folder_info = db::folder::get_by_id(params.folder_id, &pool)
 | 
					    let folder_info = db::folder::get_by_id(params.folder_id, &pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .handle_internal("Error getting folder info")?
 | 
					        .handle_internal("Error getting folder info")?
 | 
				
			||||||
        .ok_or_else(GeneralError::item_not_found)?;
 | 
					        .item_not_found()?;
 | 
				
			||||||
    if folder_info.owner_id == params.user_id {
 | 
					    if folder_info.owner_id == params.user_id {
 | 
				
			||||||
        return Err(GeneralError::message(
 | 
					        return Err(GeneralError::message(
 | 
				
			||||||
            StatusCode::BAD_REQUEST,
 | 
					            StatusCode::BAD_REQUEST,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
use axum::Form;
 | 
					use axum::Form;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    auth::{authenticate_user, Error, Token},
 | 
					    auth::{authenticate_user, Token},
 | 
				
			||||||
    prelude::*,
 | 
					    prelude::*,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,10 +14,13 @@ pub struct Params {
 | 
				
			|||||||
pub async fn login(
 | 
					pub async fn login(
 | 
				
			||||||
    State(pool): State<Pool>,
 | 
					    State(pool): State<Pool>,
 | 
				
			||||||
    Form(payload): Form<Params>,
 | 
					    Form(payload): Form<Params>,
 | 
				
			||||||
) -> Result<Json<Token>, Error> {
 | 
					) -> GeneralResult<Json<Token>> {
 | 
				
			||||||
    let user_id = authenticate_user(&payload.username, &payload.password, &pool)
 | 
					    let user_id = authenticate_user(&payload.username, &payload.password, &pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .map_err(|_| Error::WrongCredentials)?
 | 
					        .handle_internal("Error getting user from database")?
 | 
				
			||||||
        .ok_or(Error::WrongCredentials)?;
 | 
					        .handle(
 | 
				
			||||||
 | 
					            StatusCode::NOT_FOUND,
 | 
				
			||||||
 | 
					            "User with this name and password doesn't exist",
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
    Claims::new(user_id).encode().map(Json)
 | 
					    Claims::new(user_id).encode().map(Json)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ pub async fn put(
 | 
				
			|||||||
    claims: Claims,
 | 
					    claims: Claims,
 | 
				
			||||||
    Json(params): Json<Params>,
 | 
					    Json(params): Json<Params>,
 | 
				
			||||||
) -> GeneralResult<Json<db::users::UserInfo>> {
 | 
					) -> GeneralResult<Json<db::users::UserInfo>> {
 | 
				
			||||||
    params.validate().map_err(GeneralError::validation)?;
 | 
					    params.validate().handle_validation()?;
 | 
				
			||||||
    db::users::update(claims.user_id, ¶ms.username, ¶ms.email, &pool)
 | 
					    db::users::update(claims.user_id, ¶ms.username, ¶ms.email, &pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .handle_internal("Error updating the user")
 | 
					        .handle_internal("Error updating the user")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,9 @@
 | 
				
			|||||||
use axum::Form;
 | 
					use axum::Form;
 | 
				
			||||||
use axum_extra::either::Either;
 | 
					 | 
				
			||||||
use itertools::Itertools;
 | 
					use itertools::Itertools;
 | 
				
			||||||
use validator::{Validate, ValidationError};
 | 
					use validator::{Validate, ValidationError};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    auth::{Error, HashedBytes, Token},
 | 
					    auth::{HashedBytes, Token},
 | 
				
			||||||
    prelude::*,
 | 
					    prelude::*,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,23 +47,17 @@ fn validate_password(password: &str) -> Result<(), ValidationError> {
 | 
				
			|||||||
pub async fn register(
 | 
					pub async fn register(
 | 
				
			||||||
    State(pool): State<Pool>,
 | 
					    State(pool): State<Pool>,
 | 
				
			||||||
    Form(params): Form<Params>,
 | 
					    Form(params): Form<Params>,
 | 
				
			||||||
) -> Result<Json<Token>, Either<GeneralError, Error>> {
 | 
					) -> GeneralResult<Json<Token>> {
 | 
				
			||||||
    params
 | 
					    params.validate().handle_validation()?;
 | 
				
			||||||
        .validate()
 | 
					 | 
				
			||||||
        .map_err(GeneralError::validation)
 | 
					 | 
				
			||||||
        .map_err(Either::E1)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let password = HashedBytes::hash_bytes(params.password.as_bytes()).as_bytes();
 | 
					    let password = HashedBytes::hash_bytes(params.password.as_bytes()).as_bytes();
 | 
				
			||||||
    let id = db::users::create_user(¶ms.username, ¶ms.email, &password, &pool)
 | 
					    let id = db::users::create_user(¶ms.username, ¶ms.email, &password, &pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .handle_internal("Error creating the user")
 | 
					        .handle_internal("Error creating the user")?
 | 
				
			||||||
        .map_err(Either::E1)?
 | 
					 | 
				
			||||||
        .handle(
 | 
					        .handle(
 | 
				
			||||||
            StatusCode::BAD_REQUEST,
 | 
					            StatusCode::BAD_REQUEST,
 | 
				
			||||||
            "The username or the email are taken",
 | 
					            "The username or the email are taken",
 | 
				
			||||||
        )
 | 
					        )?;
 | 
				
			||||||
        .map_err(Either::E1)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let token = Claims::new(id).encode().map_err(Either::E2)?;
 | 
					    Claims::new(id).encode().map(Json)
 | 
				
			||||||
    Ok(Json(token))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,14 +4,16 @@ use axum::{http::StatusCode, response::IntoResponse};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type BoxError = Box<dyn std::error::Error>;
 | 
					type BoxError = Box<dyn std::error::Error>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Common error type for the project
 | 
				
			||||||
pub struct GeneralError {
 | 
					pub struct GeneralError {
 | 
				
			||||||
 | 
					    /// Response status code
 | 
				
			||||||
    pub status_code: StatusCode,
 | 
					    pub status_code: StatusCode,
 | 
				
			||||||
 | 
					    /// Message to send to the user
 | 
				
			||||||
    pub message: Cow<'static, str>,
 | 
					    pub message: Cow<'static, str>,
 | 
				
			||||||
 | 
					    /// Error to log
 | 
				
			||||||
    pub error: Option<BoxError>,
 | 
					    pub error: Option<BoxError>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type GeneralResult<T> = Result<T, GeneralError>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl GeneralError {
 | 
					impl GeneralError {
 | 
				
			||||||
    pub fn message(status_code: StatusCode, message: impl Into<Cow<'static, str>>) -> Self {
 | 
					    pub fn message(status_code: StatusCode, message: impl Into<Cow<'static, str>>) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
@@ -21,15 +23,10 @@ impl GeneralError {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[allow(clippy::needless_pass_by_value)]
 | 
					    pub const fn const_message(status_code: StatusCode, message: &'static str) -> Self {
 | 
				
			||||||
    pub fn validation(error: validator::ValidationErrors) -> Self {
 | 
					        Self {
 | 
				
			||||||
        Self::message(StatusCode::BAD_REQUEST, error.to_string())
 | 
					            status_code,
 | 
				
			||||||
    }
 | 
					            message: Cow::Borrowed(message),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub const fn item_not_found() -> Self {
 | 
					 | 
				
			||||||
        GeneralError {
 | 
					 | 
				
			||||||
            status_code: StatusCode::NOT_FOUND,
 | 
					 | 
				
			||||||
            message: Cow::Borrowed("Item not found"),
 | 
					 | 
				
			||||||
            error: None,
 | 
					            error: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -44,6 +41,8 @@ impl IntoResponse for GeneralError {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub type GeneralResult<T> = Result<T, GeneralError>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait ErrorHandlingExt<T, E>
 | 
					pub trait ErrorHandlingExt<T, E>
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    Self: Sized,
 | 
					    Self: Sized,
 | 
				
			||||||
@@ -86,3 +85,25 @@ impl<T> ErrorHandlingExt<T, Infallible> for Option<T> {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait ItemNotFoundExt<T> {
 | 
				
			||||||
 | 
					    fn item_not_found(self) -> Result<T, GeneralError>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> ItemNotFoundExt<T> for Option<T> {
 | 
				
			||||||
 | 
					    fn item_not_found(self) -> GeneralResult<T> {
 | 
				
			||||||
 | 
					        const ITEM_NOT_FOUND_ERROR: GeneralError =
 | 
				
			||||||
 | 
					            GeneralError::const_message(StatusCode::NOT_FOUND, "Item not found");
 | 
				
			||||||
 | 
					        self.ok_or(ITEM_NOT_FOUND_ERROR)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait ValidationExt<T> {
 | 
				
			||||||
 | 
					    fn handle_validation(self) -> GeneralResult<T>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> ValidationExt<T> for Result<T, validator::ValidationErrors> {
 | 
				
			||||||
 | 
					    fn handle_validation(self) -> GeneralResult<T> {
 | 
				
			||||||
 | 
					        self.map_err(|err| GeneralError::message(StatusCode::BAD_REQUEST, err.to_string()))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
pub(crate) use crate::{
 | 
					pub(crate) use crate::{
 | 
				
			||||||
    auth::Claims,
 | 
					    auth::Claims,
 | 
				
			||||||
    db::{self, permissions::PermissionExt as _},
 | 
					    db::{self, permissions::PermissionExt as _},
 | 
				
			||||||
    errors::{ErrorHandlingExt as _, GeneralError, GeneralResult},
 | 
					    errors::{
 | 
				
			||||||
 | 
					        ErrorHandlingExt as _, GeneralError, GeneralResult, ItemNotFoundExt as _,
 | 
				
			||||||
 | 
					        ValidationExt as _,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    AppState, Pool,
 | 
					    AppState, Pool,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
pub use axum::{
 | 
					pub use axum::{
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user