add initial crates and apps
This commit is contained in:
parent
5c87ba3519
commit
1ca300098f
113 changed files with 28169 additions and 0 deletions
17
crates/chattyness-error/Cargo.toml
Normal file
17
crates/chattyness-error/Cargo.toml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "chattyness-error"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
# SSR-only dependencies
|
||||
sqlx = { workspace = true, optional = true }
|
||||
axum = { workspace = true, optional = true }
|
||||
http.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
ssr = ["sqlx", "axum"]
|
||||
106
crates/chattyness-error/src/lib.rs
Normal file
106
crates/chattyness-error/src/lib.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Application error types for chattyness.
|
||||
///
|
||||
/// All errors derive From for automatic conversion where applicable.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AppError {
|
||||
#[cfg(feature = "ssr")]
|
||||
#[error("Database error: {0}")]
|
||||
Database(#[from] sqlx::Error),
|
||||
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
#[error("Database error: {0}")]
|
||||
Database(String),
|
||||
|
||||
#[error("Validation error: {0}")]
|
||||
Validation(String),
|
||||
|
||||
#[error("Authentication required")]
|
||||
Unauthorized,
|
||||
|
||||
#[error("Forbidden: {0}")]
|
||||
Forbidden(String),
|
||||
|
||||
#[error("Not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("Conflict: {0}")]
|
||||
Conflict(String),
|
||||
|
||||
#[error("Internal error: {0}")]
|
||||
Internal(String),
|
||||
|
||||
#[error("Invalid credentials")]
|
||||
InvalidCredentials,
|
||||
|
||||
#[error("Account suspended or banned")]
|
||||
AccountSuspended,
|
||||
|
||||
#[error("Not a staff member")]
|
||||
NotStaffMember,
|
||||
|
||||
#[error("Password reset required")]
|
||||
PasswordResetRequired,
|
||||
}
|
||||
|
||||
/// API error response for JSON serialization.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ErrorResponse {
|
||||
pub error: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub code: Option<String>,
|
||||
}
|
||||
|
||||
impl From<AppError> for ErrorResponse {
|
||||
fn from(err: AppError) -> Self {
|
||||
let code = match &err {
|
||||
AppError::Database(_) => Some("DATABASE_ERROR".to_string()),
|
||||
AppError::Validation(_) => Some("VALIDATION_ERROR".to_string()),
|
||||
AppError::Unauthorized => Some("UNAUTHORIZED".to_string()),
|
||||
AppError::Forbidden(_) => Some("FORBIDDEN".to_string()),
|
||||
AppError::NotFound(_) => Some("NOT_FOUND".to_string()),
|
||||
AppError::Conflict(_) => Some("CONFLICT".to_string()),
|
||||
AppError::Internal(_) => Some("INTERNAL_ERROR".to_string()),
|
||||
AppError::InvalidCredentials => Some("INVALID_CREDENTIALS".to_string()),
|
||||
AppError::AccountSuspended => Some("ACCOUNT_SUSPENDED".to_string()),
|
||||
AppError::NotStaffMember => Some("NOT_STAFF_MEMBER".to_string()),
|
||||
AppError::PasswordResetRequired => Some("PASSWORD_RESET_REQUIRED".to_string()),
|
||||
};
|
||||
|
||||
ErrorResponse {
|
||||
error: err.to_string(),
|
||||
code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
mod ssr_impl {
|
||||
use super::*;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::Json;
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> Response {
|
||||
let status = match &self {
|
||||
AppError::Database(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AppError::Validation(_) => StatusCode::BAD_REQUEST,
|
||||
AppError::Unauthorized => StatusCode::UNAUTHORIZED,
|
||||
AppError::Forbidden(_) => StatusCode::FORBIDDEN,
|
||||
AppError::NotFound(_) => StatusCode::NOT_FOUND,
|
||||
AppError::Conflict(_) => StatusCode::CONFLICT,
|
||||
AppError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AppError::InvalidCredentials => StatusCode::UNAUTHORIZED,
|
||||
AppError::AccountSuspended => StatusCode::FORBIDDEN,
|
||||
AppError::NotStaffMember => StatusCode::FORBIDDEN,
|
||||
AppError::PasswordResetRequired => StatusCode::FORBIDDEN,
|
||||
};
|
||||
|
||||
let body = ErrorResponse::from(self);
|
||||
(status, Json(body)).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue