gitdataai/libs/service/error.rs

299 lines
10 KiB
Rust

use git::GitError;
#[derive(Debug)]
pub enum AppError {
UserNotFound,
RsaGenerationError,
RsaDecodeError,
CaptchaError,
TwoFactorRequired,
Unauthorized,
DoMainNotSet,
UserNameExists,
EmailExists,
AccountAlreadyExists,
TxnError,
PasswordHashError(String),
TwoFactorAlreadyEnabled,
TwoFactorNotSetup,
InvalidTwoFactorCode,
TwoFactorNotEnabled,
DatabaseError(String),
InvalidPassword,
PasswordTooWeak,
ProjectNotFound,
NoPower,
InternalError,
NotFound(String),
RoleParseError,
ProjectNameAlreadyExists,
RepoNameAlreadyExists,
AvatarUploadError(String),
InternalServerError(String),
PermissionDenied,
RepoNotFound,
RepoForBidAccess,
GitError(GitError),
SerdeError(serde_json::Error),
Io(std::io::Error),
BadRequest(String),
Forbidden(String),
Conflict(String),
InvalidResetToken,
ResetTokenExpired,
ResetTokenUsed,
}
impl AppError {
pub const fn code(&self) -> i32 {
use AppError::*;
match self {
BadRequest(_) => 40000,
CaptchaError => 40001,
SerdeError(_) => 40002,
RsaDecodeError => 40003,
RoleParseError => 40004,
TwoFactorNotSetup => 40005,
TwoFactorNotEnabled => 40006,
InvalidResetToken => 40014,
ResetTokenExpired => 40015,
ResetTokenUsed => 40016,
PasswordTooWeak => 40011,
Unauthorized => 40101,
InvalidTwoFactorCode => 40102,
InvalidPassword => 40103,
NoPower => 40301,
PermissionDenied => 40302,
Forbidden(_) => 40304,
RepoForBidAccess => 40303,
NotFound(_) => 40401,
UserNotFound => 40402,
ProjectNotFound => 40403,
RepoNotFound => 40404,
UserNameExists => 40901,
EmailExists => 40902,
AccountAlreadyExists => 40910,
ProjectNameAlreadyExists => 40903,
RepoNameAlreadyExists => 40905,
TwoFactorAlreadyEnabled => 40904,
Conflict(_) => 40909,
TwoFactorRequired => 42801,
DoMainNotSet => 50001,
TxnError => 50002,
RsaGenerationError => 50003,
PasswordHashError(_) => 50004,
DatabaseError(_) => 50005,
GitError(_) => 50006,
Io(_) => 50007,
InternalError => 50008,
InternalServerError(_) => 50009,
AvatarUploadError(_) => 50010,
}
}
pub const fn http_status_code(&self) -> u16 {
use AppError::*;
match self {
BadRequest(_) => 400,
CaptchaError => 400,
SerdeError(_) => 400,
RsaDecodeError => 400,
RoleParseError => 400,
TwoFactorNotSetup => 400,
TwoFactorNotEnabled => 400,
InvalidResetToken => 400,
ResetTokenExpired => 400,
ResetTokenUsed => 400,
PasswordTooWeak => 400,
Unauthorized => 401,
InvalidTwoFactorCode => 401,
InvalidPassword => 401,
NoPower => 403,
PermissionDenied => 403,
Forbidden(_) => 403,
RepoForBidAccess => 403,
NotFound(_) => 404,
UserNotFound => 404,
ProjectNotFound => 404,
RepoNotFound => 404,
UserNameExists => 409,
EmailExists => 409,
AccountAlreadyExists => 409,
ProjectNameAlreadyExists => 409,
RepoNameAlreadyExists => 409,
TwoFactorAlreadyEnabled => 409,
Conflict(_) => 409,
TwoFactorRequired => 428,
DoMainNotSet => 500,
TxnError => 500,
RsaGenerationError => 500,
PasswordHashError(_) => 500,
DatabaseError(_) => 500,
GitError(_) => 500,
Io(_) => 500,
InternalError => 500,
InternalServerError(_) => 500,
AvatarUploadError(_) => 500,
}
}
pub const fn slug(&self) -> &'static str {
use AppError::*;
match self {
Unauthorized => "unauthorized",
UserNotFound => "user_not_found",
ProjectNotFound => "project_not_found",
RepoNotFound => "repo_not_found",
NotFound(_) => "not_found",
TwoFactorRequired => "two_factor_required",
UserNameExists => "username_exists",
EmailExists => "email_exists",
AccountAlreadyExists => "account_already_exists",
ProjectNameAlreadyExists => "project_name_exists",
RepoNameAlreadyExists => "repo_name_exists",
CaptchaError => "captcha_error",
BadRequest(_) => "bad_request",
SerdeError(_) => "serde_error",
RsaDecodeError => "rsa_decode_error",
RoleParseError => "role_parse_error",
TwoFactorNotSetup => "two_factor_not_setup",
TwoFactorNotEnabled => "two_factor_not_enabled",
InvalidTwoFactorCode => "invalid_two_factor_code",
InvalidPassword => "invalid_password",
PasswordTooWeak => "password_too_weak",
NoPower => "no_power",
PermissionDenied => "permission_denied",
Forbidden(_) => "forbidden",
RepoForBidAccess => "repo_forbidden",
TwoFactorAlreadyEnabled => "two_factor_already_enabled",
Conflict(_) => "conflict",
InvalidResetToken => "invalid_reset_token",
ResetTokenExpired => "reset_token_expired",
ResetTokenUsed => "reset_token_used",
DoMainNotSet => "domain_not_set",
TxnError => "transaction_error",
RsaGenerationError => "rsa_generation_error",
PasswordHashError(_) => "password_hash_error",
DatabaseError(_) => "database_error",
GitError(_) => "git_error",
Io(_) => "io_error",
InternalError => "internal_error",
InternalServerError(_) => "internal_server_error",
AvatarUploadError(_) => "avatar_upload_error",
}
}
pub fn user_message(&self) -> String {
match self {
AppError::NotFound(s) => s.clone(),
AppError::BadRequest(s) => s.clone(),
AppError::Forbidden(s) => s.clone(),
AppError::DatabaseError(s) => {
tracing::error!(error = %s, "database error");
"A database error occurred".to_string()
}
AppError::PasswordHashError(s) => {
tracing::error!(error = %s, "password hash error");
"A password processing error occurred".to_string()
}
AppError::GitError(s) => {
tracing::error!(error = %s, "git error");
"A git error occurred".to_string()
}
AppError::SerdeError(s) => {
tracing::error!(error = %s, "serde error");
"A data parsing error occurred".to_string()
}
AppError::Io(s) => {
tracing::error!(error = %s, "io error");
"A file system error occurred".to_string()
}
AppError::InternalServerError(e) => {
tracing::error!(error = %e, "internal server error");
"An internal server error occurred".to_string()
}
AppError::AvatarUploadError(s) => {
tracing::error!(error = %s, "avatar upload error");
"An avatar upload error occurred".to_string()
}
AppError::Conflict(s) => {
tracing::error!(error = %s, "resource conflict");
"Resource conflict".to_string()
}
_ => self.slug().to_string(),
}
}
}
impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.user_message())
}
}
impl From<argon2::password_hash::Error> for AppError {
fn from(value: argon2::password_hash::Error) -> Self {
AppError::PasswordHashError(value.to_string())
}
}
impl From<sea_orm::error::DbErr> for AppError {
fn from(value: sea_orm::error::DbErr) -> Self {
AppError::DatabaseError(value.to_string())
}
}
impl From<GitError> for AppError {
fn from(value: GitError) -> Self {
AppError::GitError(value)
}
}
impl From<serde_json::Error> for AppError {
fn from(value: serde_json::Error) -> Self {
AppError::SerdeError(value)
}
}
impl From<std::io::Error> for AppError {
fn from(value: std::io::Error) -> Self {
AppError::Io(value)
}
}
impl From<anyhow::Error> for AppError {
fn from(value: anyhow::Error) -> Self {
AppError::InternalServerError(value.to_string())
}
}
impl From<room::RoomError> for AppError {
fn from(err: room::RoomError) -> Self {
use room::RoomError;
match err {
RoomError::Database(e) => {
tracing::error!(error = %e, "room database error");
AppError::DatabaseError(e.to_string())
}
RoomError::NotFound(s) => AppError::NotFound(s),
RoomError::Unauthorized => AppError::Unauthorized,
RoomError::NoPower => AppError::NoPower,
RoomError::RateLimited(s) => AppError::BadRequest(s),
RoomError::BadRequest(s) => AppError::BadRequest(s),
RoomError::RoleParseError => AppError::RoleParseError,
RoomError::Internal(s) => AppError::InternalServerError(s),
}
}
}
impl From<agent::AgentError> for AppError {
fn from(e: agent::AgentError) -> Self {
match e {
agent::AgentError::NotFound(s) => AppError::NotFound(s),
agent::AgentError::InvalidInput { field, reason } => {
AppError::BadRequest(format!("invalid {}: {}", field, reason))
}
_ => AppError::InternalServerError(e.to_string()),
}
}
}