diff --git a/src/api/api_utils.rs b/src/api/api_utils.rs index af76da285..d337a89ef 100644 --- a/src/api/api_utils.rs +++ b/src/api/api_utils.rs @@ -24,7 +24,7 @@ use crate::tools::lru_cache::LRUResourceCache; use crate::utils::file::file_utils::create_new_file_for_write; use crate::utils::network::request; use crate::utils::network::request::{extract_extension_from_url, replace_url_extension, sanitize_sensitive_info}; -use crate::utils::{debug_if_enabled, trace_if_enabled}; +use crate::utils::{debug_if_enabled, sys_utils, trace_if_enabled}; use axum::http::HeaderMap; use axum::response::IntoResponse; use futures::{StreamExt, TryStreamExt}; @@ -35,6 +35,7 @@ use std::collections::HashMap; use std::io::BufWriter; use std::path::Path; use std::sync::Arc; +use chrono::{DateTime, Utc}; use tokio::sync::Mutex; use url::Url; @@ -78,6 +79,21 @@ macro_rules! try_result_bad_request { pub use try_option_bad_request; pub use try_result_bad_request; +use crate::BUILD_TIMESTAMP; +use crate::utils::size_utils::human_readable_byte_size; + +pub fn get_server_time() -> String { + chrono::offset::Local::now().with_timezone(&chrono::Local).format("%Y-%m-%d %H:%M:%S %Z").to_string() +} + +pub fn get_build_time() -> Option { + BUILD_TIMESTAMP.to_string().parse::>().ok().map(|datetime| datetime.format("%Y-%m-%d %H:%M:%S %Z").to_string()) +} + +pub fn get_memory_usage() -> String { + sys_utils::get_memory_usage().map_or(String::from("?"), human_readable_byte_size) +} + #[allow(clippy::missing_panics_doc)] pub async fn serve_file(file_path: &Path, mime_type: mime::Mime) -> impl axum::response::IntoResponse + Send { diff --git a/src/api/endpoints/v1_api.rs b/src/api/endpoints/v1_api.rs index cdc0f5389..495a84643 100644 --- a/src/api/endpoints/v1_api.rs +++ b/src/api/endpoints/v1_api.rs @@ -16,9 +16,11 @@ use crate::utils::network::request::sanitize_sensitive_info; use axum::response::IntoResponse; use log::error; use serde_json::{json}; -use std::collections::{HashSet}; +use std::collections::{BTreeMap, HashSet}; use std::sync::Arc; use crate::api::endpoints::api_playlist_utils::{get_playlist, get_playlist_for_target}; +use crate::model::healthcheck::StatusCheck; +use crate::VERSION; fn intern_save_config_api_proxy(backup_dir: &str, api_proxy: &ApiProxyConfig, file_path: &str) -> Option { match config_reader::save_api_proxy(file_path, backup_dir, api_proxy) { @@ -296,9 +298,48 @@ async fn config( axum::response::Json(result).into_response() } + +async fn create_status_check(app_state: &Arc) -> StatusCheck { + let cache = match app_state.cache.as_ref().as_ref() { + None => None, + Some(lock) => { + Some(lock.lock().await.get_size_text()) + } + }; + let (active_users, active_user_connections) = { + let active_user = &app_state.active_users; + (active_user.active_users().await, active_user.active_connections().await) + }; + + let active_provider_connections = app_state.active_provider.active_connections().await.map(|c| c.into_iter().collect::>()); + + StatusCheck { + status: "ok".to_string(), + version: VERSION.to_string(), + build_time: crate::api::api_utils::get_build_time(), + server_time: crate::api::api_utils::get_server_time(), + memory: crate::api::api_utils::get_memory_usage(), + active_users, + active_user_connections, + active_provider_connections, + cache, + } +} +#[axum::debug_handler] +async fn status(axum::extract::State(app_state): axum::extract::State>) -> axum::response::Response { + let status = create_status_check(&app_state).await; + match serde_json::to_string_pretty(&status) { + Ok(pretty_json) => axum::response::Response::builder().status(axum::http::StatusCode::OK) + .header(axum::http::header::CONTENT_TYPE, mime::APPLICATION_JSON.to_string()).body(pretty_json).unwrap().into_response(), + Err(_) => axum::Json(status).into_response(), + } +} + pub fn v1_api_register(web_auth_enabled: bool, app_state: Arc, web_ui_path: &str) -> axum::Router> { let mut router = axum::Router::new(); - router = router.route("/config", axum::routing::get(config)) + router = router + .route("/status", axum::routing::get(status)) + .route("/config", axum::routing::get(config)) .route("/config/main", axum::routing::post(save_config_main)) .route("/config/user", axum::routing::post(save_config_api_proxy_user)) .route("/config/apiproxy", axum::routing::post(save_config_api_proxy_config)) diff --git a/src/api/main_api.rs b/src/api/main_api.rs index 3b841f237..0df5d5f8c 100644 --- a/src/api/main_api.rs +++ b/src/api/main_api.rs @@ -12,17 +12,11 @@ use crate::api::model::download::DownloadQueue; use crate::api::model::streams::shared_stream_manager::SharedStreamManager; use crate::api::scheduler::start_scheduler; use crate::model::config::{validate_targets, Config, ProcessTargets, RateLimitConfig, ScheduleConfig}; -use crate::model::healthcheck::{Healthcheck, StatusCheck}; +use crate::model::healthcheck::{Healthcheck}; use crate::processing::processor::playlist; use crate::tools::lru_cache::LRUResourceCache; -use crate::utils::size_utils::human_readable_byte_size; -use crate::utils::sys_utils; -use crate::{BUILD_TIMESTAMP, VERSION}; -use axum::response::IntoResponse; -use chrono::{DateTime, Utc}; use log::{error, info}; use reqwest::Client; -use std::collections::BTreeMap; use std::future::IntoFuture; use std::io::ErrorKind; use std::net::SocketAddr; @@ -32,6 +26,8 @@ use std::time::Duration; use axum::Router; use tokio::sync::Mutex; use tower_governor::key_extractor::SmartIpKeyExtractor; +use crate::api::api_utils::{get_build_time, get_server_time}; +use crate::VERSION; fn get_web_dir_path(web_ui_enabled: bool, web_root: &str) -> Result { let web_dir = web_root.to_string(); @@ -44,52 +40,12 @@ fn get_web_dir_path(web_ui_enabled: bool, web_root: &str) -> Result String { - chrono::offset::Local::now().with_timezone(&chrono::Local).format("%Y-%m-%d %H:%M:%S %Z").to_string() -} - -fn get_build_time() -> Option { - BUILD_TIMESTAMP.to_string().parse::>().ok().map(|datetime| datetime.format("%Y-%m-%d %H:%M:%S %Z").to_string()) -} - -fn get_memory_usage() -> String { - sys_utils::get_memory_usage().map_or(String::from("?"), human_readable_byte_size) -} - fn create_healthcheck() -> Healthcheck { Healthcheck { status: "ok".to_string(), version: VERSION.to_string(), build_time: get_build_time(), server_time: get_server_time(), - memory: get_memory_usage(), - } -} - -async fn create_status_check(app_state: &Arc) -> StatusCheck { - let cache = match app_state.cache.as_ref().as_ref() { - None => None, - Some(lock) => { - Some(lock.lock().await.get_size_text()) - } - }; - let (active_users, active_user_connections) = { - let active_user = &app_state.active_users; - (active_user.active_users().await, active_user.active_connections().await) - }; - - let active_provider_connections = app_state.active_provider.active_connections().await.map(|c| c.into_iter().collect::>()); - - StatusCheck { - status: "ok".to_string(), - version: VERSION.to_string(), - build_time: get_build_time(), - server_time: get_server_time(), - memory: get_memory_usage(), - active_users, - active_user_connections, - active_provider_connections, - cache, } } @@ -97,16 +53,6 @@ async fn healthcheck() -> impl axum::response::IntoResponse { axum::Json(create_healthcheck()) } -#[axum::debug_handler] -async fn status(axum::extract::State(app_state): axum::extract::State>) -> axum::response::Response { - let status = create_status_check(&app_state).await; - match serde_json::to_string_pretty(&status) { - Ok(pretty_json) => axum::response::Response::builder().status(axum::http::StatusCode::OK) - .header(axum::http::header::CONTENT_TYPE, mime::APPLICATION_JSON.to_string()).body(pretty_json).unwrap().into_response(), - Err(_) => axum::Json(status).into_response(), - } -} - async fn create_shared_data(cfg: &Arc) -> AppState { let lru_cache = cfg.reverse_proxy.as_ref().and_then(|r| r.cache.as_ref()).and_then(|c| if c.enabled { Some(Mutex::new(LRUResourceCache::new(c.t_size, &PathBuf::from(c.dir.as_ref().unwrap())))) @@ -125,14 +71,13 @@ async fn create_shared_data(cfg: &Arc) -> AppState { let active_users = Arc::new(ActiveUserManager::new(cfg.log.as_ref().is_some_and(|l| l.log_active_user))); let active_provider = Arc::new(ActiveProviderManager::new(cfg).await); - let client = if cfg.connect_timeout_secs > 0 { - Client::builder() - .connect_timeout(Duration::from_secs(u64::from(cfg.connect_timeout_secs))) - .build().unwrap_or_else(|_| Client::new()) - } else { - Client::new() + let mut builder = Client::builder(); + if cfg.connect_timeout_secs > 0 { + builder = builder.connect_timeout(Duration::from_secs(u64::from(cfg.connect_timeout_secs))) }; + let client = builder.build().unwrap_or_else(|_| Client::new()); + AppState { config: Arc::clone(cfg), http_client: Arc::new(client), @@ -298,8 +243,7 @@ pub async fn start_server(cfg: Arc, targets: Arc) -> fut // Web Server let mut router = axum::Router::new() - .route("/healthcheck", axum::routing::get(healthcheck)) - .route(&format!("{web_ui_path}/status"), axum::routing::get(status)); + .route("/healthcheck", axum::routing::get(healthcheck)); if web_ui_enabled { router = router .nest_service(&format!("{web_ui_path}/static"), tower_http::services::ServeDir::new(web_dir_path.join("static"))) @@ -321,7 +265,8 @@ pub async fn start_server(cfg: Arc, targets: Arc) -> fut api_router = add_rate_limiter(api_router, &rate_limiter); } - router = router.merge(api_router); + router = router + .merge(api_router); if web_ui_enabled && web_ui_path.is_empty() { router = router.merge(index_register_without_path(&web_dir_path)); @@ -330,7 +275,6 @@ pub async fn start_server(cfg: Arc, targets: Arc) -> fut router = router.layer(create_cors_layer()) .layer(create_compression_layer()); //router = router.layer(tower_http::trace::TraceLayer::new_for_http()); // `Logger::default()` - // router = router.layer(axum::middleware::from_fn(log_routes)); let router: axum::Router<()> = router.with_state(shared_data.clone()); diff --git a/src/model/healthcheck.rs b/src/model/healthcheck.rs index be471bdef..47d68896a 100644 --- a/src/model/healthcheck.rs +++ b/src/model/healthcheck.rs @@ -8,7 +8,6 @@ pub struct Healthcheck { #[serde(skip_serializing_if = "Option::is_none")] pub build_time: Option, pub server_time: String, - pub memory: String, } #[derive(Debug, Clone, Serialize, Deserialize)]