From 7ed0657eb543610776090467928930cd8955bd65 Mon Sep 17 00:00:00 2001 From: dynco-nym <173912580+dynco-nym@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:36:35 +0100 Subject: [PATCH] Add remaining bandwidth log --- .../nym-node-status-api/src/main.rs | 3 +- .../src/monitor/geodata.rs | 55 ++++++++++++++++++- .../nym-node-status-api/src/monitor/mod.rs | 22 +++++++- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/nym-node-status-api/nym-node-status-api/src/main.rs b/nym-node-status-api/nym-node-status-api/src/main.rs index e332f28fe7..6274a01fe5 100644 --- a/nym-node-status-api/nym-node-status-api/src/main.rs +++ b/nym-node-status-api/nym-node-status-api/src/main.rs @@ -29,14 +29,13 @@ async fn main() -> anyhow::Result<()> { let db_pool = storage.pool_owned(); let args_clone = args.clone(); - let ipinfo_client = monitor::IpInfoClient::new(args.ipinfo_api_token.clone())?; tokio::spawn(async move { monitor::spawn_in_background( db_pool, args_clone.nym_api_client_timeout, args_clone.nyxd_addr, args_clone.monitor_refresh_interval, - ipinfo_client, + args_clone.ipinfo_api_token, args_clone.geodata_ttl, ) .await; diff --git a/nym-node-status-api/nym-node-status-api/src/monitor/geodata.rs b/nym-node-status-api/nym-node-status-api/src/monitor/geodata.rs index ab9837420d..1042b15cce 100644 --- a/nym-node-status-api/nym-node-status-api/src/monitor/geodata.rs +++ b/nym-node-status-api/nym-node-status-api/src/monitor/geodata.rs @@ -7,11 +7,11 @@ pub(crate) struct IpInfoClient { } impl IpInfoClient { - pub(crate) fn new(token: impl Into) -> anyhow::Result { + pub(crate) fn new(token: impl Into) -> Self { let client = reqwest::Client::new(); let token = token.into(); - Ok(Self { client, token }) + Self { client, token } } pub(crate) async fn locate_ip(&self, ip: impl AsRef) -> anyhow::Result { @@ -33,12 +33,35 @@ impl IpInfoClient { } anyhow::Error::from(err) })?; - let response_text = response.text().await?; + let response_text = response.text().await?.trim().to_string(); Ok(Location { two_letter_iso_country_code: response_text, }) } + + /// check DOESN'T consume bandwidth allowance + pub(crate) async fn check_remaining_bandwidth( + &self, + ) -> anyhow::Result { + let url = format!("https://ipinfo.io/me?token={}", &self.token); + let response = self + .client + .get(url) + .send() + .await + // map non 2xx responses to error + .and_then(|res| res.error_for_status()) + .map_err(|err| { + if matches!(err.status(), Some(reqwest::StatusCode::TOO_MANY_REQUESTS)) { + tracing::error!("ipinfo rate limit exceeded"); + } + anyhow::Error::from(err) + })?; + let response: ipinfo::MeResponse = response.json().await?; + + Ok(response.requests) + } } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -61,3 +84,29 @@ impl Location { } } } + +pub(crate) mod ipinfo { + use super::*; + + // clippy doesn't understand it's used for typed deserialization + #[allow(dead_code)] + #[derive(Debug, Clone, Deserialize)] + /// `/me` is undocumented in their developers page + /// https://ipinfo.io/developers/responses + /// but explained here + /// https://community.ipinfo.io/t/easy-way-to-check-allowance-usage/5755/2 + pub(crate) struct MeResponse { + token: String, + pub(crate) requests: MeResponseRequests, + } + + // clippy doesn't understand it's used for typed deserialization + #[allow(dead_code)] + #[derive(Debug, Clone, Deserialize)] + pub(crate) struct MeResponseRequests { + pub(crate) day: u64, + pub(crate) month: u64, + pub(crate) limit: u64, + pub(crate) remaining: u64, + } +} diff --git a/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs index 5d75953d37..459992b0b3 100644 --- a/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs @@ -52,10 +52,11 @@ pub(crate) async fn spawn_in_background( nym_api_client_timeout: Duration, nyxd_addr: Url, refresh_interval: Duration, - ipinfo: IpInfoClient, + ipinfo_api_token: String, geodata_ttl: Duration, ) { let geocache = Cache::builder().time_to_live(geodata_ttl).build(); + let ipinfo = IpInfoClient::new(ipinfo_api_token.clone()); let mut monitor = Monitor { db_pool, network_details: nym_network_defaults::NymNetworkDetails::new_from_env(), @@ -87,6 +88,8 @@ pub(crate) async fn spawn_in_background( impl Monitor { async fn run(&mut self) -> anyhow::Result<()> { + self.check_ipinfo_bandwidth().await; + let default_api_url = self .network_details .endpoints @@ -322,7 +325,7 @@ impl Monitor { Ok(()) } - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip_all)] async fn location_cached(&mut self, node: &NymNodeDescription) -> Location { let node_id = node.node_id; @@ -433,6 +436,21 @@ impl Monitor { Ok(mixnode_records) } + + async fn check_ipinfo_bandwidth(&self) { + match self.ipinfo.check_remaining_bandwidth().await { + Ok(bandwidth) => { + tracing::info!( + "ipinfo monthly bandwidth: {}/{} spent", + bandwidth.month, + bandwidth.limit + ); + } + Err(err) => { + tracing::debug!("Couldn't check ipinfo bandwidth: {}", err); + } + } + } } // TODO dz is there a common monorepo place this can be put?