Files
podcastic/backend/src/error.rs
T
MichaelandClaude Opus 5.5 1028b7968c feat: rewrite backend in Rust (axum)
Replace the Node/Express backend with a Rust implementation that keeps
the same REST API, MongoDB collections and Redis cache keys, so existing
users, subscriptions and progress keep working.

- axum + mongodb + redis, bcrypt and HS256 JWT compatible with old data
- RSS parsing, iTunes discovery/trending, background sync (1h / 4h)
- Docker image now builds a static Rust binary on debian-slim
- CI runs cargo tests before publishing the image
- compose uses pull_policy: always so a restart picks up the new image

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:34:33 +02:00

67 lines
1.7 KiB
Rust

use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde_json::{json, Value};
/// API error rendered as a JSON body (`{ "message": ... }` by default).
#[derive(Debug)]
pub struct AppError {
pub status: StatusCode,
pub body: Value,
}
impl AppError {
pub fn new(status: StatusCode, message: impl Into<String>) -> Self {
Self {
status,
body: json!({ "message": message.into() }),
}
}
pub fn with_body(status: StatusCode, body: Value) -> Self {
Self { status, body }
}
pub fn bad_request(message: impl Into<String>) -> Self {
Self::new(StatusCode::BAD_REQUEST, message)
}
pub fn unauthorized(message: impl Into<String>) -> Self {
Self::new(StatusCode::UNAUTHORIZED, message)
}
pub fn not_found(message: impl Into<String>) -> Self {
Self::new(StatusCode::NOT_FOUND, message)
}
pub fn validation(errors: Vec<Value>) -> Self {
Self::with_body(
StatusCode::BAD_REQUEST,
json!({ "message": "Validation error", "errors": errors }),
)
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
(self.status, Json(self.body)).into_response()
}
}
pub type ApiResult<T = Json<Value>> = Result<T, AppError>;
/// Logs the underlying error and maps it to a 500 carrying `message`.
pub fn internal<E: std::fmt::Display>(message: &'static str) -> impl FnOnce(E) -> AppError {
move |err| {
tracing::error!("{message}: {err}");
AppError::new(StatusCode::INTERNAL_SERVER_ERROR, message)
}
}
/// Builds a zod-like validation issue.
pub fn issue(field: &str, message: &str) -> Value {
json!({ "path": [field], "message": message })
}