diff --git a/backend/src/api/endpoints/v1_api.rs b/backend/src/api/endpoints/v1_api.rs index 83946ed74..c2de3ed28 100644 --- a/backend/src/api/endpoints/v1_api.rs +++ b/backend/src/api/endpoints/v1_api.rs @@ -16,7 +16,7 @@ use log::error; use serde_json::json; use shared::error::TuliproxError; use shared::model::{ApiProxyConfigDto, ApiProxyServerInfoDto, ConfigDto, InputType, IpCheckDto, PlaylistRequest, PlaylistRequestType, StatusCheck, TargetUserDto, XtreamPlaylistItem}; -use shared::utils::{concat_path, sanitize_sensitive_info}; +use shared::utils::{concat_path_leading_slash, sanitize_sensitive_info}; use std::collections::{BTreeMap, HashSet}; use std::sync::Arc; use crate::utils::prepare_sources_batch; @@ -349,5 +349,5 @@ pub fn v1_api_register(web_auth_enabled: bool, app_state: Arc, web_ui_ if config.web_ui.as_ref().is_none_or(|c| c.user_ui_enabled) { base_router = base_router.merge(user_api_register(app_state)); } - base_router.nest(&concat_path(web_ui_path, "api/v1"), router) + base_router.nest(&concat_path_leading_slash(web_ui_path, "api/v1"), router) } diff --git a/backend/src/api/endpoints/web_index.rs b/backend/src/api/endpoints/web_index.rs index 60c083135..b1ead611d 100644 --- a/backend/src/api/endpoints/web_index.rs +++ b/backend/src/api/endpoints/web_index.rs @@ -6,7 +6,7 @@ use axum::response::IntoResponse; use log::error; use serde_json::json; use shared::model::{TokenResponse, UserCredential}; -use shared::utils::{concat_path, CONSTANTS}; +use shared::utils::{concat_path_leading_slash, CONSTANTS}; use std::path::{Path, PathBuf}; use std::sync::Arc; use tower::Service; @@ -107,10 +107,10 @@ async fn index( match tokio::fs::read_to_string(&path).await { Ok(content) => { let mut new_content = CONSTANTS.re_base_href.replace_all(&content, |caps: ®ex::Captures| { - format!(r#"{}="/{}""#, &caps[1], concat_path(&web_ui_path, &caps[2]).trim_start_matches('/')) + format!(r#"{}="{}""#, &caps[1], concat_path_leading_slash(web_ui_path, &caps[2])) }).to_string(); - let base_href = format!(r#""#, web_ui_path.trim_matches('/')); + let base_href = format!(r#""#); if let Some(pos) = new_content.find("") { new_content.replace_range(pos..pos + 6, &base_href); } @@ -139,13 +139,13 @@ async fn index_config( if let Some(api) = json_data.get_mut("api") { if let Some(api_url) = api.get_mut("apiUrl") { if let Some(url) = api_url.as_str() { - let new_url = concat_path(web_ui_path, url); + let new_url = concat_path_leading_slash(web_ui_path, url); *api_url = json!(new_url); } } if let Some(auth_url) = api.get_mut("authUrl") { if let Some(url) = auth_url.as_str() { - let new_url = concat_path(web_ui_path, url); + let new_url = concat_path_leading_slash(web_ui_path, url); *auth_url = json!(new_url); } } @@ -177,12 +177,12 @@ pub fn index_register_without_path(web_dir_path: &Path) -> axum::Router axum::Router> { axum::Router::new() - .nest(&concat_path(web_ui_path, "auth"), axum::Router::new() + .nest(&concat_path_leading_slash(web_ui_path, "auth"), axum::Router::new() .route("/token", axum::routing::post(token)) .route("/refresh", axum::routing::post(token_refresh))) .merge(axum::Router::new() - .route(&format!("/{}/", web_ui_path.trim_matches('/')), axum::routing::get(index)) - .route(&concat_path(web_ui_path, "config.json"), axum::routing::get(index_config)) + .route(&format!("/{web_ui_path}/"), axum::routing::get(index)) + .route(&concat_path_leading_slash(web_ui_path, "config.json"), axum::routing::get(index_config)) .fallback({ let mut serve_dir = tower_http::services::ServeDir::new(web_dir_path); let path_prefix = web_ui_path.to_string(); diff --git a/backend/src/api/endpoints/websocket_api.rs b/backend/src/api/endpoints/websocket_api.rs index 2eafa8edf..6219e2baf 100644 --- a/backend/src/api/endpoints/websocket_api.rs +++ b/backend/src/api/endpoints/websocket_api.rs @@ -10,7 +10,7 @@ use axum::{ use log::{error, info}; use shared::model::{ProtocolHandler, ProtocolHandlerMemory, ProtocolMessage, UserRole, WsCloseCode, PROTOCOL_VERSION}; use std::sync::Arc; -use shared::utils::concat_path; +use shared::utils::{concat_path_leading_slash}; // WebSocket upgrade handler async fn websocket_handler( @@ -33,12 +33,12 @@ async fn websocket_handler_auth( pub fn ws_api_register(web_auth_enabled: bool, web_ui_path: &str) -> axum::Router> { if web_auth_enabled { axum::Router::new().route( - &format!("/{}", concat_path(web_ui_path.trim_start_matches('/'), "ws")), + &concat_path_leading_slash(web_ui_path, "ws"), axum::routing::get(websocket_handler_auth), ) } else { axum::Router::new().route( - &format!("/{}", concat_path(web_ui_path.trim_start_matches('/'), "ws")), + &concat_path_leading_slash(web_ui_path, "ws"), axum::routing::get(websocket_handler), ) } diff --git a/backend/src/api/main_api.rs b/backend/src/api/main_api.rs index dd38051f3..38f6c6930 100644 --- a/backend/src/api/main_api.rs +++ b/backend/src/api/main_api.rs @@ -30,7 +30,7 @@ use std::path::PathBuf; use std::sync::Arc; use tokio_util::sync::CancellationToken; use tower_governor::key_extractor::SmartIpKeyExtractor; -use shared::utils::concat_path; +use shared::utils::{concat_path_leading_slash}; fn get_web_dir_path(web_ui_enabled: bool, web_root: &str) -> Result { let web_dir = web_root.to_string(); @@ -276,11 +276,11 @@ pub async fn start_server( if web_ui_enabled { router = router .nest_service( - &concat_path(&web_ui_path, "static"), + &concat_path_leading_slash(&web_ui_path, "static"), tower_http::services::ServeDir::new(web_dir_path.join("static")), ) .nest_service( - &concat_path(&web_ui_path, "assets"), + &concat_path_leading_slash(&web_ui_path, "assets"), tower_http::services::ServeDir::new(web_dir_path.join("assets")), ) .merge(v1_api_register( diff --git a/backend/src/api/model/event_manager.rs b/backend/src/api/model/event_manager.rs index 2b7a10f37..49e8be0f9 100644 --- a/backend/src/api/model/event_manager.rs +++ b/backend/src/api/model/event_manager.rs @@ -1,4 +1,4 @@ -use log::{error, info}; +use log::{error, info, trace}; use tokio::task; use shared::model::{ConfigType, PlaylistUpdateState}; use crate::api::model::{ActiveUserConnectionChangeReceiver}; @@ -31,13 +31,13 @@ impl EventManager { tokio::select! { Some((user_count, connection_count)) = active_user_change_rx.recv() => { if let Err(e) = channel_tx_clone.send(EventMessage::ActiveUser(user_count, connection_count)) { - error!("Failed to send active user change event: {e}"); + trace!("Failed to send active user change event: {e}"); } } Some((provider, connection_count)) = provider_change_rx.recv() => { if let Err(e) = channel_tx_clone.send(EventMessage::ActiveProvider(provider, connection_count)) { - error!("Failed to send active provider change event: {e}"); + trace!("Failed to send active provider change event: {e}"); } } else => { diff --git a/shared/src/utils/request.rs b/shared/src/utils/request.rs index b5173fa17..465f7bcd3 100644 --- a/shared/src/utils/request.rs +++ b/shared/src/utils/request.rs @@ -124,6 +124,19 @@ pub fn get_base_url_from_str(url: &str) -> Option { pub fn concat_path(first: &str, second: &str) -> String { let first = first.trim_end_matches('/'); let second = second.trim_start_matches('/'); + match (first.is_empty(), second.is_empty()) { + (true, true) => String::new(), + (true, false) => second.to_string(), + (false, true) => first.to_string(), + (false, false) => format!("{first}/{second}"), + } +} - format!("{first}/{second}") +pub fn concat_path_leading_slash(first: &str, second: &str) -> String { + let path = concat_path(first, second); + if path.is_empty() { + return path; + } + let path = path.trim_start_matches('/'); + format!("/{path}") } \ No newline at end of file diff --git a/webui/src/services/auth_service.rs b/webui/src/services/auth_service.rs index 9ff61a1bd..b4208ebff 100644 --- a/webui/src/services/auth_service.rs +++ b/webui/src/services/auth_service.rs @@ -6,7 +6,7 @@ use futures_signals::signal::Mutable; use futures_signals::signal::SignalExt; use shared::model::{TokenResponse, UserCredential}; use std::future::Future; -use shared::utils::concat_path; +use shared::utils::{concat_path, concat_path_leading_slash}; #[derive(Debug)] pub struct AuthService { @@ -19,7 +19,7 @@ impl AuthService { pub fn new() -> Self { let base_href = get_base_href(); Self { - auth_path: format!("{base_href}auth"), + auth_path: concat_path_leading_slash(&base_href, "auth"), username: RefCell::new(String::new()), auth_channel: Mutable::new(false), } diff --git a/webui/src/services/config_service.rs b/webui/src/services/config_service.rs index deeb58f27..819013adb 100644 --- a/webui/src/services/config_service.rs +++ b/webui/src/services/config_service.rs @@ -9,6 +9,7 @@ use log::error; use futures_signals::signal::Mutable; use futures_signals::signal::SignalExt; use shared::foundation::filter::{get_filter, prepare_templates}; +use shared::utils::concat_path_leading_slash; pub struct ConfigService { pub ui_config: Rc, @@ -27,8 +28,8 @@ impl ConfigService { server_config: RefCell::new(None), config_channel: Mutable::new(None), is_fetching: AtomicBool::new(false), - config_path: format!("{base_href}api/v1/config"), - ip_check_path: format!("{base_href}api/v1/ipinfo"), + config_path: concat_path_leading_slash(&base_href, "api/v1/config"), + ip_check_path: concat_path_leading_slash(&base_href, "api/v1/ipinfo"), } } diff --git a/webui/src/services/playlist_service.rs b/webui/src/services/playlist_service.rs index 313add5a9..0d0bbd761 100644 --- a/webui/src/services/playlist_service.rs +++ b/webui/src/services/playlist_service.rs @@ -2,6 +2,7 @@ use crate::services::{get_base_href, request_post}; use log::error; use shared::model::{PlaylistCategoriesResponse, PlaylistRequest, UiPlaylistCategories}; use std::rc::Rc; +use shared::utils::concat_path_leading_slash; pub struct PlaylistService { target_update_api_path: String, @@ -17,8 +18,8 @@ impl PlaylistService { pub fn new() -> Self { let base_href = get_base_href(); Self { - target_update_api_path: format!("{base_href}api/v1/playlist/update"), - playlist_api_path: format!("{base_href}api/v1/playlist"), + target_update_api_path: concat_path_leading_slash(&base_href, "api/v1/playlist/update"), + playlist_api_path: concat_path_leading_slash(&base_href, "api/v1/playlist"), } } pub async fn update_targets(&self, targets: &[&str]) -> bool { diff --git a/webui/src/services/status_service.rs b/webui/src/services/status_service.rs index c5cc12a06..98fc9f96e 100644 --- a/webui/src/services/status_service.rs +++ b/webui/src/services/status_service.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use crate::services::{get_base_href, request_get}; use shared::model::StatusCheck; +use shared::utils::concat_path_leading_slash; pub struct StatusService { status_path: String, @@ -16,7 +17,7 @@ impl StatusService { pub fn new() -> Self { let base_href = get_base_href(); Self { - status_path: format!("{base_href}api/v1/status"), + status_path: concat_path_leading_slash(&base_href, "api/v1/status"), } } diff --git a/webui/src/services/websocket_service.rs b/webui/src/services/websocket_service.rs index af10d5760..1934ddf4b 100644 --- a/webui/src/services/websocket_service.rs +++ b/webui/src/services/websocket_service.rs @@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use web_sys::js_sys::{Uint8Array, ArrayBuffer}; use log::{debug, error, trace}; use shared::model::{ProtocolMessage, PROTOCOL_VERSION}; -use shared::utils::concat_path; +use shared::utils::{concat_path_leading_slash}; use crate::model::EventMessage; use crate::services::{get_base_href, get_token, EventService, StatusService}; @@ -27,7 +27,7 @@ impl WebSocketService { ws: Rc::new(RefCell::new(None)), status_service, event_service, - ws_path: concat_path(&base_href, "ws"), + ws_path: concat_path_leading_slash(&base_href, "ws"), } }