Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions nym-node-status-api/nym-node-status-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

[package]
name = "nym-node-status-api"
version = "3.0.0"
version = "3.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
Expand All @@ -28,7 +28,7 @@ moka = { workspace = true, features = ["future"] }
# Nym API: revert after Cheddar is out
nym-contracts-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-mixnet-contract-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-bin-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-bin-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar", features = ["openapi"]}
nym-node-status-client = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-crypto = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-http-api-client = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
Expand Down
4 changes: 3 additions & 1 deletion nym-node-status-api/nym-node-status-api/src/http/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub(crate) mod metrics;
pub(crate) mod mixnodes;
pub(crate) mod nym_nodes;
pub(crate) mod services;
pub(crate) mod status;
pub(crate) mod summary;
pub(crate) mod testruns;

Expand All @@ -39,7 +40,8 @@ impl RouterBuilder {
.nest("/mixnodes", mixnodes::routes())
.nest("/services", services::routes())
.nest("/summary", summary::routes())
.nest("/metrics", metrics::routes()),
.nest("/metrics", metrics::routes())
.nest("/status", status::routes()),
)
.nest(
"/explorer/v3",
Expand Down
46 changes: 46 additions & 0 deletions nym-node-status-api/nym-node-status-api/src/http/api/status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use axum::{extract::State, Json, Router};
use nym_validator_client::models::BinaryBuildInformationOwned;
use tracing::instrument;

use crate::http::{
error::HttpResult,
state::{AppState, HealthInfo},
};

pub(crate) fn routes() -> Router<AppState> {
Router::new()
.route("/build_information", axum::routing::get(build_information))
.route("/health", axum::routing::get(health))
}

#[utoipa::path(
tag = "Status",
get,
path = "/build_information",
context_path = "/v2/status",
responses(
(status = 200, body = BinaryBuildInformationOwned)
)
)]
#[instrument(level = tracing::Level::INFO, skip_all)]
async fn build_information(
State(state): State<AppState>,
) -> HttpResult<Json<BinaryBuildInformationOwned>> {
let build_info = state.build_information().to_owned();

Ok(Json(build_info))
}

#[utoipa::path(
tag = "Status",
get,
path = "/health",
context_path = "/v2/status",
responses(
(status = 200, body = HealthInfo)
)
)]
#[instrument(level = tracing::Level::INFO, skip_all)]
async fn health(State(state): State<AppState>) -> HttpResult<Json<HealthInfo>> {
Ok(Json(state.health()))
}
37 changes: 36 additions & 1 deletion nym-node-status-api/nym-node-status-api/src/http/state.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use cosmwasm_std::Decimal;
use itertools::Itertools;
use moka::{future::Cache, Entry};
use nym_bin_common::bin_info_owned;
use nym_contracts_common::NaiveFloat;
use nym_crypto::asymmetric::ed25519::PublicKey;
use nym_mixnet_contract_common::NodeId;
use nym_validator_client::nym_api::SkimmedNode;
use nym_validator_client::{models::BinaryBuildInformationOwned, nym_api::SkimmedNode};
use semver::Version;
use serde::Serialize;
use time::UtcDateTime;
use std::{collections::HashMap, sync::Arc, time::Duration};
use tokio::sync::RwLock;
use tracing::{error, instrument, warn};
use utoipa::ToSchema;

use crate::{
db::{queries, DbPool},
Expand All @@ -28,6 +32,7 @@ pub(crate) struct AppState {
agent_max_count: i64,
node_geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
bin_info: BinaryInfo,
}

impl AppState {
Expand All @@ -46,6 +51,7 @@ impl AppState {
agent_max_count,
node_geocache,
node_delegations,
bin_info: BinaryInfo::new(),
}
}

Expand Down Expand Up @@ -78,6 +84,15 @@ impl AppState {
.await
.delegations_owned(node_id)
}

pub(crate) fn health(&self) -> HealthInfo {
let uptime = (UtcDateTime::now() - self.bin_info.startup_time).whole_seconds();
HealthInfo { uptime }
}

pub(crate) fn build_information(&self) -> &BinaryBuildInformationOwned {
&self.bin_info.build_info
}
}

static GATEWAYS_LIST_KEY: &str = "gateways";
Expand Down Expand Up @@ -631,3 +646,23 @@ async fn aggregate_node_info_from_db(

Ok(parsed_nym_nodes)
}

#[derive(Debug, Clone)]
pub(crate) struct BinaryInfo {
startup_time: UtcDateTime,
build_info: BinaryBuildInformationOwned,
}

impl BinaryInfo {
fn new() -> Self {
Self {
startup_time: UtcDateTime::now(),
build_info: bin_info_owned!(),
}
}
}

#[derive(Serialize, ToSchema)]
pub(crate) struct HealthInfo {
uptime: i64,
}
Loading