Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70fb7d75d6 | |||
| f11b2caeb1 | |||
| 1a07dbb09c | |||
| 9587247536 |
Generated
+2
-1
@@ -6768,7 +6768,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "3.2.2"
|
||||
version = "3.2.6"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@@ -6780,6 +6780,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"itertools 0.14.0",
|
||||
"moka",
|
||||
"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]
|
||||
name = "nym-node-status-api"
|
||||
version = "3.2.2"
|
||||
version = "3.2.6"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -22,6 +22,7 @@ clap = { workspace = true, features = ["cargo", "derive", "env", "string"] }
|
||||
cosmwasm-std = { workspace = true }
|
||||
envy = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
moka = { workspace = true, features = ["future"] }
|
||||
# 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)
|
||||
}
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailyStats>> {
|
||||
let mut conn = pool.acquire().await?;
|
||||
let items = sqlx::query_as!(
|
||||
@@ -142,6 +143,48 @@ pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailySt
|
||||
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>> {
|
||||
let mut conn = pool.acquire().await?;
|
||||
let items = crate::db::query(
|
||||
|
||||
@@ -6,7 +6,7 @@ use tower_http::cors::CorsLayer;
|
||||
use utoipa::OpenApi;
|
||||
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 gateways;
|
||||
@@ -63,17 +63,23 @@ impl RouterBuilder {
|
||||
|
||||
pub(crate) fn with_state(self, state: AppState) -> 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
|
||||
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
|
||||
.layer(setup_cors())
|
||||
// logger should be outermost layer
|
||||
.layer(axum::middleware::from_fn(log_request_debug))
|
||||
.with_state(state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,12 +133,4 @@ mod tests {
|
||||
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(())
|
||||
}
|
||||
|
||||
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<()> {
|
||||
// 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_docs;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod middleware;
|
||||
pub(crate) mod models;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod state;
|
||||
|
||||
@@ -34,6 +34,7 @@ pub(crate) struct AppState {
|
||||
node_geocache: NodeGeoCache,
|
||||
node_delegations: Arc<RwLock<DelegationsCache>>,
|
||||
bin_info: BinaryInfo,
|
||||
instance_id: String,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -53,6 +54,7 @@ impl AppState {
|
||||
node_geocache,
|
||||
node_delegations,
|
||||
bin_info: BinaryInfo::new(),
|
||||
instance_id: generate_instance_id(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +96,10 @@ impl AppState {
|
||||
pub(crate) fn build_information(&self) -> &BinaryBuildInformationOwned {
|
||||
&self.bin_info.build_info
|
||||
}
|
||||
|
||||
pub(crate) fn instance_id(&self) -> &str {
|
||||
&self.instance_id
|
||||
}
|
||||
}
|
||||
|
||||
static GATEWAYS_LIST_KEY: &str = "gateways";
|
||||
@@ -499,7 +505,10 @@ impl HttpCache {
|
||||
None => {
|
||||
let new_node_stats = crate::db::queries::get_daily_stats(db)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.unwrap_or_else(|e| {
|
||||
tracing::error!("Failed to get daily stats from database: {}", e);
|
||||
vec![]
|
||||
})
|
||||
.into_iter()
|
||||
.rev()
|
||||
.collect::<Vec<_>>();
|
||||
@@ -698,3 +707,14 @@ impl BinaryInfo {
|
||||
pub(crate) struct HealthInfo {
|
||||
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