From f0d04474165a8c397b527eedd59263390462af95 Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Wed, 11 Feb 2026 22:44:02 +0100 Subject: [PATCH] [Clean] --- src/server/mod.rs | 1 + src/server/server.rs | 70 +++++++---------------- src/server/web_path_parser.rs | 101 ++++++++++++++++++++++++++++++++++ src/util/file_util.rs | 75 ++++++++++--------------- 4 files changed, 152 insertions(+), 95 deletions(-) create mode 100644 src/server/web_path_parser.rs mode change 100644 => 100755 src/util/file_util.rs diff --git a/src/server/mod.rs b/src/server/mod.rs index e19c91e..003deea 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,4 @@ pub mod api; pub mod server; pub mod socket; +pub mod web_path_parser; diff --git a/src/server/server.rs b/src/server/server.rs index b4449f2..cc0bbc8 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -1,7 +1,7 @@ use crate::gui::log_panel::log_message; -use crate::server::api; use crate::server::socket::handle; -use crate::util::file_util::{load_file_buf, load_file_vec}; +use crate::server::{api, web_path_parser}; +use crate::util::file_util::load_file_buf; use crate::{ACTIVE_TASKS, SHUTDOWN}; use base64::Engine; @@ -29,7 +29,7 @@ use std::result::Result::Ok; use std::sync::Arc; use std::{future::Future, pin::Pin, time::Duration}; use tokio::net::TcpListener; -use tokio::sync::broadcast; // Import broadcast for the kill switch +use tokio::sync::broadcast; use tokio_rustls::TlsAcceptor; use tokio_tungstenite::WebSocketStream; use tower::Service; @@ -141,57 +141,27 @@ impl Service> for HttpService { Ok(api::handle(&path, &is_acceptable, headers.clone(), body_string).await) } else { - let mut path_parts: Vec<&str> = path.split("/").collect(); - let name = path_parts.remove(path_parts.len() - 1); - let name = if name.is_empty() { - "index.html" - } else if name.contains(".") && name.contains("?") { - name.split("?").next().unwrap() - } else if name.contains(".") { - name - } else { - &format!("{}.html", name) - }; - let code = if let Some(ext) = name.split(".").last() { - match ext { - "html" => "text/html", - "css" => "text/css", - "ico" => "image/x-icon", - "png" => "image/png", - "js" => "application/javascript", - "json" => "application/json", - _ => "application/octet-stream", + let whole_body = match body.collect().await { + Ok(collected) => collected, + Err(e) => { + log_message(format!("Error collecting body: {}", e)); + return Ok(HttpResponse::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Full::new(Bytes::from(format!( + "Failed to read body: {}", + e + )))) + .unwrap()); } - } else { - "application/octet-stream" }; - let (status, content, body_text): (StatusCode, &str, Vec) = { - let content = load_file_vec(&format!("web{}/", path_parts.join("/")), name); - if content.is_empty() { - let content = load_file_vec("web", "404.html"); - if content.is_empty() { - ( - StatusCode::NOT_FOUND, - code, - include_str!("../../static/web/404.html") - .as_bytes() - .to_vec(), - ) - } else { - (StatusCode::OK, code, content) - } - } else { - (StatusCode::OK, code, content) - } + let bytes = whole_body.to_bytes(); + + let body_string: Option = match String::from_utf8(bytes.to_vec()) { + Ok(s) => Some(s), + Err(_) => None, }; - let body = Full::new(Bytes::from(body_text.to_vec())); - let response = HttpResponse::builder() - .header("Content-Type", content) - .status(status) - .body(body) - .unwrap(); - Ok(response) + Ok(web_path_parser::handle(&path, headers, body_string).await) } }; diff --git a/src/server/web_path_parser.rs b/src/server/web_path_parser.rs new file mode 100644 index 0000000..4438d0e --- /dev/null +++ b/src/server/web_path_parser.rs @@ -0,0 +1,101 @@ +use std::path::{Path, PathBuf}; + +use axum::http::HeaderValue; +use http_body_util::Full; +use hyper::body::Bytes; +use hyper::{HeaderMap, Response as HttpResponse, StatusCode}; +use json::JsonValue; + +use crate::util::file_util::load_file_vec; + +fn codec_for_ext(ext: &str) -> &'static str { + match ext { + "html" => "text/html; charset=utf-8", + "css" => "text/css", + "js" => "application/javascript", + "json" => "application/json", + "png" => "image/png", + "ico" => "image/x-icon", + "woff2" => "font/woff2", + _ => "application/octet-stream", + } +} + +pub async fn handle( + path: &str, + _headers: HeaderMap, + body_string: Option, +) -> HttpResponse> { + let path_parts: Vec<&str> = path.split("/").filter(|s| !s.is_empty()).collect(); + + let _body: Option = if body_string.is_some() { + if let Ok(body_json) = json::parse(&body_string.unwrap()) { + Some(body_json) + } else { + None + } + } else { + None + }; + + let req_path = path.trim_start_matches('/'); + let mut fs_path = PathBuf::from("web"); + + if req_path.is_empty() { + fs_path.push("index.html"); + } else { + fs_path.extend(req_path.split('/')); + } + + if fs_path.is_dir() { + fs_path.push("index.html"); + } + + let ext = fs_path.extension().and_then(|e| e.to_str()).unwrap_or(""); + + let codec = codec_for_ext(ext); + + let dir = fs_path.parent().unwrap_or(Path::new("web")); + let name = fs_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("index.html"); + + let content = load_file_vec(dir.to_str().unwrap_or("web"), name); + if let Ok(content) = content { + HttpResponse::builder() + .status(StatusCode::OK) + .header("content-type", codec) + .body(Full::new(Bytes::from(content))) + .unwrap() + } else { + if matches!(ext, "js" | "css" | "woff2") { + return HttpResponse::builder() + .status(StatusCode::NOT_FOUND) + .header("content-type", "text/plain") + .body(Full::new(Bytes::from("Not found"))) + .unwrap(); + } + + let fallback = load_file_vec("web", "404.html"); + let body = if let Ok(fallback) = fallback { + if fallback.is_empty() { + include_str!("../../static/web/404.html") + .as_bytes() + .to_vec() + } else { + fallback + } + } else { + include_str!("../../static/web/404.html") + .as_bytes() + .to_vec() + }; + + HttpResponse::builder() + .status(StatusCode::NOT_FOUND) + .header("content-type", "text/html; charset=utf-8") + .body(Full::new(Bytes::from(body))) + .unwrap() + } +} diff --git a/src/util/file_util.rs b/src/util/file_util.rs old mode 100644 new mode 100755 index 1936e03..9f26eb7 --- a/src/util/file_util.rs +++ b/src/util/file_util.rs @@ -1,8 +1,9 @@ use std::ffi::OsStr; use std::fs::{self, File}; -use std::io::{self, BufReader, Read, Write}; +use std::io::{self, BufReader, Read}; use std::path::{Path, PathBuf}; use sysinfo::System; +use tokio::io::AsyncWriteExt; use uuid::Uuid; use walkdir::WalkDir; use zip::ZipArchive; @@ -33,7 +34,7 @@ fn delete_dir_recursive(directory: &Path) -> bool { log_message(format!( "[IMPORTANT] Couldn't delete directory {}: {}", directory.display(), - e + e, )); return false; } @@ -124,31 +125,13 @@ pub fn load_file(path: &str, name: &str) -> String { content } -pub fn load_file_vec(path: &str, name: &str) -> Vec { +pub fn load_file_vec(path: &str, name: &str) -> Result, std::io::Error> { let dir = Path::new(&get_directory()).join(path); let file_path = dir.join(name); - if !dir.exists() { - if let Err(e) = fs::create_dir_all(&dir) { - log_message(format!("[IMPORTANT] Couldn't create directories: {}", e)); - return Vec::new(); - } - return Vec::new(); - } - - if !file_path.exists() { - if let Err(e) = File::create(&file_path) { - log_message(format!("[IMPORTANT] Couldn't create file: {}", e)); - } - return Vec::new(); - } - - let mut content = Vec::new(); - if let Ok(mut f) = File::open(&file_path) { - let _ = f.read_to_end(&mut content); - } - content + std::fs::read(file_path) } + pub fn save_file(path: &str, name: &str, value: &str) { let dir = Path::new(&get_directory()).join(path); let file_path = dir.join(name); @@ -190,6 +173,7 @@ pub fn get_directory() -> String { .to_string() } +// Helper to download the zip file content to a file on disk #[allow(dead_code)] pub fn used_space() -> u64 { get_directory_size(&PathBuf::from(get_directory())) @@ -257,14 +241,12 @@ pub async fn download_zip(url: &str, zip_path: &Path) -> Result<(), Box