Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70fb7d75d6 | |||
| f11b2caeb1 | |||
| 1a07dbb09c | |||
| 9587247536 |
Generated
+2
-1
@@ -6768,7 +6768,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-node-status-api"
|
name = "nym-node-status-api"
|
||||||
version = "3.2.2"
|
version = "3.2.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ammonia",
|
"ammonia",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -6780,6 +6780,7 @@ dependencies = [
|
|||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"envy",
|
"envy",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"hex",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"moka",
|
"moka",
|
||||||
"nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
|
"nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
|
||||||
|
|||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "\n SELECT\n date_utc as \"date_utc!\",\n SUM(total_stake) as \"total_stake!: i64\",\n SUM(packets_received) as \"total_packets_received!: i64\",\n SUM(packets_sent) as \"total_packets_sent!: i64\",\n SUM(packets_dropped) as \"total_packets_dropped!: i64\"\n FROM (\n SELECT\n date_utc,\n n.total_stake,\n n.packets_received,\n n.packets_sent,\n n.packets_dropped\n FROM nym_node_daily_mixing_stats n\n UNION ALL\n SELECT\n m.date_utc,\n m.total_stake,\n m.packets_received,\n m.packets_sent,\n m.packets_dropped\n FROM mixnode_daily_stats m\n LEFT JOIN nym_node_daily_mixing_stats ON m.mix_id = nym_node_daily_mixing_stats.node_id\n WHERE nym_node_daily_mixing_stats.node_id IS NULL\n )\n GROUP BY date_utc\n ORDER BY date_utc ASC\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"ordinal": 0,
|
|
||||||
"name": "date_utc!",
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 1,
|
|
||||||
"name": "total_stake!: i64",
|
|
||||||
"type_info": "Numeric"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 2,
|
|
||||||
"name": "total_packets_received!: i64",
|
|
||||||
"type_info": "Int8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 3,
|
|
||||||
"name": "total_packets_sent!: i64",
|
|
||||||
"type_info": "Int8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 4,
|
|
||||||
"name": "total_packets_dropped!: i64",
|
|
||||||
"type_info": "Int8"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": []
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hash": "124d45b9604439584650f401607c46bdbd162c7c689f74fe9ddfdfd48f5ddc07"
|
|
||||||
}
|
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT\n date_utc as \"date_utc!\",\n SUM(total_stake)::BIGINT as \"total_stake!: i64\",\n SUM(packets_received)::BIGINT as \"total_packets_received!: i64\",\n SUM(packets_sent)::BIGINT as \"total_packets_sent!: i64\",\n SUM(packets_dropped)::BIGINT as \"total_packets_dropped!: i64\"\n FROM (\n SELECT\n date_utc,\n n.total_stake,\n n.packets_received,\n n.packets_sent,\n n.packets_dropped\n FROM nym_node_daily_mixing_stats n\n UNION ALL\n SELECT\n m.date_utc,\n m.total_stake,\n m.packets_received,\n m.packets_sent,\n m.packets_dropped\n FROM mixnode_daily_stats m\n LEFT JOIN nym_node_daily_mixing_stats ON m.mix_id = nym_node_daily_mixing_stats.node_id\n WHERE nym_node_daily_mixing_stats.node_id IS NULL\n )\n GROUP BY date_utc\n ORDER BY date_utc ASC\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "date_utc!",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "total_stake!: i64",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "total_packets_received!: i64",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "total_packets_sent!: i64",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "total_packets_dropped!: i64",
|
||||||
|
"type_info": "Int8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": []
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "a19da2073bc691fe5cd2119a9527c16d6a8806ba50dcb759ac705b1a19288ca9"
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-node-status-api"
|
name = "nym-node-status-api"
|
||||||
version = "3.2.2"
|
version = "3.2.6"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
homepage.workspace = true
|
homepage.workspace = true
|
||||||
@@ -22,6 +22,7 @@ clap = { workspace = true, features = ["cargo", "derive", "env", "string"] }
|
|||||||
cosmwasm-std = { workspace = true }
|
cosmwasm-std = { workspace = true }
|
||||||
envy = { workspace = true }
|
envy = { workspace = true }
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
|
hex = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
moka = { workspace = true, features = ["future"] }
|
moka = { workspace = true, features = ["future"] }
|
||||||
# TODO dz had to switch to cheddar versions because develop is ahead of current
|
# TODO dz had to switch to cheddar versions because develop is ahead of current
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ pub(crate) async fn get_all_mixnodes(pool: &DbPool) -> anyhow::Result<Vec<Mixnod
|
|||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlite")]
|
||||||
pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailyStats>> {
|
pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailyStats>> {
|
||||||
let mut conn = pool.acquire().await?;
|
let mut conn = pool.acquire().await?;
|
||||||
let items = sqlx::query_as!(
|
let items = sqlx::query_as!(
|
||||||
@@ -142,6 +143,48 @@ pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailySt
|
|||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "pg")]
|
||||||
|
pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailyStats>> {
|
||||||
|
let mut conn = pool.acquire().await?;
|
||||||
|
let items = sqlx::query_as!(
|
||||||
|
DailyStats,
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
date_utc as "date_utc!",
|
||||||
|
SUM(total_stake)::BIGINT as "total_stake!: i64",
|
||||||
|
SUM(packets_received)::BIGINT as "total_packets_received!: i64",
|
||||||
|
SUM(packets_sent)::BIGINT as "total_packets_sent!: i64",
|
||||||
|
SUM(packets_dropped)::BIGINT as "total_packets_dropped!: i64"
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
date_utc,
|
||||||
|
n.total_stake,
|
||||||
|
n.packets_received,
|
||||||
|
n.packets_sent,
|
||||||
|
n.packets_dropped
|
||||||
|
FROM nym_node_daily_mixing_stats n
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
m.date_utc,
|
||||||
|
m.total_stake,
|
||||||
|
m.packets_received,
|
||||||
|
m.packets_sent,
|
||||||
|
m.packets_dropped
|
||||||
|
FROM mixnode_daily_stats m
|
||||||
|
LEFT JOIN nym_node_daily_mixing_stats ON m.mix_id = nym_node_daily_mixing_stats.node_id
|
||||||
|
WHERE nym_node_daily_mixing_stats.node_id IS NULL
|
||||||
|
)
|
||||||
|
GROUP BY date_utc
|
||||||
|
ORDER BY date_utc ASC
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.fetch(&mut *conn)
|
||||||
|
.try_collect::<Vec<DailyStats>>()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(items)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_bonded_mix_ids(pool: &DbPool) -> anyhow::Result<HashSet<i64>> {
|
pub(crate) async fn get_bonded_mix_ids(pool: &DbPool) -> anyhow::Result<HashSet<i64>> {
|
||||||
let mut conn = pool.acquire().await?;
|
let mut conn = pool.acquire().await?;
|
||||||
let items = crate::db::query(
|
let items = crate::db::query(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use tower_http::cors::CorsLayer;
|
|||||||
use utoipa::OpenApi;
|
use utoipa::OpenApi;
|
||||||
use utoipa_swagger_ui::SwaggerUi;
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
use crate::http::{server::HttpServer, state::AppState};
|
use crate::http::{middleware, server::HttpServer, state::AppState};
|
||||||
|
|
||||||
pub(crate) mod dvpn;
|
pub(crate) mod dvpn;
|
||||||
pub(crate) mod gateways;
|
pub(crate) mod gateways;
|
||||||
@@ -63,17 +63,23 @@ impl RouterBuilder {
|
|||||||
|
|
||||||
pub(crate) fn with_state(self, state: AppState) -> RouterWithState {
|
pub(crate) fn with_state(self, state: AppState) -> RouterWithState {
|
||||||
RouterWithState {
|
RouterWithState {
|
||||||
router: self.finalize_routes().with_state(state),
|
router: self.finalize_routes(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_routes(self) -> Router<AppState> {
|
fn finalize_routes(self, state: AppState) -> Router {
|
||||||
// layers added later wrap earlier layers
|
// layers added later wrap earlier layers
|
||||||
self.unfinished_router
|
self.unfinished_router
|
||||||
|
// Add response headers middleware
|
||||||
|
.layer(axum::middleware::from_fn_with_state(
|
||||||
|
state.clone(),
|
||||||
|
middleware::add_response_headers,
|
||||||
|
))
|
||||||
// CORS layer needs to wrap other API layers
|
// CORS layer needs to wrap other API layers
|
||||||
.layer(setup_cors())
|
.layer(setup_cors())
|
||||||
// logger should be outermost layer
|
// logger should be outermost layer
|
||||||
.layer(axum::middleware::from_fn(log_request_debug))
|
.layer(axum::middleware::from_fn(log_request_debug))
|
||||||
|
.with_state(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,12 +133,4 @@ mod tests {
|
|||||||
let _test_router = unfinished_router;
|
let _test_router = unfinished_router;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_router_builder_finalize() {
|
|
||||||
let router_builder = RouterBuilder::with_default_routes();
|
|
||||||
let finalized = router_builder.finalize_routes();
|
|
||||||
|
|
||||||
// This tests that finalize_routes produces a valid Router
|
|
||||||
let _router = finalized;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ fn authenticate(request: &impl VerifiableRequest, state: &AppState) -> HttpResul
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
static FRESHNESS_CUTOFF: time::Duration = time::Duration::minutes(2);
|
static FRESHNESS_CUTOFF: time::Duration = time::Duration::minutes(5);
|
||||||
|
|
||||||
fn is_fresh(request_time: &i64) -> HttpResult<()> {
|
fn is_fresh(request_time: &i64) -> HttpResult<()> {
|
||||||
// if a request took longer than N minutes to reach NS API, something is very wrong
|
// if a request took longer than N minutes to reach NS API, something is very wrong
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use axum::{
|
||||||
|
extract::{Request, State},
|
||||||
|
http::{HeaderName, HeaderValue},
|
||||||
|
middleware::Next,
|
||||||
|
response::Response,
|
||||||
|
};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
use crate::http::state::AppState;
|
||||||
|
|
||||||
|
pub async fn add_response_headers(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
request: Request,
|
||||||
|
next: Next,
|
||||||
|
) -> Response {
|
||||||
|
let mut response = next.run(request).await;
|
||||||
|
|
||||||
|
let headers = response.headers_mut();
|
||||||
|
|
||||||
|
// Add timestamp header (RFC3339 format)
|
||||||
|
let timestamp = OffsetDateTime::now_utc()
|
||||||
|
.format(&time::format_description::well_known::Rfc3339)
|
||||||
|
.unwrap_or_else(|_| "unknown".to_string());
|
||||||
|
if let Ok(value) = HeaderValue::from_str(×tamp) {
|
||||||
|
headers.insert(HeaderName::from_static("x-response-timestamp"), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add instance ID header
|
||||||
|
if let Ok(value) = HeaderValue::from_str(state.instance_id()) {
|
||||||
|
headers.insert(HeaderName::from_static("x-instance-id"), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
response
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ use utoipa::ToSchema;
|
|||||||
pub(crate) mod api;
|
pub(crate) mod api;
|
||||||
pub(crate) mod api_docs;
|
pub(crate) mod api_docs;
|
||||||
pub(crate) mod error;
|
pub(crate) mod error;
|
||||||
|
pub(crate) mod middleware;
|
||||||
pub(crate) mod models;
|
pub(crate) mod models;
|
||||||
pub(crate) mod server;
|
pub(crate) mod server;
|
||||||
pub(crate) mod state;
|
pub(crate) mod state;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pub(crate) struct AppState {
|
|||||||
node_geocache: NodeGeoCache,
|
node_geocache: NodeGeoCache,
|
||||||
node_delegations: Arc<RwLock<DelegationsCache>>,
|
node_delegations: Arc<RwLock<DelegationsCache>>,
|
||||||
bin_info: BinaryInfo,
|
bin_info: BinaryInfo,
|
||||||
|
instance_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
@@ -53,6 +54,7 @@ impl AppState {
|
|||||||
node_geocache,
|
node_geocache,
|
||||||
node_delegations,
|
node_delegations,
|
||||||
bin_info: BinaryInfo::new(),
|
bin_info: BinaryInfo::new(),
|
||||||
|
instance_id: generate_instance_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +96,10 @@ impl AppState {
|
|||||||
pub(crate) fn build_information(&self) -> &BinaryBuildInformationOwned {
|
pub(crate) fn build_information(&self) -> &BinaryBuildInformationOwned {
|
||||||
&self.bin_info.build_info
|
&self.bin_info.build_info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn instance_id(&self) -> &str {
|
||||||
|
&self.instance_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GATEWAYS_LIST_KEY: &str = "gateways";
|
static GATEWAYS_LIST_KEY: &str = "gateways";
|
||||||
@@ -499,7 +505,10 @@ impl HttpCache {
|
|||||||
None => {
|
None => {
|
||||||
let new_node_stats = crate::db::queries::get_daily_stats(db)
|
let new_node_stats = crate::db::queries::get_daily_stats(db)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default()
|
.unwrap_or_else(|e| {
|
||||||
|
tracing::error!("Failed to get daily stats from database: {}", e);
|
||||||
|
vec![]
|
||||||
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.rev()
|
.rev()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -698,3 +707,14 @@ impl BinaryInfo {
|
|||||||
pub(crate) struct HealthInfo {
|
pub(crate) struct HealthInfo {
|
||||||
pub(crate) uptime: i64,
|
pub(crate) uptime: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_instance_id() -> String {
|
||||||
|
use rand::Rng;
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let random_bytes: [u8; 16] = rng.gen();
|
||||||
|
format!(
|
||||||
|
"ns-api-{}-{}",
|
||||||
|
hex::encode(&random_bytes[..8]),
|
||||||
|
std::process::id()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user