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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ cd server_script/server_manager
cargo test
```

### ⚡ Optimizations & Performance

Server Manager is optimized for performance and resource usage:
* **Zero-Cost Abstractions**: Written in Rust, utilizing zero-cost abstractions to manage services without adding runtime overhead.
* **Async Operations**: Employs non-blocking async IO via Tokio for smooth handling of configuration and Docker communication.
* **Optimized Memory Usage**: In-memory caching minimizes redundant file reads and optimizes dashboard responsiveness.
* **Hardware Profiling**: Automatically disables unnecessary GC and limits memory consumption on Low profile hardware.

### CLI Commands

The tool provides several subcommands:
Expand Down Expand Up @@ -235,6 +243,14 @@ cargo build --release
sudo cp target/release/server_manager /usr/local/bin/
```

### ⚡ Optimisations & Performance

Server Manager est optimisé pour les performances et l'utilisation des ressources :
* **Abstractions à coût nul** : Écrit en Rust, l'outil gère les services sans introduire de charge d'exécution supplémentaire.
* **Opérations Asynchrones** : Utilisation d'E/S asynchrones non bloquantes via Tokio pour une gestion fluide de la configuration et de Docker.
* **Utilisation optimisée de la mémoire** : La mise en cache en mémoire minimise les lectures redondantes et optimise la réactivité du tableau de bord.
* **Profilage matériel** : Désactive automatiquement le GC (Garbage Collection) inutile et limite la consommation de mémoire sur le matériel à profil "Low".

### Commandes CLI

L'outil dispose de plusieurs sous-commandes :
Expand Down
1 change: 0 additions & 1 deletion server_manager/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server_manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ libc = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_yaml_ng = "0.10"
nix = { version = "0.27", features = ["user"] }
async-trait = "0.1"

axum = "0.7"
serde_json = "1.0"
tower = { version = "0.4", features = ["util"] }
Expand Down
2 changes: 1 addition & 1 deletion server_manager/src/core/secrets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ mod tests {

#[test]
fn test_hex_generation() {
let hex = generate_hex(16).unwrap();
let hex = generate_hex(16).expect("Should generate hex correctly");
assert_eq!(hex.len(), 32); // 16 bytes = 32 hex chars
}

Expand Down
6 changes: 3 additions & 3 deletions server_manager/src/core/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ mod tests {
// Verify
let user = manager.verify("testuser", "password123");
assert!(user.is_some());
assert_eq!(user.unwrap().role, Role::Observer);
assert_eq!(user.expect("User should exist").role, Role::Observer);

assert!(manager.verify("testuser", "wrongpass").is_none());

Expand All @@ -252,15 +252,15 @@ mod tests {
let mut manager = UserManager::default();
manager
.add_user("admin", "admin", Role::Admin, None)
.unwrap();
.expect("Should add initial admin");

// Should fail to delete last admin
assert!(manager.delete_user("admin").is_err());

// Add another admin
manager
.add_user("admin2", "admin", Role::Admin, None)
.unwrap();
.expect("Should add second admin");
// Now can delete one
assert!(manager.delete_user("admin").is_ok());
}
Expand Down
29 changes: 16 additions & 13 deletions server_manager/src/interface/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ fn write_html_head(out: &mut String, title: &str) {
}

fn write_html_foot(out: &mut String) {
out.push_str(r#"
let _ = write!(out, r#"
</div>
</body>
</html>
Expand All @@ -331,9 +331,9 @@ async fn dashboard(State(state): State<SharedState>, session: Session) -> impl I
let config = state.get_config().await;

// System Stats
let mut sys = state.system.lock().unwrap();
let mut sys = state.system.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
let now = SystemTime::now();
let mut last_refresh = state.last_system_refresh.lock().unwrap();
let mut last_refresh = state.last_system_refresh.lock().unwrap_or_else(|poisoned| poisoned.into_inner());

// Throttle refresh to max once every 500ms
if now
Expand Down Expand Up @@ -385,13 +385,14 @@ async fn dashboard(State(state): State<SharedState>, session: Session) -> impl I

// Navigation
if is_admin {
html.push_str(
let _ = write!(
html,
r#"
<div class="nav">
<a href="/">Dashboard</a>
<a href="/users">User Management</a>
</div>
"#,
"#
);
}

Expand Down Expand Up @@ -422,7 +423,8 @@ async fn dashboard(State(state): State<SharedState>, session: Session) -> impl I
);

// Services Table
html.push_str(
let _ = write!(
html,
r#"
<h2>Services</h2>
<table>
Expand All @@ -435,7 +437,7 @@ async fn dashboard(State(state): State<SharedState>, session: Session) -> impl I
</tr>
</thead>
<tbody>
"#,
"#
);

for svc in services {
Expand Down Expand Up @@ -477,13 +479,14 @@ async fn dashboard(State(state): State<SharedState>, session: Session) -> impl I
if enabled { "Disable" } else { "Enable" }
);
} else {
html.push_str("<span>Read-only</span>");
let _ = write!(html, "<span>Read-only</span>");
};

html.push_str("</td></tr>");
let _ = write!(html, "</td></tr>");
}

html.push_str(
let _ = write!(
html,
r#"
</tbody>
</table>
Expand All @@ -509,7 +512,7 @@ async fn users_page(State(state): State<SharedState>, session: Session) -> impl
let mut html = String::with_capacity(4096);
write_html_head(&mut html, "User Management - Server Manager");

html.push_str(r#"
let _ = write!(html, r#"
<div class="header">
<h1>User Management 👥</h1>
<form method="POST" action="/logout">
Expand Down Expand Up @@ -575,7 +578,7 @@ async fn users_page(State(state): State<SharedState>, session: Session) -> impl
let _ = write!(html, "{} GB", gb);
}
_ => {
html.push_str("Unlimited");
let _ = write!(html, "Unlimited");
}
}

Expand All @@ -591,7 +594,7 @@ async fn users_page(State(state): State<SharedState>, session: Session) -> impl
"#, Escaped(&u.username));
}

html.push_str("</tbody></table>");
let _ = write!(html, "</tbody></table>");
write_html_foot(&mut html);

Html(html).into_response()
Expand Down
52 changes: 52 additions & 0 deletions server_manager/web.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Compiling serde v1.0.228
Compiling zerocopy v0.8.35
Compiling either v1.15.0
Compiling rayon v1.11.0
Compiling which v4.4.2
Compiling lock_api v0.4.14
Compiling serde_urlencoded v0.7.1
Compiling parking_lot v0.12.5
Compiling serde_yaml_ng v0.10.0
Compiling tokio v1.49.0
Compiling tower-cookies v0.10.0
Compiling sysinfo v0.29.11
Compiling ppv-lite86 v0.2.21
Compiling rand_chacha v0.3.1
Compiling rand v0.8.5
Compiling hyper v1.8.1
Compiling tower-sessions-core v0.12.3
Compiling tower v0.5.3
Compiling tokio-util v0.7.18
Compiling tower-sessions-memory-store v0.12.3
Compiling hyper-util v0.1.19
Compiling tower-http v0.5.2
Compiling axum v0.7.9
Compiling tower-sessions v0.12.3
Compiling server_manager v1.0.7 (/app/server_manager)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 20.39s
Running `target/debug/server_manager web`
[2026-03-06T14:33:21Z INFO server_manager::core::users] No users found. Creating default 'admin' user.
[2026-03-06T14:33:22Z INFO server_manager::core::users] Default user 'admin' created with password 'admin'. CHANGE THIS IMMEDIATELY!
[2026-03-06T14:33:22Z INFO server_manager::interface::web] Starting Web UI on http://0.0.0.0:8099
[2026-03-06T14:34:20Z INFO tracing::span] call;
[2026-03-06T14:34:20Z INFO tracing::span] get_record;
[2026-03-06T14:34:20Z INFO tracing::span] call;
[2026-03-06T14:34:20Z INFO tracing::span] get_record;
[2026-03-06T14:34:21Z INFO tracing::span] call;
[2026-03-06T14:34:23Z INFO tracing::span] get_record;
[2026-03-06T14:34:23Z INFO tracing::span] save;
[2026-03-06T14:34:23Z INFO tracing::span] get_record;
[2026-03-06T14:34:23Z INFO tracing::span] call;
[2026-03-06T14:34:23Z INFO tracing::span] get_record;
[2026-03-06T14:34:54Z INFO tracing::span] call;
[2026-03-06T14:34:54Z INFO tracing::span] get_record;
[2026-03-06T14:34:54Z INFO tracing::span] call;
[2026-03-06T14:34:54Z INFO tracing::span] get_record;
[2026-03-06T14:34:54Z INFO tracing::span] call;
[2026-03-06T14:34:55Z INFO tracing::span] get_record;
[2026-03-06T14:34:55Z INFO tracing::span] save;
[2026-03-06T14:34:55Z INFO tracing::span] get_record;
[2026-03-06T14:34:55Z INFO tracing::span] call;
[2026-03-06T14:34:55Z INFO tracing::span] get_record;
[2026-03-06T14:34:56Z INFO tracing::span] call;
[2026-03-06T14:34:56Z INFO tracing::span] get_record;