Rest of the data for /gateways
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{
|
||||
http::{self, models::SummaryHistory},
|
||||
utils::{decimal_to_i64, unix_timestamp_to_utc_rfc3339, NumericalCheckedCast},
|
||||
@@ -14,6 +12,7 @@ use nym_validator_client::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
use std::str::FromStr;
|
||||
use strum_macros::{EnumString, FromRepr};
|
||||
use time::{Date, OffsetDateTime};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
@@ -36,6 +36,8 @@ pub(crate) async fn get_all_nym_nodes(pool: &DbPool) -> anyhow::Result<Vec<NymNo
|
||||
bond_info as "bond_info: serde_json::Value"
|
||||
FROM
|
||||
nym_nodes
|
||||
ORDER BY
|
||||
node_id
|
||||
"#,
|
||||
)
|
||||
.fetch(&mut *conn)
|
||||
|
||||
@@ -46,20 +46,19 @@ async fn dvpn_gateways(
|
||||
state: State<AppState>,
|
||||
) -> HttpResult<Json<Vec<DVpnGateway>>> {
|
||||
let min_node_version: String = min_node_version.unwrap_or_else(|| String::from("1.6.2"));
|
||||
let min_node_version = semver::Version::parse(&min_node_version)
|
||||
let _min_node_version = semver::Version::parse(&min_node_version)
|
||||
.map_err(|_| HttpError::invalid_input("Min version must be valid semver"))?;
|
||||
|
||||
let db = state.db_pool();
|
||||
let res = state.cache().get_dvpn_gateway_list(db).await;
|
||||
|
||||
Ok(Json(res))
|
||||
Ok(Json(
|
||||
state.cache().get_dvpn_gateway_list(state.db_pool()).await,
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // clippy doesn't detect usage in utoipa macros
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
#[into_params(parameter_in = Path)]
|
||||
struct TwoLetterCountryCodeParam {
|
||||
#[param(minimum = 2, maximum = 2)]
|
||||
#[param(min_length = 2, max_length = 2)]
|
||||
two_letter_country_code: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use nym_validator_client::{
|
||||
models::{
|
||||
AuthenticatorDetails, BinaryBuildInformationOwned, IpPacketRouterDetails, NymNodeData,
|
||||
},
|
||||
nym_api::SkimmedNode,
|
||||
nym_nodes::{BasicEntryInformation, NodeRole},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -143,19 +144,9 @@ pub mod wg_outcome_versions {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Gateway> for DVpnGateway {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &Gateway) -> Result<Self, Self::Error> {
|
||||
Self::try_from(value.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Gateway> for DVpnGateway {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: Gateway) -> Result<Self, Self::Error> {
|
||||
let location = value
|
||||
impl DVpnGateway {
|
||||
pub(crate) fn new(gateway: Gateway, skimmed_node: &SkimmedNode) -> anyhow::Result<Self> {
|
||||
let location = gateway
|
||||
.explorer_pretty_bond
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing explorer_pretty_bond"))
|
||||
.and_then(|value| {
|
||||
@@ -163,50 +154,18 @@ impl TryFrom<Gateway> for DVpnGateway {
|
||||
})
|
||||
.map(|bond| bond.location)?;
|
||||
|
||||
let self_described = value
|
||||
let self_described = gateway
|
||||
.self_described
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing self_described"))
|
||||
.and_then(|value| serde_json::from_value::<NymNodeData>(value).map_err(From::from))?;
|
||||
|
||||
let last_probe_result = value
|
||||
let last_probe_result = gateway
|
||||
.last_probe_result
|
||||
.and_then(|value| serde_json::from_value::<LastProbeResult>(value).ok());
|
||||
|
||||
// TODO: polyfill `last_probe`, see below from VPN API
|
||||
/*
|
||||
const last_testrun_utc = item.last_testrun_utc;
|
||||
|
||||
const last_probe: DirectoryGatewayProbe | undefined =
|
||||
last_testrun_utc && item.last_probe_result?.outcome
|
||||
? {
|
||||
last_updated_utc: last_testrun_utc,
|
||||
outcome: item.last_probe_result.outcome,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
//
|
||||
// reshape test probe
|
||||
//
|
||||
if (
|
||||
last_probe?.outcome?.wg &&
|
||||
isDirectoryGatewayProbeOutcome_WG_V2(last_probe?.outcome?.wg) &&
|
||||
last_probe?.outcome?.wg?.can_handshake === undefined
|
||||
) {
|
||||
last_probe.outcome.wg = {
|
||||
...last_probe.outcome.wg,
|
||||
can_handshake: last_probe.outcome.wg.can_handshake_v4,
|
||||
can_resolve_dns: last_probe.outcome.wg.can_resolve_dns_v4,
|
||||
ping_hosts_performance:
|
||||
last_probe.outcome.wg.ping_hosts_performance_v4,
|
||||
ping_ips_performance: last_probe.outcome.wg.ping_ips_performance_v4,
|
||||
};
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
Ok(Self {
|
||||
identity_key: value.gateway_identity_key,
|
||||
name: value.description.moniker,
|
||||
identity_key: gateway.gateway_identity_key,
|
||||
name: gateway.description.moniker,
|
||||
ip_packet_router: self_described.ip_packet_router,
|
||||
authenticator: self_described.authenticator,
|
||||
location: Location {
|
||||
@@ -215,12 +174,15 @@ impl TryFrom<Gateway> for DVpnGateway {
|
||||
two_letter_iso_country_code: location.two_letter_iso_country_code,
|
||||
},
|
||||
last_probe: last_probe_result.map(|res| res.outcome),
|
||||
// TODO
|
||||
ip_addresses: vec![], // value.ip_addresses (SkimmedNode),
|
||||
mix_port: 0, // value.mix_port (SkimmedNode),
|
||||
role: NodeRole::EntryGateway, // value.role (SkimmedNode),
|
||||
entry: None, // (SkimmedNode)
|
||||
performance: value.performance, // (SkimmedNode)
|
||||
ip_addresses: skimmed_node
|
||||
.ip_addresses
|
||||
.iter()
|
||||
.map(|ip| ip.to_string())
|
||||
.collect(),
|
||||
mix_port: skimmed_node.mix_port,
|
||||
role: skimmed_node.role.clone(),
|
||||
entry: skimmed_node.entry.clone(),
|
||||
performance: gateway.performance,
|
||||
build_information: self_described.build_information,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::nym_api::SkimmedNode;
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::instrument;
|
||||
use tracing::{error, instrument, warn};
|
||||
|
||||
use crate::{
|
||||
db::{queries, DbPool},
|
||||
@@ -208,47 +208,84 @@ impl HttpCache {
|
||||
None => {
|
||||
tracing::trace!("No gateways (dVPN) in cache, refreshing from DB...");
|
||||
|
||||
let mut failed_to_convert_gw_count = 0;
|
||||
let gateways: Vec<DVpnGateway> = self
|
||||
.get_gateway_list(db)
|
||||
let gateways = self.get_gateway_list(db).await;
|
||||
|
||||
let started_with = gateways.len();
|
||||
let Ok(skimmed_nodes) = crate::db::queries::get_described_bonded_nym_nodes(db)
|
||||
.await
|
||||
.map(|records| {
|
||||
records
|
||||
.into_iter()
|
||||
.filter_map(|dto| SkimmedNode::try_from(dto).ok())
|
||||
.map(|skimmed_node| {
|
||||
(
|
||||
skimmed_node.ed25519_identity_pubkey.to_base58_string(),
|
||||
skimmed_node,
|
||||
)
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
})
|
||||
.inspect_err(|err| {
|
||||
// this would fail only in case of internal error: log and return gracefully
|
||||
error!("Failed to get nym_nodes from DB: {err}");
|
||||
})
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let res_gws = gateways
|
||||
.into_iter()
|
||||
.filter(|gw| gw.bonded)
|
||||
.filter_map(|d| {
|
||||
d.try_into()
|
||||
.inspect_err(|_| failed_to_convert_gw_count += 1)
|
||||
.ok()
|
||||
.filter_map(|gw| match skimmed_nodes.get(&gw.gateway_identity_key) {
|
||||
Some(skimmed_node) => Some((gw, skimmed_node)),
|
||||
None => {
|
||||
warn!(
|
||||
"No SkimmedNode data found for GW, identity_key={}",
|
||||
gw.gateway_identity_key
|
||||
);
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|g: &DVpnGateway| {
|
||||
.filter_map(
|
||||
|(gw, skimmed_node)| match DVpnGateway::new(gw, skimmed_node) {
|
||||
Ok(gw) => Some(gw),
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to convert GW node_id={} to dVPN form: {}",
|
||||
skimmed_node.node_id, err
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.filter(|gw| {
|
||||
// gateways must have a country
|
||||
g.location.two_letter_iso_country_code.len() == 2
|
||||
if gw.location.two_letter_iso_country_code.len() == 2 {
|
||||
true
|
||||
} else {
|
||||
warn!(
|
||||
"Invalid country code: {}",
|
||||
gw.location.two_letter_iso_country_code
|
||||
);
|
||||
false
|
||||
}
|
||||
})
|
||||
// sort by country, then by identity key
|
||||
.sorted_by_key(|item| {
|
||||
// for some reason, closure on this owned iterator takes
|
||||
// reference, but returns owned element, which means
|
||||
// we have to clone despite having access to owned data
|
||||
(
|
||||
item.identity_key.clone(),
|
||||
item.location.two_letter_iso_country_code.clone(),
|
||||
item.identity_key.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if failed_to_convert_gw_count > 0 {
|
||||
tracing::error!(
|
||||
"Failed to convert {} DB gateways to dVPN form",
|
||||
failed_to_convert_gw_count
|
||||
);
|
||||
}
|
||||
|
||||
if gateways.is_empty() {
|
||||
tracing::warn!("Database contains 0 gateways");
|
||||
if res_gws.is_empty() && started_with > 0 {
|
||||
tracing::warn!("Started with {}, got 0 gateways", started_with);
|
||||
} else {
|
||||
self.upsert_dvpn_gateway_list(gateways.clone()).await;
|
||||
self.upsert_dvpn_gateway_list(res_gws.clone()).await;
|
||||
}
|
||||
|
||||
gateways
|
||||
res_gws
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,8 @@ impl Monitor {
|
||||
.await
|
||||
.log_error("get_all_basic_nodes")?;
|
||||
|
||||
tracing::info!("🟣 get_all_basic_nodes: {}", nym_nodes.len());
|
||||
let nym_node_count = nym_nodes.len();
|
||||
tracing::info!("🟣 get_all_basic_nodes: {}", nym_node_count);
|
||||
|
||||
let nym_node_records =
|
||||
self.prepare_nym_node_data(nym_nodes.clone(), &bonded_nym_nodes, &described_nodes);
|
||||
@@ -252,7 +253,7 @@ impl Monitor {
|
||||
//
|
||||
|
||||
let nodes_summary = vec![
|
||||
(NYMNODE_COUNT, nym_nodes.len()),
|
||||
(NYMNODE_COUNT, nym_node_count),
|
||||
(ASSIGNED_MIXING_COUNT, assigned_mixing_count),
|
||||
(MIXNODES_LEGACY_COUNT, count_legacy_mixnodes),
|
||||
(NYMNODES_DESCRIBED_COUNT, described_nodes.len()),
|
||||
@@ -268,7 +269,7 @@ impl Monitor {
|
||||
let last_updated = now_utc();
|
||||
let last_updated_utc = last_updated.unix_timestamp().to_string();
|
||||
let network_summary = NetworkSummary {
|
||||
total_nodes: nym_nodes.len().cast_checked()?,
|
||||
total_nodes: nym_node_count.cast_checked()?,
|
||||
mixnodes: mixnode::MixnodeSummary {
|
||||
bonded: mixnode::MixingNodesSummary {
|
||||
count: assigned_mixing_count.cast_checked()?,
|
||||
|
||||
Reference in New Issue
Block a user