Enhance CLI output: UTF-8 console codepage, VT colors, clean status cards

This commit is contained in:
2026-09-07 16:01:50 +02:00
parent a50dc04bb3
commit 681fb0c032
4 changed files with 194 additions and 64 deletions
+41 -31
View File
@@ -10,10 +10,11 @@ use hyper::service::service_fn;
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use tokio::sync::watch;
use tracing::{debug, error, info, warn};
use tracing::{debug, warn};
use crate::crypto::{derive_kek, unwrap_dek};
use crate::storage::Database;
use crate::ui;
use crate::vfs::SanctumFs;
/// Hilfsfunktion zur Formatierung des Laufwerksbuchstabens (z. B. 'S' -> "S:")
@@ -24,7 +25,6 @@ pub fn format_drive(drive_letter: char) -> String {
/// Trennt ein Windows-Netzlaufwerk via `net use <DRIVE>: /delete /y`.
pub fn unmount_drive(drive_letter: char) -> Result<()> {
let drive_str = format_drive(drive_letter);
info!("Trennen des Netzlaufwerks {} ...", drive_str);
let output = Command::new("net")
.args(["use", &drive_str, "/delete", "/y"])
@@ -42,14 +42,12 @@ pub fn unmount_drive(drive_letter: char) -> Result<()> {
);
}
info!("Laufwerk {} erfolgreich getrennt.", drive_str);
Ok(())
}
/// Bindet ein Windows-Netzlaufwerk via `net use <DRIVE>: http://127.0.0.1:<PORT>/ /persistent:no` ein.
fn run_net_use_mount(drive_str: &str, port: u16) -> Result<()> {
let url = format!("http://127.0.0.1:{}/", port);
info!("Verbinde Netzlaufwerk {} mit {} ...", drive_str, url);
let output = Command::new("net")
.args(["use", drive_str, &url, "/persistent:no"])
@@ -67,7 +65,6 @@ fn run_net_use_mount(drive_str: &str, port: u16) -> Result<()> {
);
}
info!("Laufwerk {} erfolgreich eingebunden!", drive_str);
Ok(())
}
@@ -87,23 +84,20 @@ pub async fn mount_container(
);
}
info!(
"Öffne Container '{}' ...",
container_path.display()
);
println!();
ui::step(1, 4, "📦", "Öffne Container & verifiziere Header...");
let db = Database::open(container_path)
.context("Konnte Container-Datenbank nicht öffnen")?;
info!("Lese Header und verifiziere Magic Bytes ...");
let meta = db
.read_meta()
.context("Konnte Container-Header nicht lesen")?;
info!("Leite KEK via Argon2id ab ...");
ui::step(2, 4, "🔑", "Leite KEK via Argon2id ab...");
let kek = derive_kek(password, &meta.kdf_salt, &meta.kdf_params)
.context("Schlüsselableitung fehlgeschlagen")?;
info!("Entschlüssele DEK via AES-256-GCM ...");
ui::step(3, 4, "🔓", "Entschlüssele DEK via AES-256-GCM...");
let dek = unwrap_dek(
&kek,
&meta.wrapped_dek,
@@ -124,10 +118,6 @@ pub async fn mount_container(
let listener = match TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], port_to_try))).await {
Ok(l) => l,
Err(_) if requested_port.is_none() => {
info!(
"Standard-Port {} belegt, wähle dynamischen freien Port ...",
port_to_try
);
TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))
.await
.context("Konnte keinen lokalen TCP-Port binden")?
@@ -139,7 +129,16 @@ pub async fn mount_container(
let bound_addr = listener.local_addr()?;
let bound_port = bound_addr.port();
info!("WebDAV-Server lauscht auf http://{}", bound_addr);
ui::step(
4,
4,
"🌐",
&format!(
"Starte WebDAV (Port {}) & binde Netzlaufwerk {} ein...",
bound_port, drive_str
),
);
let (shutdown_tx, mut shutdown_rx) = watch::channel(false);
@@ -175,7 +174,7 @@ pub async fn mount_container(
});
}
_ = shutdown_rx.changed() => {
info!("WebDAV-Server-Task empfängt Shutdown-Signal.");
debug!("WebDAV-Server-Task empfängt Shutdown-Signal.");
break;
}
}
@@ -190,13 +189,15 @@ pub async fn mount_container(
}
println!();
println!("============================================================");
println!(" Sanctum Container erfolgreich gemountet!");
println!(" Pfad: {}", container_path.display());
println!(" Laufwerk: {}", drive_str);
println!(" WebDAV URL: http://127.0.0.1:{}/", bound_port);
println!(" Drücke [Ctrl+C] zum sauberen Trennen und Schließen.");
println!("============================================================");
println!("┌─────────────────────────────────────────────────────────────┐");
println!("│ ✔ Sanctum Container erfolgreich gemountet");
println!("└─────────────────────────────────────────────────────────────┘");
println!();
println!(" • Container: {}", container_path.display());
println!(" • Netzlaufwerk: {} (im Windows Explorer bereit)", ui::cyan(&drive_str));
println!(" • WebDAV-URL: http://127.0.0.1:{}/", bound_port);
println!();
println!(" [{}] Drücke [Ctrl+C] zum sicheren Trennen und Schließen.", ui::yellow("Tipp"));
println!();
// Warten auf Strg+C
@@ -205,12 +206,15 @@ pub async fn mount_container(
.context("Fehler beim Registrieren des Ctrl+C Signalhandlers")?;
println!();
info!("Beendigungssignal (Ctrl+C) erhalten.");
info!("Trennen des Windows-Netzlaufwerks {} ...", drive_str);
println!(" {} Beendigungssignal (Ctrl+C) empfangen.", ui::yellow("[!]"));
print!(" {} Trenne Windows-Netzlaufwerk {} ... ", ui::dim("[-]"), drive_str);
let _ = std::io::Write::flush(&mut std::io::stdout());
// Automatisches Unmount
if let Err(e) = unmount_drive(drive_letter) {
warn!("Warnung beim automatischen Unmount: {e}");
println!("{}", ui::yellow(&format!("Warnung ({e})")));
} else {
println!("{}", ui::green("OK"));
}
// HTTP Server beenden
@@ -218,11 +222,17 @@ pub async fn mount_container(
let _ = server_handle.await;
// SQLite WAL Checkpoint erzwingen
info!("Führe SQLite WAL-Checkpoint aus (PRAGMA wal_checkpoint(TRUNCATE)) ...");
print!(" {} Führe SQLite WAL-Checkpoint aus ... ", ui::dim("[-]"));
let _ = std::io::Write::flush(&mut std::io::stdout());
if let Err(e) = db.checkpoint() {
error!("Fehler beim WAL-Checkpoint: {e}");
println!("{}", ui::red(&format!("Fehler ({e})")));
} else {
println!("{}", ui::green("OK"));
}
info!("Sanctum Container wurde sicher und vollständig geschlossen.");
println!();
println!("{} Sanctum Container wurde sicher und vollständig geschlossen.", ui::green(""));
println!();
Ok(())
}