From 0489e5aa652b5e17428e8e8f49eae7cb24e244c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 22 Sep 2023 13:53:50 +0100 Subject: [PATCH] missing swagger data + using closure capture for immutable state --- gateway/src/main.rs | 15 ++++++- nym-node/Cargo.toml | 1 + nym-node/src/http/middleware/logging.rs | 16 +++++-- nym-node/src/http/router/api/mod.rs | 1 - nym-node/src/http/router/api/v1/build_info.rs | 9 ++-- .../src/http/router/api/v1/gateway/mod.rs | 17 +++++-- .../src/http/router/api/v1/gateway/root.rs | 27 +++++++---- .../src/http/router/api/v1/gateway/types.rs | 15 +++++++ .../src/http/router/api/v1/mixnode/mod.rs | 24 +++++++--- .../src/http/router/api/v1/mixnode/root.rs | 33 ++++++++++++++ .../src/http/router/api/v1/mixnode/types.rs | 14 ++++++ nym-node/src/http/router/api/v1/mod.rs | 22 +++++++-- .../router/api/v1/network_requester/mod.rs | 24 +++++++--- .../router/api/v1/network_requester/root.rs | 33 ++++++++++++++ .../router/api/v1/network_requester/types.rs | 14 ++++++ nym-node/src/http/router/api/v1/openapi.rs | 30 ++++++------- nym-node/src/http/router/api/v1/roles.rs | 45 ++++++++++++------- nym-node/src/http/router/mod.rs | 2 +- nym-node/src/http/state.rs | 12 +++-- 19 files changed, 277 insertions(+), 77 deletions(-) create mode 100644 nym-node/src/http/router/api/v1/gateway/types.rs create mode 100644 nym-node/src/http/router/api/v1/mixnode/root.rs create mode 100644 nym-node/src/http/router/api/v1/mixnode/types.rs create mode 100644 nym-node/src/http/router/api/v1/network_requester/root.rs create mode 100644 nym-node/src/http/router/api/v1/network_requester/types.rs diff --git a/gateway/src/main.rs b/gateway/src/main.rs index b15febb489..96a0138008 100644 --- a/gateway/src/main.rs +++ b/gateway/src/main.rs @@ -12,6 +12,7 @@ use nym_bin_common::logging::{maybe_print_banner, setup_logging}; use nym_bin_common::output_format::OutputFormat; use nym_bin_common::{bin_info, bin_info_owned}; use nym_network_defaults::setup_env; +use nym_node::http::api::v1::roles::NodeRoles; use std::error::Error; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; @@ -56,8 +57,18 @@ async fn main() -> Result<(), Box> { api: nym_node::http::api::Config { v1_config: nym_node::http::api::v1::Config { build_information: bin_info_owned!(), - gateway: Default::default(), - mixnide: Default::default(), + roles: NodeRoles { + mixnode_enabled: false, + gateway_enabled: false, + network_requester_enabled: false, + }, + gateway: nym_node::http::api::v1::gateway::Config { + details: Some(nym_node::http::api::v1::gateway::types::Gateway { + encoded_identity_key: "aaa".to_string(), + encoded_sphinx_key: "bbb".to_string(), + }), + }, + mixnode: Default::default(), network_requester: Default::default(), }, }, diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index 90ce2f6704..86acfc788a 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -13,6 +13,7 @@ license.workspace = true [dependencies] anyhow = { workspace = true } bytes = "1.5.0" +colored = "2" serde = { workspace = true, features = ["derive"] } serde_yaml = "0.9.25" serde_json = { workspace = true } diff --git a/nym-node/src/http/middleware/logging.rs b/nym-node/src/http/middleware/logging.rs index d89fe8cb59..8ab0c41946 100644 --- a/nym-node/src/http/middleware/logging.rs +++ b/nym-node/src/http/middleware/logging.rs @@ -6,13 +6,14 @@ use axum::{ middleware::Next, response::IntoResponse, }; +use colored::*; use hyper::header::{HOST, USER_AGENT}; -use tracing::{debug, info}; +use tracing::info; /// Simple logger for requests pub async fn logger(req: Request, next: Next) -> impl IntoResponse { - let method = req.method().to_string(); - let uri = req.uri().to_string(); + let method = req.method().to_string().green(); + let uri = req.uri().to_string().blue(); let agent = header_map( req.headers().get(USER_AGENT), "Unknown User Agent".to_string(), @@ -22,8 +23,15 @@ pub async fn logger(req: Request, next: Next) -> impl IntoResponse { let res = next.run(req).await; let status = res.status(); + let print_status = if status.is_client_error() || status.is_server_error() { + status.to_string().red() + } else if status.is_success() { + status.to_string().green() + } else { + status.to_string().yellow() + }; - info!("[{host}] {method} '{uri}': {status} / agent: {agent}"); + info!("[{host}] {method} '{uri}': {print_status} / agent: {agent}"); res } diff --git a/nym-node/src/http/router/api/mod.rs b/nym-node/src/http/router/api/mod.rs index 7866a386fc..2b914b0eab 100644 --- a/nym-node/src/http/router/api/mod.rs +++ b/nym-node/src/http/router/api/mod.rs @@ -13,7 +13,6 @@ pub mod v1; pub(crate) mod routes { pub(crate) const V1: &str = "/v1"; - pub(crate) const SWAGGER: &str = "/swagger"; } #[derive(Debug, Clone)] diff --git a/nym-node/src/http/router/api/v1/build_info.rs b/nym-node/src/http/router/api/v1/build_info.rs index 9d9314a1a0..719b4cd51d 100644 --- a/nym-node/src/http/router/api/v1/build_info.rs +++ b/nym-node/src/http/router/api/v1/build_info.rs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::http::router::api::{FormattedResponse, OutputParams}; -use crate::http::state::AppState; -use axum::extract::{Query, State}; +use axum::extract::Query; use nym_bin_common::build_information::BinaryBuildInformationOwned; /// Returns build metadata of the binary running the API @@ -11,6 +10,7 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned; get, path = "/build-info", context_path = "/api/v1", + tag = "Base", responses( (status = 200, content( ("application/json" = BinaryBuildInformationOwned), @@ -20,12 +20,11 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned; params(OutputParams) )] pub(crate) async fn build_info( - State(state): State, + build_information: BinaryBuildInformationOwned, Query(output): Query, ) -> BuildInfoResponse { let output = output.output.unwrap_or_default(); - // TODO: get rid of the clone since it's getting serialized anyway - output.to_response(state.build_information.clone()) + output.to_response(build_information) } pub type BuildInfoResponse = FormattedResponse; diff --git a/nym-node/src/http/router/api/v1/gateway/mod.rs b/nym-node/src/http/router/api/v1/gateway/mod.rs index b2f6725437..8946b18f51 100644 --- a/nym-node/src/http/router/api/v1/gateway/mod.rs +++ b/nym-node/src/http/router/api/v1/gateway/mod.rs @@ -6,14 +6,23 @@ use axum::routing::get; use axum::Router; pub mod root; +pub mod types; pub(crate) mod routes { pub(crate) const ROOT: &str = "/"; } #[derive(Debug, Clone, Default)] -pub struct Config {} - -pub(crate) fn routes(_config: Config) -> Router { - Router::new().route(routes::ROOT, get(root::root_gateway)) +pub struct Config { + pub details: Option, +} + +pub(crate) fn routes(config: Config) -> Router { + Router::new().route( + routes::ROOT, + get({ + let gateway_details = config.details; + move |query| root::root_gateway(gateway_details, query) + }), + ) } diff --git a/nym-node/src/http/router/api/v1/gateway/root.rs b/nym-node/src/http/router/api/v1/gateway/root.rs index 6e96bafcb9..7044213f1f 100644 --- a/nym-node/src/http/router/api/v1/gateway/root.rs +++ b/nym-node/src/http/router/api/v1/gateway/root.rs @@ -1,24 +1,33 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::error::NymNodeError; -use crate::http::router::api::OutputParams; +use crate::http::api::v1::gateway::types::Gateway; +use crate::http::router::api::{FormattedResponse, OutputParams}; use axum::extract::Query; use axum::http::StatusCode; -use axum::response::IntoResponse; +/// Returns root gateway information #[utoipa::path( get, path = "", context_path = "/api/v1/gateway", + tag = "Gateway", responses( - (status = 501, description = "the endpoint hasn't been implemented yet") + (status = 501, description = "the endpoint hasn't been implemented yet"), + (status = 200, content( + ("application/json" = Gateway), + ("application/yaml" = Gateway) + )) ), params(OutputParams) )] -pub(crate) async fn root_gateway(Query(_output): Query) -> impl IntoResponse { - ( - StatusCode::NOT_IMPLEMENTED, - NymNodeError::Unimplemented.to_string(), - ) +pub(crate) async fn root_gateway( + details: Option, + Query(output): Query, +) -> Result { + let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?; + let output = output.output.unwrap_or_default(); + Ok(output.to_response(details)) } + +pub type GatewayResponse = FormattedResponse; diff --git a/nym-node/src/http/router/api/v1/gateway/types.rs b/nym-node/src/http/router/api/v1/gateway/types.rs new file mode 100644 index 0000000000..df16b609ba --- /dev/null +++ b/nym-node/src/http/router/api/v1/gateway/types.rs @@ -0,0 +1,15 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; +use utoipa::ToSchema; + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct Gateway { + /// Base58 encoded ed25519 EdDSA public key of the gateway used for deriving shared keys with clients + /// and for signing any messages + pub encoded_identity_key: String, + + /// Base58-encoded x25519 public key used for sphinx key derivation. + pub encoded_sphinx_key: String, +} diff --git a/nym-node/src/http/router/api/v1/mixnode/mod.rs b/nym-node/src/http/router/api/v1/mixnode/mod.rs index e8619ab838..070f71d2b2 100644 --- a/nym-node/src/http/router/api/v1/mixnode/mod.rs +++ b/nym-node/src/http/router/api/v1/mixnode/mod.rs @@ -2,13 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 use crate::http::state::AppState; -use axum::http::StatusCode; use axum::routing::get; use axum::Router; -#[derive(Debug, Clone, Default)] -pub struct Config {} +pub mod root; +pub mod types; -pub(crate) fn routes(_config: Config) -> Router { - Router::new().route("/", get(|| async { StatusCode::NOT_IMPLEMENTED })) +pub(crate) mod routes { + pub(crate) const ROOT: &str = "/"; +} + +#[derive(Debug, Clone, Default)] +pub struct Config { + pub details: Option, +} + +pub(crate) fn routes(config: Config) -> Router { + Router::new().route( + routes::ROOT, + get({ + let mixnode_details = config.details; + move |query| root::root_mixnode(mixnode_details, query) + }), + ) } diff --git a/nym-node/src/http/router/api/v1/mixnode/root.rs b/nym-node/src/http/router/api/v1/mixnode/root.rs new file mode 100644 index 0000000000..927b556c54 --- /dev/null +++ b/nym-node/src/http/router/api/v1/mixnode/root.rs @@ -0,0 +1,33 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::http::api::v1::mixnode::types::Mixnode; +use crate::http::router::api::{FormattedResponse, OutputParams}; +use axum::extract::Query; +use axum::http::StatusCode; + +/// Returns root mixnode information +#[utoipa::path( + get, + path = "", + context_path = "/api/v1/mixnode", + tag = "Mixnode", + responses( + (status = 501, description = "the endpoint hasn't been implemented yet"), + (status = 200, content( + ("application/json" = Mixnode), + ("application/yaml" = Mixnode) + )) + ), + params(OutputParams) +)] +pub(crate) async fn root_mixnode( + details: Option, + Query(output): Query, +) -> Result { + let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?; + let output = output.output.unwrap_or_default(); + Ok(output.to_response(details)) +} + +pub type MixnodeResponse = FormattedResponse; diff --git a/nym-node/src/http/router/api/v1/mixnode/types.rs b/nym-node/src/http/router/api/v1/mixnode/types.rs new file mode 100644 index 0000000000..30c03957a4 --- /dev/null +++ b/nym-node/src/http/router/api/v1/mixnode/types.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; +use utoipa::ToSchema; + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct Mixnode { + /// Base58 encoded ed25519 EdDSA public key of the mixnode. + pub encoded_identity_key: String, + + /// Base58-encoded x25519 public key used for sphinx key derivation. + pub encoded_sphinx_key: String, +} diff --git a/nym-node/src/http/router/api/v1/mod.rs b/nym-node/src/http/router/api/v1/mod.rs index 3e199baede..71591b431d 100644 --- a/nym-node/src/http/router/api/v1/mod.rs +++ b/nym-node/src/http/router/api/v1/mod.rs @@ -1,6 +1,7 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::http::api::v1::roles::NodeRoles; use crate::http::router::api::v1::build_info::build_info; use crate::http::router::api::v1::roles::roles; use crate::http::state::AppState; @@ -27,20 +28,33 @@ pub(crate) mod routes { #[derive(Debug, Clone)] pub struct Config { pub build_information: BinaryBuildInformationOwned, + pub roles: NodeRoles, pub gateway: gateway::Config, - pub mixnide: mixnode::Config, + pub mixnode: mixnode::Config, pub network_requester: network_requester::Config, } pub(super) fn routes(config: Config) -> Router { Router::new() .nest(routes::GATEWAY, gateway::routes(config.gateway)) - .nest(routes::MIXNODE, mixnode::routes(config.mixnide)) + .nest(routes::MIXNODE, mixnode::routes(config.mixnode)) .nest( routes::NETWORK_REQUESTER, network_requester::routes(config.network_requester), ) - .route(routes::BUILD_INFO, get(build_info)) - .route(routes::ROLES, get(roles)) + .route( + routes::BUILD_INFO, + get({ + let build_information = config.build_information; + move |query| build_info(build_information, query) + }), + ) + .route( + routes::ROLES, + get({ + let node_roles = config.roles; + move |query| roles(node_roles, query) + }), + ) .merge(openapi::route()) } diff --git a/nym-node/src/http/router/api/v1/network_requester/mod.rs b/nym-node/src/http/router/api/v1/network_requester/mod.rs index e8619ab838..6bd474c429 100644 --- a/nym-node/src/http/router/api/v1/network_requester/mod.rs +++ b/nym-node/src/http/router/api/v1/network_requester/mod.rs @@ -2,13 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 use crate::http::state::AppState; -use axum::http::StatusCode; use axum::routing::get; use axum::Router; -#[derive(Debug, Clone, Default)] -pub struct Config {} +pub mod root; +pub mod types; -pub(crate) fn routes(_config: Config) -> Router { - Router::new().route("/", get(|| async { StatusCode::NOT_IMPLEMENTED })) +pub(crate) mod routes { + pub(crate) const ROOT: &str = "/"; +} + +#[derive(Debug, Clone, Default)] +pub struct Config { + pub details: Option, +} + +pub(crate) fn routes(config: Config) -> Router { + Router::new().route( + routes::ROOT, + get({ + let network_requester_details = config.details; + move |query| root::root_network_requester(network_requester_details, query) + }), + ) } diff --git a/nym-node/src/http/router/api/v1/network_requester/root.rs b/nym-node/src/http/router/api/v1/network_requester/root.rs new file mode 100644 index 0000000000..4683d1e686 --- /dev/null +++ b/nym-node/src/http/router/api/v1/network_requester/root.rs @@ -0,0 +1,33 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::http::api::v1::network_requester::types::NetworkRequester; +use crate::http::router::api::{FormattedResponse, OutputParams}; +use axum::extract::Query; +use axum::http::StatusCode; + +/// Returns root network requester information +#[utoipa::path( + get, + path = "", + context_path = "/api/v1/network-requester", + tag = "Network Requester", + responses( + (status = 501, description = "the endpoint hasn't been implemented yet"), + (status = 200, content( + ("application/json" = NetworkRequester), + ("application/yaml" = NetworkRequester) + )) + ), + params(OutputParams) +)] +pub(crate) async fn root_network_requester( + details: Option, + Query(output): Query, +) -> Result { + let details = details.ok_or(StatusCode::NOT_IMPLEMENTED)?; + let output = output.output.unwrap_or_default(); + Ok(output.to_response(details)) +} + +pub type NetworkRequesterResponse = FormattedResponse; diff --git a/nym-node/src/http/router/api/v1/network_requester/types.rs b/nym-node/src/http/router/api/v1/network_requester/types.rs new file mode 100644 index 0000000000..5d1277cf1f --- /dev/null +++ b/nym-node/src/http/router/api/v1/network_requester/types.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; +use utoipa::ToSchema; + +#[derive(Serialize, Debug, Clone, ToSchema)] +pub struct NetworkRequester { + /// Base58 encoded ed25519 EdDSA public key of the network requester. + pub encoded_identity_key: String, + + /// Base58-encoded x25519 public key used for performing key exchange with remote clients. + pub encoded_sphinx_key: String, +} diff --git a/nym-node/src/http/router/api/v1/openapi.rs b/nym-node/src/http/router/api/v1/openapi.rs index 8894ae6e71..5250b3c47a 100644 --- a/nym-node/src/http/router/api/v1/openapi.rs +++ b/nym-node/src/http/router/api/v1/openapi.rs @@ -1,6 +1,7 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use super::roles::NodeRoles; use crate::http::router::api; use crate::http::state::AppState; use axum::Router; @@ -8,28 +9,27 @@ use nym_bin_common::build_information::BinaryBuildInformationOwned; use utoipa::OpenApi; use utoipa_swagger_ui::SwaggerUi; -#[derive(OpenApi)] -#[openapi( - info(title = "NymNode API"), - paths(api::v1::build_info::build_info, api::v1::gateway::root::root_gateway,), - components(schemas(api::Output, api::OutputParams, BinaryBuildInformationOwned)) -)] -/* #[derive(OpenApi)] #[openapi( info(title = "NymNode API"), paths( api::v1::build_info::build_info, + api::v1::roles::roles, + api::v1::gateway::root::root_gateway, + api::v1::mixnode::root::root_mixnode, + api::v1::network_requester::root::root_network_requester, ), - components( - schemas( - api::Output, - api::OutputParams, - BinaryBuildInformationOwned - ) - ) + components(schemas( + api::Output, + api::OutputParams, + BinaryBuildInformationOwned, + NodeRoles, + api::v1::gateway::types::Gateway, + api::v1::mixnode::types::Mixnode, + api::v1::network_requester::types::NetworkRequester, + )) )] - */ + pub(crate) struct ApiDoc; pub(crate) fn route() -> Router { diff --git a/nym-node/src/http/router/api/v1/roles.rs b/nym-node/src/http/router/api/v1/roles.rs index 4455a0800c..66617d80b5 100644 --- a/nym-node/src/http/router/api/v1/roles.rs +++ b/nym-node/src/http/router/api/v1/roles.rs @@ -1,23 +1,38 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::http::router::api::Output; +use crate::http::router::api::{FormattedResponse, OutputParams}; use axum::extract::Query; -use axum::response::IntoResponse; +use serde::Serialize; +use utoipa::ToSchema; -pub enum NodeRole { - Mixnode { - // - }, - Gateway { - // - }, - NetworkRequester { - // - }, +#[derive(Clone, Debug, Copy, ToSchema, Serialize)] +pub struct NodeRoles { + pub mixnode_enabled: bool, + pub gateway_enabled: bool, + pub network_requester_enabled: bool, } -pub async fn roles(output: Query>) -> impl IntoResponse { - let output = output.unwrap_or_default(); - todo!() +/// Returns roles supported by this node +#[utoipa::path( + get, + path = "/roles", + context_path = "/api/v1", + tag = "Base", + responses( + (status = 200, content( + ("application/json" = NodeRoles), + ("application/yaml" = NodeRoles) + )) + ), + params(OutputParams) +)] +pub(crate) async fn roles( + node_roles: NodeRoles, + Query(output): Query, +) -> RolesResponse { + let output = output.output.unwrap_or_default(); + output.to_response(node_roles) } + +pub type RolesResponse = FormattedResponse; diff --git a/nym-node/src/http/router/mod.rs b/nym-node/src/http/router/mod.rs index 6690772f29..f480eeb56c 100644 --- a/nym-node/src/http/router/mod.rs +++ b/nym-node/src/http/router/mod.rs @@ -37,7 +37,7 @@ pub struct NymNodeRouter { impl NymNodeRouter { pub fn new(config: Config) -> NymNodeRouter { - let state = AppState::new(config.api.v1_config.build_information.clone()); + let state = AppState::new(); NymNodeRouter { inner: Router::new() diff --git a/nym-node/src/http/state.rs b/nym-node/src/http/state.rs index 2c63d67ed6..7308157cb6 100644 --- a/nym-node/src/http/state.rs +++ b/nym-node/src/http/state.rs @@ -1,7 +1,6 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use nym_bin_common::build_information::BinaryBuildInformationOwned; use std::ops::Deref; use std::sync::Arc; @@ -10,10 +9,12 @@ pub(crate) struct AppState { inner: Arc, } +// TODO: https://docs.rs/axum/latest/axum/extract/struct.State.html#substates + impl AppState { - pub fn new(build_information: BinaryBuildInformationOwned) -> Self { + pub fn new() -> Self { AppState { - inner: Arc::new(AppStateInner { build_information }), + inner: Arc::new(AppStateInner {}), } } } @@ -28,7 +29,4 @@ impl Deref for AppState { } #[derive(Debug)] -pub(crate) struct AppStateInner { - // TODO: split it based on routes? - pub(crate) build_information: BinaryBuildInformationOwned, -} +pub(crate) struct AppStateInner {}