diff --git a/README.md b/README.md index 6187960..1d57e34 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Server Manager - Next-Gen Media Server Orchestrator 🚀 -![Server Manager Banner](https://img.shields.io/badge/Status-Tested-brightgreen) ![Version](https://img.shields.io/badge/Version-1.0.7-blue) ![Rust](https://img.shields.io/badge/Built%20With-Rust-orange) ![Docker](https://img.shields.io/badge/Powered%20By-Docker-blue) +![Server Manager Banner](https://img.shields.io/badge/Status-Tested-brightgreen) ![Version](https://img.shields.io/badge/Version-1.0.8-blue) ![Rust](https://img.shields.io/badge/Built%20With-Rust-orange) ![Docker](https://img.shields.io/badge/Powered%20By-Docker-blue) **Server Manager** is a powerful and intelligent tool written in Rust to deploy, manage, and optimize a complete personal media and cloud server stack. It detects your hardware and automatically configures 28 Docker services for optimal performance. diff --git a/server_manager/Cargo.lock b/server_manager/Cargo.lock index 9d73bac..88fc67f 100644 --- a/server_manager/Cargo.lock +++ b/server_manager/Cargo.lock @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "server_manager" -version = "1.0.7" +version = "1.0.8" dependencies = [ "anyhow", "async-trait", diff --git a/server_manager/Cargo.toml b/server_manager/Cargo.toml index 475f80e..59a6547 100644 --- a/server_manager/Cargo.toml +++ b/server_manager/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "server_manager" -version = "1.0.7" +version = "1.0.8" edition = "2021" [dependencies] diff --git a/server_manager/src/core/config.rs b/server_manager/src/core/config.rs index c57c2a7..6ac6c9a 100644 --- a/server_manager/src/core/config.rs +++ b/server_manager/src/core/config.rs @@ -12,6 +12,7 @@ use tokio::sync::RwLock; struct CachedConfig { config: Config, last_mtime: Option, + last_check: Option, } static CONFIG_CACHE: OnceLock> = OnceLock::new(); @@ -42,12 +43,23 @@ impl Config { RwLock::new(CachedConfig { config: Config::default(), last_mtime: None, + last_check: None, }) }); // Fast path: Optimistic read { let guard = cache.read().await; + + // Throttle check + if let Some(last_check) = guard.last_check { + if let Ok(duration) = SystemTime::now().duration_since(last_check) { + if duration.as_millis() < 500 { + return Ok(guard.config.clone()); + } + } + } + if let Some(cached_mtime) = guard.last_mtime { // Check if file still matches if let Ok(metadata) = tokio::fs::metadata("config.yaml").await { @@ -63,12 +75,24 @@ impl Config { // Slow path: Update cache let mut guard = cache.write().await; + // Double-check throttle under write lock + if let Some(last_check) = guard.last_check { + if let Ok(duration) = SystemTime::now().duration_since(last_check) { + if duration.as_millis() < 500 { + return Ok(guard.config.clone()); + } + } + } + // Check metadata again (double-checked locking pattern) let metadata_res = tokio::fs::metadata("config.yaml").await; + // Update check time + guard.last_check = Some(SystemTime::now()); + match metadata_res { Ok(metadata) => { - let modified = metadata.modified().unwrap_or(SystemTime::now()); + let modified = metadata.modified().unwrap_or_else(|_| SystemTime::now()); if let Some(cached_mtime) = guard.last_mtime { if modified == cached_mtime { @@ -103,6 +127,52 @@ impl Config { } } + pub async fn enable_service_async(service_name: String) -> Result<()> { + let cache = CONFIG_CACHE.get_or_init(|| { + RwLock::new(CachedConfig { + config: Config::default(), + last_mtime: None, + last_check: None, + }) + }); + + let mut guard = cache.write().await; + guard.config.enable_service(&service_name); + + let config_clone = guard.config.clone(); + tokio::task::spawn_blocking(move || config_clone.save()).await??; + + if let Ok(metadata) = tokio::fs::metadata("config.yaml").await { + guard.last_mtime = metadata.modified().ok(); + } + guard.last_check = Some(SystemTime::now()); + + Ok(()) + } + + pub async fn disable_service_async(service_name: String) -> Result<()> { + let cache = CONFIG_CACHE.get_or_init(|| { + RwLock::new(CachedConfig { + config: Config::default(), + last_mtime: None, + last_check: None, + }) + }); + + let mut guard = cache.write().await; + guard.config.disable_service(&service_name); + + let config_clone = guard.config.clone(); + tokio::task::spawn_blocking(move || config_clone.save()).await??; + + if let Ok(metadata) = tokio::fs::metadata("config.yaml").await { + guard.last_mtime = metadata.modified().ok(); + } + guard.last_check = Some(SystemTime::now()); + + Ok(()) + } + pub fn save(&self) -> Result<()> { let content = serde_yaml_ng::to_string(self)?; fs::write("config.yaml", content).context("Failed to write config.yaml")?; diff --git a/server_manager/src/core/users.rs b/server_manager/src/core/users.rs index 41870d0..4c633dd 100644 --- a/server_manager/src/core/users.rs +++ b/server_manager/src/core/users.rs @@ -159,6 +159,41 @@ impl UserManager { self.save() } + pub async fn add_user_async( + &mut self, + username: &str, + password: &str, + role: Role, + quota_gb: Option, + ) -> Result<()> { + let mut manager_clone = self.clone(); + let username = username.to_string(); + let password = password.to_string(); + + let updated_manager = tokio::task::spawn_blocking(move || { + manager_clone.add_user(&username, &password, role, quota_gb)?; + Ok::(manager_clone) + }) + .await??; + + *self = updated_manager; + Ok(()) + } + + pub async fn delete_user_async(&mut self, username: &str) -> Result<()> { + let mut manager_clone = self.clone(); + let username = username.to_string(); + + let updated_manager = tokio::task::spawn_blocking(move || { + manager_clone.delete_user(&username)?; + Ok::(manager_clone) + }) + .await??; + + *self = updated_manager; + Ok(()) + } + pub fn update_password(&mut self, username: &str, new_password: &str) -> Result<()> { if let Some(user) = self.users.get_mut(username) { // System Password Update diff --git a/server_manager/src/interface/web.rs b/server_manager/src/interface/web.rs index 1f677c3..99ae8a9 100644 --- a/server_manager/src/interface/web.rs +++ b/server_manager/src/interface/web.rs @@ -627,9 +627,10 @@ async fn add_user_handler(State(state): State, session: Session, Fo }; let mut cache = state.users_cache.write().await; - let res = tokio::task::block_in_place(|| { - cache.manager.add_user(&payload.username, &payload.password, role_enum, quota_val) - }); + let res = cache + .manager + .add_user_async(&payload.username, &payload.password, role_enum, quota_val) + .await; if let Err(e) = res { error!("Failed to add user: {}", e); @@ -659,9 +660,7 @@ async fn delete_user_handler(State(state): State, session: Session, } let mut cache = state.users_cache.write().await; - let res = tokio::task::block_in_place(|| { - cache.manager.delete_user(&username) - }); + let res = cache.manager.delete_user_async(&username).await; if let Err(e) = res { error!("Failed to delete user: {}", e);