Add axum server to nym-api (#4803)
* Migrate nym-api HTTP server from rocket to axum (#4698) Migrate endpoints to Axum * Squashed after PR review Initial WIP - bootstrap axum server with same data as rocket - start axum server alongside rocket - add routes for circulating-supply, contract-cache, network - write simple bash validation that migrated APIs return 200 - mark rocket parts of code as deprecated - start more complicated routes: WIP Init storage always Add coconut routes Add api-status routes Expand tests WIP Migrate unstable APIs with query params Update bash tests Add node-status routes Redirect / to /swagger Update API tests Implement graceful shutdown rustfmt Fix clippy * Add ecash routes after rebase * PR feedback - add CORS layer - move logger to common crate - remove global log filters for nym-api and axum * Serve OpenAPI for all endpoints (#4761) * Playing around with swagger * Generate OpenAPI for /status routes * Phase out static_routes as strings - also nest routers in a clearer way * Generate OpenAPI for /network routes * Generate OpenAPI for /api-status routes * Generate OpenAPI for "nym nodes" routes * Fix some network-monitor routes * Generate OpenAPI for /ecash routes * Add utoipa feature to /common mods * Add OpenAPI for unstable routes * Fix MixNodeDetails field in models * Introduce axum feature flag (#4775) * Add Axum bind_address to config * Introduce axum feature flag * Add comment to template.rs * Add Github action to build wtih `axum` feature * Refactor server start & shutdown (#4777) * Clippy: don't forget axum feature * Refactor router so it's safer * Implement graceful shutdown * Nicer pattern matching * Better Result syntax
This commit is contained in:
@@ -330,21 +330,22 @@ impl From<HistoricalUptime> for HistoricalUptimeResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ErrorResponse {
|
||||
#[deprecated(note = "TODO rocket remove once Rocket is phased out")]
|
||||
pub(crate) struct RocketErrorResponse {
|
||||
error_message: RequestError,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
impl ErrorResponse {
|
||||
impl RocketErrorResponse {
|
||||
pub(crate) fn new(error_message: impl Into<String>, status: Status) -> Self {
|
||||
ErrorResponse {
|
||||
RocketErrorResponse {
|
||||
error_message: RequestError::new(error_message),
|
||||
status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for ErrorResponse {
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for RocketErrorResponse {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
|
||||
// piggyback on the existing implementation
|
||||
// also prefer json over plain for ease of use in frontend
|
||||
@@ -355,7 +356,7 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for ErrorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonSchema for ErrorResponse {
|
||||
impl JsonSchema for RocketErrorResponse {
|
||||
fn schema_name() -> String {
|
||||
"ErrorResponse".to_owned()
|
||||
}
|
||||
@@ -384,7 +385,7 @@ impl JsonSchema for ErrorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenApiResponderInner for ErrorResponse {
|
||||
impl OpenApiResponderInner for RocketErrorResponse {
|
||||
fn responses(_gen: &mut OpenApiGenerator) -> rocket_okapi::Result<Responses> {
|
||||
let mut responses = Responses::default();
|
||||
ensure_status_code_exists(&mut responses, 404);
|
||||
@@ -392,6 +393,103 @@ impl OpenApiResponderInner for ErrorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
pub(crate) use axum_error::{AxumErrorResponse, AxumResult};
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
/// TODO rocket: extract types from this module when axum becomes the only server in Nym API
|
||||
mod axum_error {
|
||||
pub use super::*;
|
||||
use crate::ecash::error::{EcashError, RedemptionError};
|
||||
use std::fmt::Display;
|
||||
|
||||
// TODO rocket remove smurf name after eliminating `rocket`
|
||||
pub(crate) type AxumResult<T> = Result<T, AxumErrorResponse>;
|
||||
pub(crate) struct AxumErrorResponse {
|
||||
message: RequestError,
|
||||
status: axum::http::StatusCode,
|
||||
}
|
||||
|
||||
impl AxumErrorResponse {
|
||||
pub(crate) fn internal_msg(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(msg.to_string()),
|
||||
status: axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn internal() -> Self {
|
||||
Self {
|
||||
message: RequestError::new("Internal server error"),
|
||||
status: axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn not_implemented() -> Self {
|
||||
Self {
|
||||
message: RequestError::empty(),
|
||||
status: axum::http::StatusCode::NOT_IMPLEMENTED,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn not_found(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(msg.to_string()),
|
||||
status: axum::http::StatusCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn service_unavailable() -> Self {
|
||||
Self {
|
||||
message: RequestError::empty(),
|
||||
status: axum::http::StatusCode::SERVICE_UNAVAILABLE,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unprocessable_entity(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(msg.to_string()),
|
||||
status: axum::http::StatusCode::UNPROCESSABLE_ENTITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl axum::response::IntoResponse for AxumErrorResponse {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
(self.status, self.message.message().to_string()).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NymApiStorageError> for AxumErrorResponse {
|
||||
fn from(value: NymApiStorageError) -> Self {
|
||||
error!("{value}");
|
||||
Self {
|
||||
message: RequestError::empty(),
|
||||
status: axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EcashError> for AxumErrorResponse {
|
||||
fn from(value: EcashError) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(value.to_string()),
|
||||
status: axum::http::StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
impl From<RedemptionError> for AxumErrorResponse {
|
||||
fn from(value: RedemptionError) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(value.to_string()),
|
||||
status: axum::http::StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum NymApiStorageError {
|
||||
#[error("could not find status report associated with mixnode {mix_id}")]
|
||||
|
||||
Reference in New Issue
Block a user