2026-01-21 12:34:59 +01:00
|
|
|
// Copyright (C) 2024-2026 Whiterun LLC
|
2025-02-15 09:15:22 +01:00
|
|
|
//
|
|
|
|
|
// This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later.
|
|
|
|
|
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
|
|
2026-01-21 08:27:57 +01:00
|
|
|
use rocket::{http::Status, response::status::Custom, serde::json::Json};
|
|
|
|
|
use serde_json::{json, Value};
|
|
|
|
|
use std::fmt;
|
2025-02-14 20:43:51 +01:00
|
|
|
|
2026-01-21 10:37:55 +01:00
|
|
|
use super::response::CachedJson;
|
|
|
|
|
|
|
|
|
|
/// Standard API result type with JSON-formatted errors (without caching headers)
|
|
|
|
|
#[allow(dead_code)]
|
2026-01-21 08:27:57 +01:00
|
|
|
pub type ApiResult<T> = Result<Json<T>, Custom<Json<Value>>>;
|
|
|
|
|
|
2026-01-21 10:37:55 +01:00
|
|
|
/// API result type with Cache-Control headers for cacheable responses
|
|
|
|
|
pub type CachedApiResult<T> = Result<CachedJson<T>, Custom<Json<Value>>>;
|
|
|
|
|
|
2026-01-21 08:27:57 +01:00
|
|
|
/// API error codes for consistent error responses
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum ApiErrorCode {
|
|
|
|
|
// 400 Bad Request codes
|
|
|
|
|
InvalidTimestamp,
|
|
|
|
|
InvalidTokenId,
|
|
|
|
|
InvalidPoolId,
|
|
|
|
|
InvalidTxid,
|
|
|
|
|
InvalidParameters,
|
|
|
|
|
InvalidTimeRange,
|
|
|
|
|
InvalidStepsize,
|
|
|
|
|
MissingParameters,
|
|
|
|
|
MissingCategory,
|
|
|
|
|
TooManyIntervals,
|
|
|
|
|
FutureTimestamp,
|
|
|
|
|
|
|
|
|
|
// 404 Not Found codes
|
|
|
|
|
PriceNotFound,
|
|
|
|
|
PoolNotFound,
|
|
|
|
|
|
|
|
|
|
// 500 Internal Server Error codes
|
|
|
|
|
InternalError,
|
|
|
|
|
|
|
|
|
|
// 503 Service Unavailable codes
|
|
|
|
|
CacheWarming,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for ApiErrorCode {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
let s = match self {
|
|
|
|
|
// 400 codes
|
|
|
|
|
Self::InvalidTimestamp => "INVALID_TIMESTAMP",
|
|
|
|
|
Self::InvalidTokenId => "INVALID_TOKEN_ID",
|
|
|
|
|
Self::InvalidPoolId => "INVALID_POOL_ID",
|
|
|
|
|
Self::InvalidTxid => "INVALID_TXID",
|
|
|
|
|
Self::InvalidParameters => "INVALID_PARAMETERS",
|
|
|
|
|
Self::InvalidTimeRange => "INVALID_TIME_RANGE",
|
|
|
|
|
Self::InvalidStepsize => "INVALID_STEPSIZE",
|
|
|
|
|
Self::MissingParameters => "MISSING_PARAMETERS",
|
|
|
|
|
Self::MissingCategory => "MISSING_CATEGORY",
|
|
|
|
|
Self::TooManyIntervals => "TOO_MANY_INTERVALS",
|
|
|
|
|
Self::FutureTimestamp => "FUTURE_TIMESTAMP",
|
|
|
|
|
// 404 codes
|
|
|
|
|
Self::PriceNotFound => "PRICE_NOT_FOUND",
|
|
|
|
|
Self::PoolNotFound => "POOL_NOT_FOUND",
|
|
|
|
|
// 500 codes
|
|
|
|
|
Self::InternalError => "INTERNAL_ERROR",
|
|
|
|
|
// 503 codes
|
|
|
|
|
Self::CacheWarming => "CACHE_WARMING",
|
|
|
|
|
};
|
|
|
|
|
write!(f, "{s}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a 400 Bad Request error with JSON body
|
|
|
|
|
pub fn bad_request(code: ApiErrorCode, message: &str) -> Custom<Json<Value>> {
|
|
|
|
|
Custom(
|
|
|
|
|
Status::BadRequest,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": { "code": code.to_string(), "message": message }
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a 404 Not Found error with JSON body
|
|
|
|
|
pub fn not_found(code: ApiErrorCode, message: &str) -> Custom<Json<Value>> {
|
|
|
|
|
Custom(
|
|
|
|
|
Status::NotFound,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": { "code": code.to_string(), "message": message }
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a 500 Internal Server Error with JSON body
|
|
|
|
|
pub fn internal_error(message: &str) -> Custom<Json<Value>> {
|
|
|
|
|
Custom(
|
|
|
|
|
Status::InternalServerError,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": { "code": ApiErrorCode::InternalError.to_string(), "message": message }
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a 500 Internal Server Error for database errors with JSON body
|
|
|
|
|
pub fn db_error<E: std::fmt::Display>(e: E) -> Custom<Json<Value>> {
|
|
|
|
|
internal_error(&format!("Database error: {e}"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a 503 Service Unavailable error with JSON body
|
|
|
|
|
pub fn service_unavailable(code: ApiErrorCode, message: &str) -> Custom<Json<Value>> {
|
|
|
|
|
Custom(
|
|
|
|
|
Status::ServiceUnavailable,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": { "code": code.to_string(), "message": message }
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Legacy helper for backward compatibility during migration
|
2025-02-14 20:43:51 +01:00
|
|
|
pub fn to_internal_error<E: std::fmt::Display>(e: E) -> Custom<String> {
|
2025-07-16 09:31:14 +02:00
|
|
|
Custom(Status::InternalServerError, format!("Error: {e}"))
|
2025-02-14 20:43:51 +01:00
|
|
|
}
|