use actix_web::HttpResponse; use thiserror::Error; pub type AppResult = Result; #[derive(Debug, Error)] pub enum AppError { #[error("config error: {0}")] Config(String), #[error("database error: {0}")] Database(#[from] sqlx::Error), #[error("redis error: {0}")] Redis(#[from] redis::RedisError), #[error("r2d2 error: {0}")] R2d2(#[from] r2d2::Error), #[error("json error: {0}")] Json(#[from] serde_json::Error), #[error("io error: {0}")] Io(#[from] std::io::Error), #[error("storage error: {0}")] Storage(#[from] object_store::Error), #[error("parse error: {0}")] Parse(String), #[error("user not found")] UserNotFound, #[error("password too weak")] PasswordTooWeak, #[error("password hash error: {0}")] PasswordHashError(String), #[error("invalid password")] InvalidPassword, #[error("account already exists")] AccountAlreadyExists, #[error("captcha error")] CaptchaError, #[error("rsa key generation failed")] RsaGenerationError, #[error("rsa decode error")] RsaDecodeError, #[error("invalid two-factor code")] InvalidTwoFactorCode, #[error("two-factor authentication required")] TwoFactorRequired, #[error("two-factor already enabled")] TwoFactorAlreadyEnabled, #[error("two-factor not set up")] TwoFactorNotSetup, #[error("two-factor not enabled")] TwoFactorNotEnabled, #[error("invalid reset token")] InvalidResetToken, #[error("reset token expired")] ResetTokenExpired, #[error("email already exists")] EmailExists, #[error("invalid email code")] InvalidEmailCode, #[error("unauthorized")] Unauthorized, #[error("forbidden: {0}")] Forbidden(String), #[error("bad request: {0}")] BadRequest(String), #[error("conflict: {0}")] Conflict(String), #[error("quota exceeded: {0}")] QuotaExceeded(String), #[error("not found: {0}")] NotFound(String), #[error("internal server error: {0}")] InternalServerError(String), #[error("transaction error")] TxnError, } impl actix_web::ResponseError for AppError { fn status_code(&self) -> actix_web::http::StatusCode { use actix_web::http::StatusCode; match self { AppError::Unauthorized => StatusCode::UNAUTHORIZED, AppError::Forbidden(_) => StatusCode::FORBIDDEN, AppError::NotFound(_) | AppError::UserNotFound => StatusCode::NOT_FOUND, AppError::BadRequest(_) | AppError::Parse(_) | AppError::CaptchaError => { StatusCode::BAD_REQUEST } AppError::Conflict(_) | AppError::AccountAlreadyExists | AppError::EmailExists => { StatusCode::CONFLICT } AppError::QuotaExceeded(_) => StatusCode::TOO_MANY_REQUESTS, AppError::InvalidPassword | AppError::PasswordTooWeak | AppError::InvalidTwoFactorCode | AppError::TwoFactorRequired | AppError::TwoFactorAlreadyEnabled | AppError::TwoFactorNotSetup | AppError::TwoFactorNotEnabled | AppError::InvalidResetToken | AppError::ResetTokenExpired | AppError::InvalidEmailCode | AppError::RsaDecodeError | AppError::RsaGenerationError => StatusCode::BAD_REQUEST, AppError::PasswordHashError(_) => StatusCode::INTERNAL_SERVER_ERROR, _ => StatusCode::INTERNAL_SERVER_ERROR, } } fn error_response(&self) -> HttpResponse { let status = self.status_code(); let message = if status == actix_web::http::StatusCode::INTERNAL_SERVER_ERROR { "internal server error".to_string() } else { self.to_string() }; HttpResponse::build(status).json(serde_json::json!({ "error": message })) } }