Files
nym/nym-statistics-api/src/http/api/stats.rs
T
Simon Wicky b69c2e1e94 Nym Statistics API (#5800)
* move stats types from vpn-client to here

* base stats api

* change storage schema

* add link to nymAPI for whitelisting

* remove outdated comment

* more comments update

* example of chrono vs time

* Add build.rs
- exports DATABASE_URL so cargo check works
- exports SQLX_OFFLINE for CI
- added pg_up.sh which spawns PG container
  - required for cargo sqlx prepare

* fixes time vs chrono issue and cleaner build with docker

* add correct swagger types, with feature locking where relevant

* apply dynco suggestions

---------

Co-authored-by: dynco-nym <173912580+dynco-nym@users.noreply.github.com>
2025-05-28 10:23:11 +02:00

64 lines
1.8 KiB
Rust

use std::net::SocketAddr;
use axum::{
extract::{ConnectInfo, State},
Json, Router,
};
use axum_extra::{headers::UserAgent, TypedHeader};
use nym_statistics_common::report::vpn_client::VpnClientStatsReport;
use tracing::debug;
use crate::{
http::{
error::{HttpError, HttpResult},
state::AppState,
},
storage::models::{ConnectionInfoDto, DailyActiveDeviceDto},
};
pub(crate) fn routes() -> Router<AppState> {
Router::new().route("/report", axum::routing::post(submit_stats_report))
}
#[utoipa::path(
post,
request_body = VpnClientStatsReport,
tag = "Stats",
path = "/report",
context_path = "/v1/stats",
responses(
(status = 200)
)
)]
#[tracing::instrument(level = "info", skip_all)]
async fn submit_stats_report(
State(mut state): State<AppState>,
ConnectInfo(addr): ConnectInfo<SocketAddr>,
TypedHeader(user_agent): TypedHeader<UserAgent>,
Json(report): Json<VpnClientStatsReport>,
) -> HttpResult<Json<()>> {
let now = time::OffsetDateTime::now_utc();
let gateway_record = state.network_view().get_country_by_ip(&addr.ip()).await;
let from_mixnet = gateway_record.is_some();
let maybe_location = gateway_record.unwrap_or_default();
if from_mixnet {
debug!("Received a report from the network");
} else {
debug!("Received a report from outside of the network");
}
let active_device = DailyActiveDeviceDto::new(now, &report, user_agent, from_mixnet);
let maybe_connection_info =
ConnectionInfoDto::maybe_new(now, &report, addr, maybe_location, from_mixnet);
state
.storage()
.store_vpn_client_report(active_device, maybe_connection_info)
.await
.map_err(HttpError::internal_with_logging)?;
Ok(Json(()))
}