diff --git a/src/apis/routes.rs b/src/apis/routes.rs index 2db23c35..1b40ac47 100644 --- a/src/apis/routes.rs +++ b/src/apis/routes.rs @@ -1,7 +1,65 @@ +use std::sync::Arc; + +use axum::extract::State; use axum::response::Json; use serde_json::{json, Value}; +use crate::api::resource::stats::Stats; +use crate::tracker::Tracker; + #[allow(clippy::unused_async)] pub async fn root() -> Json { Json(json!({ "data": 42 })) } + +#[allow(clippy::unused_async)] +pub async fn get_stats(State(tracker): State>) -> Json { + let mut results = Stats { + torrents: 0, + seeders: 0, + completed: 0, + leechers: 0, + tcp4_connections_handled: 0, + tcp4_announces_handled: 0, + tcp4_scrapes_handled: 0, + tcp6_connections_handled: 0, + tcp6_announces_handled: 0, + tcp6_scrapes_handled: 0, + udp4_connections_handled: 0, + udp4_announces_handled: 0, + udp4_scrapes_handled: 0, + udp6_connections_handled: 0, + udp6_announces_handled: 0, + udp6_scrapes_handled: 0, + }; + + let db = tracker.get_torrents().await; + + db.values().for_each(|torrent_entry| { + let (seeders, completed, leechers) = torrent_entry.get_stats(); + results.seeders += seeders; + results.completed += completed; + results.leechers += leechers; + results.torrents += 1; + }); + + let stats = tracker.get_stats().await; + + #[allow(clippy::cast_possible_truncation)] + { + results.tcp4_connections_handled = stats.tcp4_connections_handled as u32; + results.tcp4_announces_handled = stats.tcp4_announces_handled as u32; + results.tcp4_scrapes_handled = stats.tcp4_scrapes_handled as u32; + results.tcp6_connections_handled = stats.tcp6_connections_handled as u32; + results.tcp6_announces_handled = stats.tcp6_announces_handled as u32; + results.tcp6_scrapes_handled = stats.tcp6_scrapes_handled as u32; + results.udp4_connections_handled = stats.udp4_connections_handled as u32; + results.udp4_announces_handled = stats.udp4_announces_handled as u32; + results.udp4_scrapes_handled = stats.udp4_scrapes_handled as u32; + results.udp6_connections_handled = stats.udp6_connections_handled as u32; + results.udp6_announces_handled = stats.udp6_announces_handled as u32; + results.udp6_scrapes_handled = stats.udp6_scrapes_handled as u32; + } + + Json(json!(results)) +} diff --git a/src/apis/server.rs b/src/apis/server.rs index d42ae895..fb532519 100644 --- a/src/apis/server.rs +++ b/src/apis/server.rs @@ -6,11 +6,13 @@ use axum::Router; use futures::Future; use warp::hyper; -use super::routes::root; +use super::routes::{get_stats, root}; use crate::tracker; -pub fn start(socket_addr: SocketAddr, _tracker: &Arc) -> impl Future> { - let app = Router::new().route("/", get(root)); +pub fn start(socket_addr: SocketAddr, tracker: &Arc) -> impl Future> { + let app = Router::new() + .route("/", get(root)) + .route("/stats", get(get_stats).with_state(tracker.clone())); let server = axum::Server::bind(&socket_addr).serve(app.into_make_service()); diff --git a/tests/tracker_api.rs b/tests/tracker_api.rs index 5f022167..bac9d132 100644 --- a/tests/tracker_api.rs +++ b/tests/tracker_api.rs @@ -582,18 +582,69 @@ mod tracker_apis { mod for_entrypoint { use crate::api::client::{Client, Query}; - use crate::api::server::start_default_api_server; + use crate::api::server::start_default_api; use crate::api::Version; #[tokio::test] async fn test_entrypoint() { - let api_server = start_default_api_server(&Version::Axum).await; + let api_server = start_default_api(&Version::Axum).await; let response = Client::new(api_server.get_connection_info(), &Version::Axum) - .get("/", Query::default()) + .get("", Query::default()) .await; assert_eq!(response.status(), 200); } } + + mod for_stats_resources { + use std::str::FromStr; + + use torrust_tracker::api::resource::stats::Stats; + use torrust_tracker::protocol::info_hash::InfoHash; + + use crate::api::client::Client; + use crate::api::fixtures::sample_peer; + use crate::api::server::start_default_api; + use crate::api::Version; + + #[tokio::test] + async fn should_allow_getting_tracker_statistics() { + let api_server = start_default_api(&Version::Axum).await; + + api_server + .add_torrent( + &InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(), + &sample_peer(), + ) + .await; + + let response = Client::new(api_server.get_connection_info(), &Version::Axum) + .get_tracker_statistics() + .await; + + assert_eq!(response.status(), 200); + assert_eq!( + response.json::().await.unwrap(), + Stats { + torrents: 1, + seeders: 1, + completed: 0, + leechers: 0, + tcp4_connections_handled: 0, + tcp4_announces_handled: 0, + tcp4_scrapes_handled: 0, + tcp6_connections_handled: 0, + tcp6_announces_handled: 0, + tcp6_scrapes_handled: 0, + udp4_connections_handled: 0, + udp4_announces_handled: 0, + udp4_scrapes_handled: 0, + udp6_connections_handled: 0, + udp6_announces_handled: 0, + udp6_scrapes_handled: 0, + } + ); + } + } }