- Switched to secure httpOnly, SameSite=Strict cookies for JWT authentication. - Refactored backend to use AppState for shared secrets and database pool caching. - Modernized frontend with Svelte 5 runes ($state) and removed localStorage reliance. - Gated destructive test endpoints behind debug_assertions and fixed unsafe test patterns. - Enhanced CI pipeline with cargo clippy, cargo fmt, and pinned pnpm version. - Updated documentation and implementation plans to match the hardened architecture.
38 lines
1.1 KiB
Rust
38 lines
1.1 KiB
Rust
use axum::{
|
|
Json,
|
|
http::StatusCode,
|
|
response::{IntoResponse, Response},
|
|
};
|
|
use serde_json::json;
|
|
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum AppError {
|
|
#[error("database error: {0}")]
|
|
Db(#[from] sqlx::Error),
|
|
#[error("not found")]
|
|
NotFound,
|
|
#[error("conflict: {0}")]
|
|
Conflict(String),
|
|
#[error("unauthorized")]
|
|
Unauthorized,
|
|
#[error("bad request: {0}")]
|
|
BadRequest(String),
|
|
}
|
|
|
|
impl IntoResponse for AppError {
|
|
fn into_response(self) -> Response {
|
|
let (status, msg) = match &self {
|
|
AppError::Db(e) => {
|
|
tracing::error!(error = %e, "database error");
|
|
(StatusCode::INTERNAL_SERVER_ERROR, "internal error".into())
|
|
}
|
|
AppError::NotFound => (StatusCode::NOT_FOUND, "not found".into()),
|
|
AppError::Conflict(m) => (StatusCode::CONFLICT, m.clone()),
|
|
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized".into()),
|
|
AppError::BadRequest(m) => (StatusCode::BAD_REQUEST, m.clone()),
|
|
};
|
|
(status, Json(json!({"error": msg}))).into_response()
|
|
}
|
|
}
|