Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ Server Manager adjusts configuration via `HardwareManager`:

*Note: Swap presence is analyzed to avoid OOM on borderline configurations (e.g., 6GB RAM without swap -> Low).*

### ⚡ Optimizations & Performance

Server Manager includes several under-the-hood optimizations to ensure low resource usage and high responsiveness:

* **Sysctl Tuning**: Automatically applies kernel parameters (via `sysctl`) optimized for high-bandwidth media streaming (BBR congestion control, increased buffer sizes) and reduced swappiness.
* **Async Architecture**: The Web UI and core logic are built on `tokio` and `axum`, ensuring that heavy operations (like password hashing or user creation) never block the main event loop.
* **Smart Caching**: Configuration and User data are cached in memory with intelligent file modification checks (stat) to prevent unnecessary disk reads.
* **Efficient Monitoring**: System resource usage is refreshed only when needed and throttled to prevent CPU spikes.

### 🔒 Secrets Management

Passwords are stored in `secrets.yaml`.
Expand Down Expand Up @@ -277,6 +286,15 @@ Server Manager ajuste la configuration via `HardwareManager` :

*Note : La présence de Swap est analysée pour éviter les OOM sur les configurations limites (ex: 6GB RAM sans swap -> Low).*

### ⚡ Optimisations & Performances

Server Manager inclut plusieurs optimisations internes pour garantir une faible utilisation des ressources et une grande réactivité :

* **Réglage Sysctl** : Applique automatiquement les paramètres du noyau (via `sysctl`) optimisés pour le streaming multimédia à haut débit (contrôle de congestion BBR, buffers augmentés) et une utilisation réduite du swap.
* **Architecture Asynchrone** : L'interface Web et la logique centrale sont basées sur `tokio` et `axum`, garantissant que les opérations lourdes (comme le hachage des mots de passe ou la création d'utilisateurs) ne bloquent jamais la boucle d'événements principale.
* **Mise en Cache Intelligente** : Les données de configuration et d'utilisateur sont mises en cache en mémoire avec des vérifications intelligentes de modification de fichier (stat) pour éviter les lectures disque inutiles.
* **Surveillance Efficace** : L'utilisation des ressources système n'est actualisée que lorsque nécessaire et limitée pour éviter les pics de CPU.

### 🔒 Gestion des Secrets

Les mots de passe sont stockés dans `secrets.yaml`.
Expand Down
1 change: 1 addition & 0 deletions server_manager/src/core/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ impl HardwareInfo {
sys.refresh_memory();
sys.refresh_cpu();
sys.refresh_disks_list();
sys.refresh_disks();

let total_memory = sys.total_memory(); // Bytes
let ram_gb = total_memory / 1024 / 1024 / 1024;
Expand Down
98 changes: 66 additions & 32 deletions server_manager/src/interface/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ pub async fn start_server(port: u16) -> anyhow::Result<()> {
.with_expiry(Expiry::OnInactivity(Duration::hours(24)));

// Initialize System once
let mut sys = System::new_all();
sys.refresh_all();
let sys = System::new();
// We do NOT refresh everything here to save startup time.
// Dashboard will trigger refresh on first access because we init last_system_refresh to EPOCH.

let initial_config = Config::load().unwrap_or_default();
let initial_config_mtime = std::fs::metadata("config.yaml")
Expand All @@ -151,7 +152,7 @@ pub async fn start_server(port: u16) -> anyhow::Result<()> {

let app_state = Arc::new(AppState {
system: Mutex::new(sys),
last_system_refresh: Mutex::new(SystemTime::now()),
last_system_refresh: Mutex::new(SystemTime::UNIX_EPOCH),
config_cache: RwLock::new(CachedConfig {
config: initial_config,
last_modified: initial_config_mtime,
Expand Down Expand Up @@ -344,6 +345,7 @@ async fn dashboard(State(state): State<SharedState>, session: Session) -> impl I
{
sys.refresh_cpu();
sys.refresh_memory();
sys.refresh_disks_list();
sys.refresh_disks();
*last_refresh = now;
}
Expand Down Expand Up @@ -627,21 +629,37 @@ async fn add_user_handler(State(state): State<SharedState>, 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)
});

if let Err(e) = res {
error!("Failed to add user: {}", e);
// In a real app we'd flash a message. Here just redirect.
} else {
info!("User {} added via Web UI by {}", payload.username, session_user.username);
// Update mtime to prevent unnecessary reload
let path = std::path::Path::new("users.yaml");
let fallback_path = std::path::Path::new("/opt/server_manager/users.yaml");
let file_path = if path.exists() { path } else { fallback_path };
if let Ok(m) = std::fs::metadata(file_path) {
cache.last_modified = m.modified().ok();
let mut manager = cache.manager.clone();
let username = payload.username.clone();
let password = payload.password.clone();

// Perform heavy lifting (hashing, useradd) in a blocking thread
let res = tokio::task::spawn_blocking(move || -> anyhow::Result<UserManager> {
manager.add_user(&username, &password, role_enum, quota_val)?;
Ok(manager)
})
.await;

match res {
Ok(Ok(updated_manager)) => {
cache.manager = updated_manager;
info!(
"User {} added via Web UI by {}",
payload.username, session_user.username
);
// Update mtime to prevent unnecessary reload
let path = std::path::Path::new("users.yaml");
let fallback_path = std::path::Path::new("/opt/server_manager/users.yaml");
let file_path = if path.exists() { path } else { fallback_path };
if let Ok(m) = std::fs::metadata(file_path) {
cache.last_modified = m.modified().ok();
}
}
Ok(Err(e)) => {
error!("Failed to add user: {}", e);
}
Err(e) => {
error!("Task join error: {}", e);
}
}

Expand All @@ -659,20 +677,36 @@ async fn delete_user_handler(State(state): State<SharedState>, session: Session,
}

let mut cache = state.users_cache.write().await;
let res = tokio::task::block_in_place(|| {
cache.manager.delete_user(&username)
});

if let Err(e) = res {
error!("Failed to delete user: {}", e);
} else {
info!("User {} deleted via Web UI by {}", username, session_user.username);
// Update mtime to prevent unnecessary reload
let path = std::path::Path::new("users.yaml");
let fallback_path = std::path::Path::new("/opt/server_manager/users.yaml");
let file_path = if path.exists() { path } else { fallback_path };
if let Ok(m) = std::fs::metadata(file_path) {
cache.last_modified = m.modified().ok();
let mut manager = cache.manager.clone();
let username_clone = username.clone();

// Perform heavy lifting (userdel) in a blocking thread
let res = tokio::task::spawn_blocking(move || -> anyhow::Result<UserManager> {
manager.delete_user(&username_clone)?;
Ok(manager)
})
.await;

match res {
Ok(Ok(updated_manager)) => {
cache.manager = updated_manager;
info!(
"User {} deleted via Web UI by {}",
username, session_user.username
);
// Update mtime to prevent unnecessary reload
let path = std::path::Path::new("users.yaml");
let fallback_path = std::path::Path::new("/opt/server_manager/users.yaml");
let file_path = if path.exists() { path } else { fallback_path };
if let Ok(m) = std::fs::metadata(file_path) {
cache.last_modified = m.modified().ok();
}
}
Ok(Err(e)) => {
error!("Failed to delete user: {}", e);
}
Err(e) => {
error!("Task join error: {}", e);
}
}

Expand Down