Files
gitks/error.rs
T
zhenyi 1000f8a80d chore(infra): add gRPC layer, update protobufs, remove immediate module
- Add gRPC service modules: auth, channel, channel settings, member,
  permission
- Update protobuf definitions and generated code
- Remove immediate/ real-time module (superseded by IM service)
- Update etcd discovery and registration
- Update cache, error, config, and build infrastructure
- Add ADR documentation
- Update OpenAPI spec
2026-06-10 18:49:42 +08:00

170 lines
4.7 KiB
Rust

use actix_web::HttpResponse;
use thiserror::Error;
pub type AppResult<T> = Result<T, AppError>;
#[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("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::Database(e) => db_error_status_code(e),
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 {
tracing::error!(?self, "internal server error");
"internal server error".to_string()
} else {
self.to_string()
};
HttpResponse::build(status).json(serde_json::json!({ "error": message }))
}
}
fn db_error_status_code(e: &sqlx::Error) -> actix_web::http::StatusCode {
use actix_web::http::StatusCode;
match e {
sqlx::Error::Database(db_err) => {
match db_err.code().as_ref().map(|c| c.as_ref()) {
// unique_violation
Some("23505") => StatusCode::CONFLICT,
// foreign_key_violation
Some("23503") => StatusCode::CONFLICT,
// check_violation
Some("23514") => StatusCode::BAD_REQUEST,
// not_null_violation
Some("23502") => StatusCode::BAD_REQUEST,
// exclusion_violation
Some("23P01") => StatusCode::CONFLICT,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}