diff --git a/gateway/src/main.rs b/gateway/src/main.rs index e8a1e87ef2..b15febb489 100644 --- a/gateway/src/main.rs +++ b/gateway/src/main.rs @@ -8,11 +8,12 @@ use clap::{crate_name, crate_version, Parser}; use colored::Colorize; use lazy_static::lazy_static; use log::error; -use nym_bin_common::bin_info; 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 std::error::Error; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; mod commands; mod config; @@ -49,10 +50,21 @@ struct Cli { async fn main() -> Result<(), Box> { setup_logging(); - let config = nym_node::http::Config::default(); + let config = nym_node::http::Config { + landing: Default::default(), + policy: Default::default(), + 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(), + network_requester: Default::default(), + }, + }, + }; let mut router = nym_node::http::NymNodeRouter::new(config); - let address = "127.0.0.1:12345".parse().unwrap(); + let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 12345); let server = router.build_server(&address)?; server.await?; diff --git a/nym-node/src/error.rs b/nym-node/src/error.rs index 1780491c67..1bd49c9a72 100644 --- a/nym-node/src/error.rs +++ b/nym-node/src/error.rs @@ -11,4 +11,7 @@ pub enum NymNodeError { bind_address: SocketAddr, source: hyper::Error, }, + + #[error("unimplemented")] + Unimplemented, } diff --git a/nym-node/src/http/middleware/logging.rs b/nym-node/src/http/middleware/logging.rs index b98d7e1b73..d89fe8cb59 100644 --- a/nym-node/src/http/middleware/logging.rs +++ b/nym-node/src/http/middleware/logging.rs @@ -20,9 +20,12 @@ pub async fn logger(req: Request, next: Next) -> impl IntoResponse { let host = header_map(req.headers().get(HOST), "Unknown Host".to_string()); - info!("[{host}] requester {method}: {uri} via {agent}"); + let res = next.run(req).await; + let status = res.status(); - next.run(req).await + info!("[{host}] {method} '{uri}': {status} / agent: {agent}"); + + res } fn header_map(header: Option<&HeaderValue>, msg: String) -> String { diff --git a/nym-node/src/http/mod.rs b/nym-node/src/http/mod.rs index 8a403c5c52..db642a1c85 100644 --- a/nym-node/src/http/mod.rs +++ b/nym-node/src/http/mod.rs @@ -5,7 +5,7 @@ pub mod middleware; pub mod router; pub mod state; -pub use router::{Config, NymNodeRouter}; +pub use router::{Config, NymNodeRouter, api, landing_page, policy}; // pub struct Config { // router_config: router::Config, diff --git a/nym-node/src/http/router/api/mod.rs b/nym-node/src/http/router/api/mod.rs index 4ead438c35..7866a386fc 100644 --- a/nym-node/src/http/router/api/mod.rs +++ b/nym-node/src/http/router/api/mod.rs @@ -16,7 +16,7 @@ pub(crate) mod routes { pub(crate) const SWAGGER: &str = "/swagger"; } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Config { pub v1_config: v1::Config, } 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 531beb77eb..9d9314a1a0 100644 --- a/nym-node/src/http/router/api/v1/build_info.rs +++ b/nym-node/src/http/router/api/v1/build_info.rs @@ -4,30 +4,28 @@ use crate::http::router::api::{FormattedResponse, OutputParams}; use crate::http::state::AppState; use axum::extract::{Query, State}; -use axum::response::IntoResponse; use nym_bin_common::build_information::BinaryBuildInformationOwned; -/// Description of the path +/// Returns build metadata of the binary running the API #[utoipa::path( get, path = "/build-info", context_path = "/api/v1", responses( - (status=200, content( + (status = 200, content( ("application/json" = BinaryBuildInformationOwned), ("application/yaml" = BinaryBuildInformationOwned) )) ), params(OutputParams) )] -pub async fn build_info( +pub(crate) async fn build_info( State(state): State, Query(output): Query, -) -> FormattedResponse { +) -> BuildInfoResponse { let output = output.output.unwrap_or_default(); - // todo!() // TODO: get rid of the clone since it's getting serialized anyway output.to_response(state.build_information.clone()) } -pub struct BuildInfoResponse(BinaryBuildInformationOwned); +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 e8619ab838..b2f6725437 100644 --- a/nym-node/src/http/router/api/v1/gateway/mod.rs +++ b/nym-node/src/http/router/api/v1/gateway/mod.rs @@ -2,13 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 use crate::http::state::AppState; -use axum::http::StatusCode; use axum::routing::get; use axum::Router; +pub mod root; + +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("/", get(|| async { StatusCode::NOT_IMPLEMENTED })) + Router::new().route(routes::ROOT, get(root::root_gateway)) } diff --git a/nym-node/src/http/router/api/v1/gateway/root.rs b/nym-node/src/http/router/api/v1/gateway/root.rs new file mode 100644 index 0000000000..6e96bafcb9 --- /dev/null +++ b/nym-node/src/http/router/api/v1/gateway/root.rs @@ -0,0 +1,24 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::NymNodeError; +use crate::http::router::api::OutputParams; +use axum::extract::Query; +use axum::http::StatusCode; +use axum::response::IntoResponse; + +#[utoipa::path( + get, + path = "", + context_path = "/api/v1/gateway", + responses( + (status = 501, description = "the endpoint hasn't been implemented yet") + ), + params(OutputParams) +)] +pub(crate) async fn root_gateway(Query(_output): Query) -> impl IntoResponse { + ( + StatusCode::NOT_IMPLEMENTED, + NymNodeError::Unimplemented.to_string(), + ) +} diff --git a/nym-node/src/http/router/api/v1/mod.rs b/nym-node/src/http/router/api/v1/mod.rs index 0d52e4bc22..3e199baede 100644 --- a/nym-node/src/http/router/api/v1/mod.rs +++ b/nym-node/src/http/router/api/v1/mod.rs @@ -21,11 +21,12 @@ pub(crate) mod routes { pub(crate) const NETWORK_REQUESTER: &str = "/network-requester"; pub(crate) const BUILD_INFO: &str = "/build-info"; pub(crate) const ROLES: &str = "/roles"; + pub(crate) const SWAGGER: &str = "/swagger"; } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Config { - pub build_information: Option, + pub build_information: BinaryBuildInformationOwned, pub gateway: gateway::Config, pub mixnide: mixnode::Config, pub network_requester: network_requester::Config, diff --git a/nym-node/src/http/router/api/v1/openapi.rs b/nym-node/src/http/router/api/v1/openapi.rs index 7b84b60c1b..8894ae6e71 100644 --- a/nym-node/src/http/router/api/v1/openapi.rs +++ b/nym-node/src/http/router/api/v1/openapi.rs @@ -11,15 +11,31 @@ use utoipa_swagger_ui::SwaggerUi; #[derive(OpenApi)] #[openapi( info(title = "NymNode API"), - paths(api::v1::build_info::build_info,), + 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, + ), + components( + schemas( + api::Output, + api::OutputParams, + BinaryBuildInformationOwned + ) + ) +)] + */ pub(crate) struct ApiDoc; pub(crate) fn route() -> Router { // provide absolute path to the openapi.json let config = utoipa_swagger_ui::Config::from("/api/v1/api-docs/openapi.json"); - SwaggerUi::new("/swagger") + SwaggerUi::new(super::routes::SWAGGER) .url("/api-docs/openapi.json", ApiDoc::openapi()) .config(config) .into() diff --git a/nym-node/src/http/router/mod.rs b/nym-node/src/http/router/mod.rs index 8277de8f46..6690772f29 100644 --- a/nym-node/src/http/router/mod.rs +++ b/nym-node/src/http/router/mod.rs @@ -12,7 +12,7 @@ use std::net::SocketAddr; pub mod api; pub mod landing_page; -mod policy; +pub mod policy; pub(crate) mod routes { pub(crate) const LANDING_PAGE: &str = "/"; @@ -24,7 +24,7 @@ pub(crate) mod routes { // TODO: can it be made nicer? pub type NymNodeHTTPServer = Server>; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Config { pub landing: landing_page::Config, pub policy: policy::Config, @@ -32,24 +32,20 @@ pub struct Config { } pub struct NymNodeRouter { - // landing_page_assets: Option, inner: Router, } impl NymNodeRouter { pub fn new(config: Config) -> NymNodeRouter { - /* - .merge(SwaggerUi::new("/swagger-ui") - .url("/api-docs/openapi.json", ApiDoc::openapi())); - */ - use utoipa::OpenApi; + let state = AppState::new(config.api.v1_config.build_information.clone()); + NymNodeRouter { inner: Router::new() .nest(routes::LANDING_PAGE, landing_page::routes(config.landing)) .nest(routes::POLICY, policy::routes(config.policy)) .nest(routes::API, api::routes(config.api)) .layer(axum::middleware::from_fn(logging::logger)) - .with_state(AppState::new_dummy()), + .with_state(state), } } diff --git a/nym-node/src/http/state.rs b/nym-node/src/http/state.rs index d3ac6ce479..2c63d67ed6 100644 --- a/nym-node/src/http/state.rs +++ b/nym-node/src/http/state.rs @@ -1,26 +1,24 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use nym_bin_common::bin_info_owned; use nym_bin_common::build_information::BinaryBuildInformationOwned; use std::ops::Deref; use std::sync::Arc; #[derive(Debug, Clone)] -pub struct AppState { +pub(crate) struct AppState { inner: Arc, } impl AppState { - pub fn new_dummy() -> Self { + pub fn new(build_information: BinaryBuildInformationOwned) -> Self { AppState { - inner: Arc::new(AppStateInner { - build_information: bin_info_owned!(), - }), + inner: Arc::new(AppStateInner { build_information }), } } } +// TODO: that's kinda abusing it... impl Deref for AppState { type Target = AppStateInner; @@ -30,7 +28,7 @@ impl Deref for AppState { } #[derive(Debug)] -pub struct AppStateInner { +pub(crate) struct AppStateInner { // TODO: split it based on routes? pub(crate) build_information: BinaryBuildInformationOwned, }