Initial commit: Veritas SpoolerReset Pro Windows Spooler Repair Module
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "spooler-reset"
|
||||||
|
version = "1.2.0"
|
||||||
|
edition.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
veritas-core = { path = "../../veritas-core" }
|
||||||
|
anyhow.workspace = true
|
||||||
|
tokio = { version = "1.38", features = ["full"] }
|
||||||
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
colored = "2.1"
|
||||||
|
eframe = "0.27"
|
||||||
|
egui = "0.27"
|
||||||
|
image = "0.24"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
winres = "0.1"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
fn main() {
|
||||||
|
if std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default() == "windows" {
|
||||||
|
let mut res = winres::WindowsResource::new();
|
||||||
|
res.set_icon("veritas_icon.ico");
|
||||||
|
res.set("ProductName", "Veritas SpoolerReset");
|
||||||
|
res.set("FileDescription", "Veritas SpoolerReset — Windows Druckspooler & Warteschlangen-Reparatur");
|
||||||
|
res.set("LegalCopyright", "Copyright © 2026 Veritas Suite");
|
||||||
|
|
||||||
|
// Embed Windows UAC Manifest requiring Administrator privileges on launch
|
||||||
|
res.set_manifest(r#"
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges>
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
</assembly>
|
||||||
|
"#);
|
||||||
|
|
||||||
|
if let Err(e) = res.compile() {
|
||||||
|
eprintln!("Warning: Failed to embed Windows resource icon / manifest: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+975
@@ -0,0 +1,975 @@
|
|||||||
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
|
use eframe::egui;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
os::windows::process::CommandExt,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
use veritas_core::{
|
||||||
|
hwid::generate_hardware_id, ActivationRequest, ActivationResponse, AuthResponse,
|
||||||
|
LoginRequest, UserDashboardResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
|
const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
struct UserSession {
|
||||||
|
user_id: i32,
|
||||||
|
email: String,
|
||||||
|
auth_token: String,
|
||||||
|
license_key: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum ActiveTab {
|
||||||
|
Dashboard,
|
||||||
|
Diagnostics,
|
||||||
|
Account,
|
||||||
|
Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum RepairMode {
|
||||||
|
DeepReset,
|
||||||
|
FastQueueClear,
|
||||||
|
TestPrint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
enum AppTheme {
|
||||||
|
ObsidianIndigo,
|
||||||
|
CyberNeon,
|
||||||
|
EmeraldShield,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_temp_session_file() -> PathBuf {
|
||||||
|
std::env::temp_dir().join("veritas_session.tmp")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_temp_session() -> Option<UserSession> {
|
||||||
|
let path = get_temp_session_file();
|
||||||
|
if path.exists() {
|
||||||
|
if let Ok(content) = fs::read_to_string(path) {
|
||||||
|
if let Ok(sess) = serde_json::from_str::<UserSession>(&content) {
|
||||||
|
return Some(sess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_temp_session(session: &UserSession) {
|
||||||
|
let path = get_temp_session_file();
|
||||||
|
if let Ok(json) = serde_json::to_string(session) {
|
||||||
|
let _ = fs::write(path, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_temp_session() {
|
||||||
|
let path = get_temp_session_file();
|
||||||
|
let _ = fs::remove_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), eframe::Error> {
|
||||||
|
let options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default()
|
||||||
|
.with_title(format!("Veritas SpoolerReset v{} — Commercial Edition", APP_VERSION))
|
||||||
|
.with_inner_size([880.0, 640.0])
|
||||||
|
.with_min_inner_size([800.0, 580.0])
|
||||||
|
.with_resizable(true),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
eframe::run_native(
|
||||||
|
"Veritas SpoolerReset",
|
||||||
|
options,
|
||||||
|
Box::new(|_cc| Box::new(SpoolerResetApp::new())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrinterInfo {
|
||||||
|
name: String,
|
||||||
|
status: String,
|
||||||
|
is_default: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SpoolerResetApp {
|
||||||
|
session: Arc<Mutex<Option<UserSession>>>,
|
||||||
|
login_email: String,
|
||||||
|
login_pass: String,
|
||||||
|
remember_me: bool,
|
||||||
|
auth_alert: Arc<Mutex<Option<(String, bool)>>>,
|
||||||
|
hwid: String,
|
||||||
|
is_admin: bool,
|
||||||
|
log_messages: Arc<Mutex<Vec<String>>>,
|
||||||
|
active_tab: ActiveTab,
|
||||||
|
repair_mode: RepairMode,
|
||||||
|
app_theme: AppTheme,
|
||||||
|
spooler_status: String,
|
||||||
|
queue_file_count: usize,
|
||||||
|
queue_bytes: u64,
|
||||||
|
installed_printers: Vec<PrinterInfo>,
|
||||||
|
is_repairing: bool,
|
||||||
|
is_authenticating: Arc<Mutex<bool>>,
|
||||||
|
icon_texture: Option<egui::TextureHandle>,
|
||||||
|
auto_refresh: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpoolerResetApp {
|
||||||
|
fn new() -> Self {
|
||||||
|
let hwid = generate_hardware_id();
|
||||||
|
let is_admin = is_running_as_admin();
|
||||||
|
let initial_session = load_temp_session();
|
||||||
|
|
||||||
|
let mut app = Self {
|
||||||
|
session: Arc::new(Mutex::new(initial_session.clone())),
|
||||||
|
login_email: String::new(),
|
||||||
|
login_pass: String::new(),
|
||||||
|
remember_me: true,
|
||||||
|
auth_alert: Arc::new(Mutex::new(None)),
|
||||||
|
hwid,
|
||||||
|
is_admin,
|
||||||
|
log_messages: Arc::new(Mutex::new(vec![
|
||||||
|
format!("🛡️ Willkommen bei Veritas SpoolerReset v{} Commercial Edition.", APP_VERSION),
|
||||||
|
"⚙️ Initialisiere GPU-beschleunigtes UI & Account-Authentifizierung...".to_string(),
|
||||||
|
])),
|
||||||
|
active_tab: ActiveTab::Dashboard,
|
||||||
|
repair_mode: RepairMode::DeepReset,
|
||||||
|
app_theme: AppTheme::ObsidianIndigo,
|
||||||
|
spooler_status: check_spooler_status(),
|
||||||
|
queue_file_count: 0,
|
||||||
|
queue_bytes: 0,
|
||||||
|
installed_printers: vec![],
|
||||||
|
is_repairing: false,
|
||||||
|
is_authenticating: Arc::new(Mutex::new(false)),
|
||||||
|
icon_texture: None,
|
||||||
|
auto_refresh: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(sess) = initial_session {
|
||||||
|
if let Ok(mut l) = app.log_messages.lock() {
|
||||||
|
l.push(format!("🔄 Automatisch angemeldet als {} (%TEMP% Session)", sess.email));
|
||||||
|
}
|
||||||
|
app.trigger_online_activation();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.refresh_system_diagnostics();
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_icon(&mut self, ctx: &egui::Context) {
|
||||||
|
if self.icon_texture.is_none() {
|
||||||
|
let icon_bytes = include_bytes!("../veritas_icon.png");
|
||||||
|
if let Ok(image) = image::load_from_memory(icon_bytes) {
|
||||||
|
let image_rgba = image.to_rgba8();
|
||||||
|
let size = [image.width() as _, image.height() as _];
|
||||||
|
let pixels = image_rgba.as_flat_samples();
|
||||||
|
let color_image = egui::ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
|
||||||
|
|
||||||
|
let texture = ctx.load_texture("veritas_icon", color_image, Default::default());
|
||||||
|
self.icon_texture = Some(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh_system_diagnostics(&mut self) {
|
||||||
|
self.spooler_status = check_spooler_status();
|
||||||
|
let (count, bytes) = check_queue_files();
|
||||||
|
self.queue_file_count = count;
|
||||||
|
self.queue_bytes = bytes;
|
||||||
|
self.installed_printers = fetch_detailed_printers();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_login(&mut self) {
|
||||||
|
if let Ok(auth_flag) = self.is_authenticating.lock() {
|
||||||
|
if *auth_flag {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let email = self.login_email.trim().to_string();
|
||||||
|
let password = self.login_pass.clone();
|
||||||
|
let remember = self.remember_me;
|
||||||
|
let hwid = self.hwid.clone();
|
||||||
|
let logs = self.log_messages.clone();
|
||||||
|
|
||||||
|
if email.is_empty() || password.is_empty() {
|
||||||
|
if let Ok(mut alert) = self.auth_alert.lock() {
|
||||||
|
*alert = Some(("Bitte E-Mail Adresse und Passwort eingeben.".to_string(), false));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut auth_flag) = self.is_authenticating.lock() {
|
||||||
|
*auth_flag = true;
|
||||||
|
}
|
||||||
|
if let Ok(mut alert) = self.auth_alert.lock() {
|
||||||
|
*alert = Some(("Prüfe Logindaten bei https://kanjiv.at/...".to_string(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
let alert_store = self.auth_alert.clone();
|
||||||
|
let session_store = self.session.clone();
|
||||||
|
let auth_flag_store = self.is_authenticating.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let rt = tokio::runtime::Runtime::new();
|
||||||
|
if let Ok(rt) = rt {
|
||||||
|
rt.block_on(async {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let login_payload = LoginRequest { email: email.clone(), password };
|
||||||
|
|
||||||
|
match client.post("https://kanjiv.at/api/v1/auth/login").json(&login_payload).send().await {
|
||||||
|
Ok(resp) => {
|
||||||
|
if let Ok(auth_resp) = resp.json::<AuthResponse>().await {
|
||||||
|
if auth_resp.success {
|
||||||
|
let uid = auth_resp.user_id.unwrap_or(0);
|
||||||
|
let token = auth_resp.auth_token.unwrap_or_default();
|
||||||
|
|
||||||
|
let dash_res = client.get(format!("https://kanjiv.at/api/v1/account/dashboard?user_id={}", uid)).send().await;
|
||||||
|
let mut active_license: Option<String> = None;
|
||||||
|
|
||||||
|
if let Ok(d_resp) = dash_res {
|
||||||
|
if let Ok(dash_data) = d_resp.json::<UserDashboardResponse>().await {
|
||||||
|
for lic in dash_data.licenses {
|
||||||
|
if lic.active {
|
||||||
|
active_license = Some(lic.license_key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(lic_key) = active_license {
|
||||||
|
let act_payload = ActivationRequest {
|
||||||
|
license_key: lic_key.clone(),
|
||||||
|
hardware_id: hwid.clone(),
|
||||||
|
app_name: format!("Veritas SpoolerReset Commercial GUI v{}", APP_VERSION),
|
||||||
|
};
|
||||||
|
let _ = client.post("https://kanjiv.at/api/v1/activate").json(&act_payload).send().await;
|
||||||
|
|
||||||
|
let sess = UserSession {
|
||||||
|
user_id: uid,
|
||||||
|
email: email.clone(),
|
||||||
|
auth_token: token,
|
||||||
|
license_key: Some(lic_key.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if remember {
|
||||||
|
save_temp_session(&sess);
|
||||||
|
} else {
|
||||||
|
delete_temp_session();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut s) = session_store.lock() {
|
||||||
|
*s = Some(sess);
|
||||||
|
}
|
||||||
|
if let Ok(mut a) = alert_store.lock() {
|
||||||
|
*a = Some(("Anmeldung erfolgreich! Lizenz wurde aktiviert.".to_string(), true));
|
||||||
|
}
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push(format!("🔒 Angemeldet als {} (Lizenz: {})", email, lic_key));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Ok(mut a) = alert_store.lock() {
|
||||||
|
*a = Some(("⚠️ Keine aktive Veritas Pro Lizenz auf diesem Account gefunden! Bitte aktiviere eine Lizenz auf https://kanjiv.at/".to_string(), false));
|
||||||
|
}
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push(format!("⚠️ Anmeldefehler für {}: Keine aktive Lizenz vorhanden.", email));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Ok(mut a) = alert_store.lock() {
|
||||||
|
*a = Some((format!("Fehler: {}", auth_resp.message), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
if let Ok(mut a) = alert_store.lock() {
|
||||||
|
*a = Some(("Netzwerkfehler beim Verbinden mit https://kanjiv.at/".to_string(), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut auth_flag) = auth_flag_store.lock() {
|
||||||
|
*auth_flag = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_online_activation(&self) {
|
||||||
|
if let Ok(session_guard) = self.session.lock() {
|
||||||
|
if let Some(session) = session_guard.as_ref() {
|
||||||
|
if let Some(key) = &session.license_key {
|
||||||
|
let key = key.clone();
|
||||||
|
let hwid = self.hwid.clone();
|
||||||
|
let logs = self.log_messages.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let rt = tokio::runtime::Runtime::new();
|
||||||
|
if let Ok(rt) = rt {
|
||||||
|
rt.block_on(async {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let req = ActivationRequest {
|
||||||
|
license_key: key.clone(),
|
||||||
|
hardware_id: hwid.clone(),
|
||||||
|
app_name: format!("Veritas SpoolerReset Commercial GUI v{}", APP_VERSION),
|
||||||
|
};
|
||||||
|
|
||||||
|
match client
|
||||||
|
.post("https://kanjiv.at/api/v1/activate")
|
||||||
|
.json(&req)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(resp) => {
|
||||||
|
if let Ok(act_resp) = resp.json::<ActivationResponse>().await {
|
||||||
|
if let Ok(mut logs) = logs.lock() {
|
||||||
|
logs.push(format!("🔒 VPS Lizenz-Handshake: {}", act_resp.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
if let Ok(mut logs) = logs.lock() {
|
||||||
|
logs.push("⚠️ VPS Server im Offline-Fallback Modus.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn logout(&mut self) {
|
||||||
|
delete_temp_session();
|
||||||
|
if let Ok(mut s) = self.session.lock() {
|
||||||
|
*s = None;
|
||||||
|
}
|
||||||
|
if let Ok(mut l) = self.log_messages.lock() {
|
||||||
|
l.push("Abgemeldet. TEMP-Session gelöscht.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_repair(&mut self) {
|
||||||
|
if self.is_repairing {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_logged_in = self.session.lock().map(|s| s.is_some()).unwrap_or(false);
|
||||||
|
if !is_logged_in {
|
||||||
|
if let Ok(mut alert) = self.auth_alert.lock() {
|
||||||
|
*alert = Some(("⚠️ Ausführung gesperrt! Bitte melde dich zuerst mit deinem Veritas Account an.".to_string(), false));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.is_repairing = true;
|
||||||
|
let mode = match self.repair_mode {
|
||||||
|
RepairMode::DeepReset => "DeepReset",
|
||||||
|
RepairMode::FastQueueClear => "FastQueueClear",
|
||||||
|
RepairMode::TestPrint => "TestPrint",
|
||||||
|
};
|
||||||
|
|
||||||
|
let logs = self.log_messages.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
if mode == "TestPrint" {
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push("🖨️ Sende Testdruck-Befehl an Windows Standard-Drucker...".to_string());
|
||||||
|
}
|
||||||
|
let _ = Command::new("powershell")
|
||||||
|
.args(["-Command", "'Veritas SpoolerReset Testseite' | Out-Printer"])
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push("✅ Testdruck-Auftrag an Windows übermittelt.".to_string());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push(format!("⚙️ Reparatur-Modus gestartet: {}", mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode == "DeepReset" {
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push("PHASE 1: Stoppe Druckspooler (net stop spooler)...".to_string());
|
||||||
|
}
|
||||||
|
let _ = Command::new("net")
|
||||||
|
.args(["stop", "spooler"])
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push("PHASE 2: Bereinige blockierte Dateien in C:\\Windows\\System32\\spool\\PRINTERS...".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let spool_dir = Path::new(r"C:\Windows\System32\spool\PRINTERS");
|
||||||
|
let mut del_count = 0;
|
||||||
|
let mut freed = 0u64;
|
||||||
|
|
||||||
|
if spool_dir.exists() {
|
||||||
|
if let Ok(entries) = fs::read_dir(spool_dir) {
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_file() {
|
||||||
|
if let Ok(meta) = fs::metadata(&path) {
|
||||||
|
freed += meta.len();
|
||||||
|
}
|
||||||
|
if fs::remove_file(&path).is_ok() {
|
||||||
|
del_count += 1;
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push(format!(" 🗑️ Gelöscht: {}", path.file_name().unwrap_or_default().to_string_lossy()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push(format!(" ✅ {} Dateien gelöscht ({} KB freigegeben).", del_count, freed / 1024));
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode == "DeepReset" {
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push("PHASE 3: Starte Druckspooler-Dienst neu (net start spooler)...".to_string());
|
||||||
|
}
|
||||||
|
let _ = Command::new("net")
|
||||||
|
.args(["start", "spooler"])
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut l) = logs.lock() {
|
||||||
|
l.push("🎉 REPARATUR ERFOLGREICH ABGESCHLOSSEN!".to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for SpoolerResetApp {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
self.load_icon(ctx);
|
||||||
|
|
||||||
|
let mut visuals = egui::Visuals::dark();
|
||||||
|
match self.app_theme {
|
||||||
|
AppTheme::ObsidianIndigo => {
|
||||||
|
visuals.panel_fill = egui::Color32::from_rgb(13, 18, 30);
|
||||||
|
visuals.window_fill = egui::Color32::from_rgb(13, 18, 30);
|
||||||
|
}
|
||||||
|
AppTheme::CyberNeon => {
|
||||||
|
visuals.panel_fill = egui::Color32::from_rgb(8, 14, 26);
|
||||||
|
visuals.window_fill = egui::Color32::from_rgb(8, 14, 26);
|
||||||
|
}
|
||||||
|
AppTheme::EmeraldShield => {
|
||||||
|
visuals.panel_fill = egui::Color32::from_rgb(5, 24, 18);
|
||||||
|
visuals.window_fill = egui::Color32::from_rgb(5, 24, 18);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.set_visuals(visuals);
|
||||||
|
|
||||||
|
let current_session = self.session.lock().ok().and_then(|s| s.clone());
|
||||||
|
let is_logged_in = current_session.is_some();
|
||||||
|
|
||||||
|
// Top Header Banner
|
||||||
|
egui::TopBottomPanel::top("top_header").show(ctx, |ui| {
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if let Some(texture) = &self.icon_texture {
|
||||||
|
ui.image((texture.id(), egui::vec2(38.0, 38.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new("VERITAS").font(egui::FontId::proportional(20.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
ui.label(egui::RichText::new("SPOOLERRESET").font(egui::FontId::proportional(20.0)).strong().color(egui::Color32::from_rgb(6, 182, 212)));
|
||||||
|
ui.label(egui::RichText::new(format!("v{}", APP_VERSION)).size(12.0).color(egui::Color32::from_rgb(99, 102, 241)));
|
||||||
|
});
|
||||||
|
ui.label(egui::RichText::new("Sparte 3: CURA System Fixer — Autonomous Windows Print Engine Diagnostics").size(11.0).color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
|
if let Some(session) = ¤t_session {
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(6, 78, 59))
|
||||||
|
.rounding(4.0)
|
||||||
|
.inner_margin(egui::vec2(8.0, 4.0))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new(format!("🟢 AKTIV: {}", session.email)).size(11.0).strong().color(egui::Color32::from_rgb(52, 211, 153)));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(153, 27, 27))
|
||||||
|
.rounding(4.0)
|
||||||
|
.inner_margin(egui::vec2(8.0, 4.0))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new("🔒 NICHT ANGEMELDET").size(11.0).strong().color(egui::Color32::WHITE));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(8.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bottom Footer Bar
|
||||||
|
egui::TopBottomPanel::bottom("bottom_footer").show(ctx, |ui| {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new(format!("Veritas Suite Commercial License v{} | Cloud Engine: https://kanjiv.at/", APP_VERSION)).size(11.0).color(egui::Color32::from_rgb(100, 116, 139)));
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
|
ui.label(egui::RichText::new("VPS Handshake: 200 OK").size(11.0).color(egui::Color32::from_rgb(16, 185, 129)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(4.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Left Navigation Sidebar
|
||||||
|
egui::SidePanel::left("left_sidebar")
|
||||||
|
.resizable(false)
|
||||||
|
.default_width(190.0)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.add_space(15.0);
|
||||||
|
ui.label(egui::RichText::new("NAVIGATION").size(10.0).strong().color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
|
||||||
|
let btn_dash = ui.add_sized(
|
||||||
|
[170.0, 36.0],
|
||||||
|
egui::Button::new(
|
||||||
|
egui::RichText::new("🚀 Reparatur-Zentrale")
|
||||||
|
.font(egui::FontId::proportional(14.0))
|
||||||
|
.color(if self.active_tab == ActiveTab::Dashboard { egui::Color32::WHITE } else { egui::Color32::from_rgb(203, 213, 225) }),
|
||||||
|
)
|
||||||
|
.fill(if self.active_tab == ActiveTab::Dashboard { egui::Color32::from_rgb(99, 102, 241) } else { egui::Color32::from_rgb(30, 41, 59) })
|
||||||
|
);
|
||||||
|
if btn_dash.clicked() { self.active_tab = ActiveTab::Dashboard; }
|
||||||
|
|
||||||
|
ui.add_space(6.0);
|
||||||
|
let btn_diag = ui.add_sized(
|
||||||
|
[170.0, 36.0],
|
||||||
|
egui::Button::new(
|
||||||
|
egui::RichText::new("📊 System-Diagnostik")
|
||||||
|
.font(egui::FontId::proportional(14.0))
|
||||||
|
.color(if self.active_tab == ActiveTab::Diagnostics { egui::Color32::WHITE } else { egui::Color32::from_rgb(203, 213, 225) }),
|
||||||
|
)
|
||||||
|
.fill(if self.active_tab == ActiveTab::Diagnostics { egui::Color32::from_rgb(99, 102, 241) } else { egui::Color32::from_rgb(30, 41, 59) })
|
||||||
|
);
|
||||||
|
if btn_diag.clicked() {
|
||||||
|
self.active_tab = ActiveTab::Diagnostics;
|
||||||
|
self.refresh_system_diagnostics();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add_space(6.0);
|
||||||
|
let btn_acc = ui.add_sized(
|
||||||
|
[170.0, 36.0],
|
||||||
|
egui::Button::new(
|
||||||
|
egui::RichText::new("🔐 Account & Lizenz")
|
||||||
|
.font(egui::FontId::proportional(14.0))
|
||||||
|
.color(if self.active_tab == ActiveTab::Account { egui::Color32::WHITE } else { egui::Color32::from_rgb(203, 213, 225) }),
|
||||||
|
)
|
||||||
|
.fill(if self.active_tab == ActiveTab::Account { egui::Color32::from_rgb(99, 102, 241) } else { egui::Color32::from_rgb(30, 41, 59) })
|
||||||
|
);
|
||||||
|
if btn_acc.clicked() { self.active_tab = ActiveTab::Account; }
|
||||||
|
|
||||||
|
ui.add_space(6.0);
|
||||||
|
let btn_set = ui.add_sized(
|
||||||
|
[170.0, 36.0],
|
||||||
|
egui::Button::new(
|
||||||
|
egui::RichText::new("⚙️ Einstellungen")
|
||||||
|
.font(egui::FontId::proportional(14.0))
|
||||||
|
.color(if self.active_tab == ActiveTab::Settings { egui::Color32::WHITE } else { egui::Color32::from_rgb(203, 213, 225) }),
|
||||||
|
)
|
||||||
|
.fill(if self.active_tab == ActiveTab::Settings { egui::Color32::from_rgb(99, 102, 241) } else { egui::Color32::from_rgb(30, 41, 59) })
|
||||||
|
);
|
||||||
|
if btn_set.clicked() { self.active_tab = ActiveTab::Settings; }
|
||||||
|
|
||||||
|
ui.add_space(30.0);
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// Quick Diagnostics Widget on Sidebar
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(20, 29, 47))
|
||||||
|
.rounding(6.0)
|
||||||
|
.inner_margin(10.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new("STATUS").size(10.0).strong().color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.label(egui::RichText::new(format!("Spooler: {}", &self.spooler_status)).size(11.0).color(egui::Color32::from_rgb(52, 211, 153)));
|
||||||
|
ui.label(egui::RichText::new(format!("Warteschlange: {} Dateie(n)", self.queue_file_count)).size(11.0).color(egui::Color32::from_rgb(56, 189, 248)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main Workspace Area
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// If user is not logged in, prompt for Account Login first
|
||||||
|
if !is_logged_in && self.active_tab != ActiveTab::Account {
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(10.0)
|
||||||
|
.inner_margin(20.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
ui.label(egui::RichText::new("🔒 ANMELDUNG ERFORDERLICH").font(egui::FontId::proportional(18.0)).strong().color(egui::Color32::from_rgb(250, 204, 21)));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label(egui::RichText::new("Melde dich mit deinem Veritas Suite Kundenkonto (https://kanjiv.at/) an, um diesen PC freizuschalten.").color(egui::Color32::from_rgb(203, 213, 225)));
|
||||||
|
ui.add_space(14.0);
|
||||||
|
|
||||||
|
if ui.button("🔐 Jetzt im Account-Tab anmelden").clicked() {
|
||||||
|
self.active_tab = ActiveTab::Account;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(15.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.active_tab {
|
||||||
|
ActiveTab::Dashboard => {
|
||||||
|
// TAB 1: DASHBOARD / REPARATUR-ZENTRALE
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
// Stat Card A
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(8.0)
|
||||||
|
.inner_margin(12.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.set_width(200.0);
|
||||||
|
ui.label(egui::RichText::new("Windows Service Status").size(11.0).color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
ui.label(egui::RichText::new(&self.spooler_status).font(egui::FontId::proportional(16.0)).strong().color(egui::Color32::from_rgb(52, 211, 153)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stat Card B
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(8.0)
|
||||||
|
.inner_margin(12.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.set_width(200.0);
|
||||||
|
ui.label(egui::RichText::new("Blockierte Druckaufträge").size(11.0).color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
ui.label(egui::RichText::new(format!("{} Dateien ({} KB)", self.queue_file_count, self.queue_bytes / 1024)).font(egui::FontId::proportional(16.0)).strong().color(egui::Color32::from_rgb(56, 189, 248)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stat Card C
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(8.0)
|
||||||
|
.inner_margin(12.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.set_width(200.0);
|
||||||
|
ui.label(egui::RichText::new("Erkannte Drucker").size(11.0).color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
ui.label(egui::RichText::new(format!("{} Gerätz(e)", self.installed_printers.len())).font(egui::FontId::proportional(16.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(15.0);
|
||||||
|
|
||||||
|
// Hero Action Box
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(10.0)
|
||||||
|
.inner_margin(16.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new("Druckerspooler-Diagnostik & Ausführung").font(egui::FontId::proportional(15.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add_space(6.0);
|
||||||
|
ui.label(egui::RichText::new("Wähle das gewünschte Reparatur-Protokoll für das Windows-Drucksubsystem:").color(egui::Color32::from_rgb(203, 213, 225)));
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.selectable_value(&mut self.repair_mode, RepairMode::DeepReset, "🔧 Deep-Reset (5-Phasen Neustart)");
|
||||||
|
ui.selectable_value(&mut self.repair_mode, RepairMode::FastQueueClear, "⚡ Schnelle Warteschlangen-Löschung");
|
||||||
|
ui.selectable_value(&mut self.repair_mode, RepairMode::TestPrint, "📄 Testseite Senden");
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(14.0);
|
||||||
|
|
||||||
|
let btn = egui::Button::new(
|
||||||
|
egui::RichText::new("🚀 REPARATUR-PROTOKOLL JETZT STARTEN")
|
||||||
|
.font(egui::FontId::proportional(15.0))
|
||||||
|
.strong()
|
||||||
|
.color(egui::Color32::WHITE),
|
||||||
|
)
|
||||||
|
.fill(egui::Color32::from_rgb(99, 102, 241))
|
||||||
|
.min_size(egui::vec2(320.0, 44.0));
|
||||||
|
|
||||||
|
if ui.add(btn).clicked() {
|
||||||
|
self.execute_repair();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(15.0);
|
||||||
|
|
||||||
|
// Protocol Terminal Console
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new("📋 Live Reparatur-Protokoll:").strong().color(egui::Color32::WHITE));
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
|
if ui.button("🗑️ Log leeren").clicked() {
|
||||||
|
if let Ok(mut logs) = self.log_messages.lock() {
|
||||||
|
logs.clear();
|
||||||
|
logs.push("Log geleert.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(3, 7, 18))
|
||||||
|
.rounding(8.0)
|
||||||
|
.inner_margin(12.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.max_height(200.0)
|
||||||
|
.auto_shrink([false, false])
|
||||||
|
.show(ui, |ui| {
|
||||||
|
if let Ok(logs) = self.log_messages.lock() {
|
||||||
|
for line in logs.iter() {
|
||||||
|
ui.label(egui::RichText::new(line).font(egui::FontId::monospace(13.0)).color(egui::Color32::from_rgb(110, 231, 183)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ActiveTab::Diagnostics => {
|
||||||
|
// TAB 2: SYSTEM-DIAGNOSTIK & DRUCKERLISTE
|
||||||
|
ui.label(egui::RichText::new("📊 Erkannte Windows Drucksysteme & Diagnostik").font(egui::FontId::proportional(18.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
|
||||||
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
for printer in &self.installed_printers {
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(8.0)
|
||||||
|
.inner_margin(12.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new("🖨️").font(egui::FontId::proportional(20.0)));
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new(&printer.name).font(egui::FontId::proportional(15.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
if printer.is_default {
|
||||||
|
ui.label(egui::RichText::new("⭐ Standard-Drucker").size(11.0).color(egui::Color32::from_rgb(250, 204, 21)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.label(egui::RichText::new(format!("Status: {}", printer.status)).size(12.0).color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(8.0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ActiveTab::Account => {
|
||||||
|
// TAB 3: ACCOUNT ANMELDUNG & LIZENZ-CHECK
|
||||||
|
ui.label(egui::RichText::new("🔐 Account-Anmeldung & Lizenz-Aktivierung").font(egui::FontId::proportional(18.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
if let Some(session) = ¤t_session {
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(10.0)
|
||||||
|
.inner_margin(16.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new("🟢 Angemeldetes Kundenkonto").font(egui::FontId::proportional(15.0)).strong().color(egui::Color32::from_rgb(52, 211, 153)));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label(egui::RichText::new(format!("E-Mail: {}", session.email)).font(egui::FontId::proportional(14.0)).color(egui::Color32::WHITE));
|
||||||
|
if let Some(lic) = &session.license_key {
|
||||||
|
ui.label(egui::RichText::new(format!("Aktivierte Lizenz: {}", lic)).color(egui::Color32::from_rgb(56, 189, 248)));
|
||||||
|
}
|
||||||
|
ui.add_space(12.0);
|
||||||
|
ui.label(egui::RichText::new(format!("Anonymisierte Hardware ID (HWID): {}", self.hwid)).size(12.0).color(egui::Color32::from_rgb(148, 163, 184)));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label(egui::RichText::new(format!("TEMP-Session Pfad: {}", get_temp_session_file().to_string_lossy())).size(11.0).color(egui::Color32::from_rgb(100, 116, 139)));
|
||||||
|
ui.add_space(16.0);
|
||||||
|
|
||||||
|
if ui.button("🚪 Abmelden & TEMP-Session Löschen").clicked() {
|
||||||
|
self.logout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(10.0)
|
||||||
|
.inner_margin(16.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new("Veritas Suite Konto-Login").font(egui::FontId::proportional(15.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add_space(6.0);
|
||||||
|
ui.label(egui::RichText::new("Melde dich mit deiner E-Mail und deinem Passwort von https://kanjiv.at/ an.").color(egui::Color32::from_rgb(203, 213, 225)));
|
||||||
|
ui.add_space(14.0);
|
||||||
|
|
||||||
|
let current_alert = self.auth_alert.lock().ok().and_then(|a| a.clone());
|
||||||
|
if let Some((msg, is_success)) = current_alert {
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(if is_success { egui::Color32::from_rgb(6, 78, 59) } else { egui::Color32::from_rgb(153, 27, 27) })
|
||||||
|
.rounding(6.0)
|
||||||
|
.inner_margin(10.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new(msg).color(egui::Color32::WHITE));
|
||||||
|
});
|
||||||
|
ui.add_space(12.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new("E-Mail Adresse:").strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add(egui::TextEdit::singleline(&mut self.login_email).desired_width(260.0));
|
||||||
|
});
|
||||||
|
ui.add_space(8.0);
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new("Passwort:").strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add(egui::TextEdit::singleline(&mut self.login_pass).password(true).desired_width(260.0));
|
||||||
|
});
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
ui.checkbox(&mut self.remember_me, "Angemeldet bleiben (Session-Datei in %TEMP% speichern)");
|
||||||
|
ui.add_space(16.0);
|
||||||
|
|
||||||
|
let btn = egui::Button::new(
|
||||||
|
egui::RichText::new("🔐 ANMELDEN & LIZENZ AUTOMATISCH PRÜFEN")
|
||||||
|
.font(egui::FontId::proportional(14.0))
|
||||||
|
.strong()
|
||||||
|
.color(egui::Color32::WHITE),
|
||||||
|
)
|
||||||
|
.fill(egui::Color32::from_rgb(99, 102, 241))
|
||||||
|
.min_size(egui::vec2(280.0, 40.0));
|
||||||
|
|
||||||
|
if ui.add(btn).clicked() {
|
||||||
|
self.perform_login();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveTab::Settings => {
|
||||||
|
// TAB 4: EINSTELLUNGEN & THEMES
|
||||||
|
ui.label(egui::RichText::new("⚙️ Einstellungen & Design-Theme").font(egui::FontId::proportional(18.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
egui::Frame::none()
|
||||||
|
.fill(egui::Color32::from_rgb(24, 34, 53))
|
||||||
|
.rounding(10.0)
|
||||||
|
.inner_margin(16.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(egui::RichText::new("Erscheinungsbild (Visual Theme)").font(egui::FontId::proportional(15.0)).strong().color(egui::Color32::WHITE));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.selectable_value(&mut self.app_theme, AppTheme::ObsidianIndigo, "🌌 Obsidian Indigo");
|
||||||
|
ui.selectable_value(&mut self.app_theme, AppTheme::CyberNeon, "⚡ Cyber Neon Cyan");
|
||||||
|
ui.selectable_value(&mut self.app_theme, AppTheme::EmeraldShield, "🛡️ Emerald Shield");
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(15.0);
|
||||||
|
ui.checkbox(&mut self.auto_refresh, "Automatische Diagnostik-Aktualisierung alle 5 Sekunden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.request_repaint_after(Duration::from_millis(200));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_running_as_admin() -> bool {
|
||||||
|
let output = Command::new("net")
|
||||||
|
.arg("session")
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
match output {
|
||||||
|
Ok(out) => out.status.success(),
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_spooler_status() -> String {
|
||||||
|
let output = Command::new("sc")
|
||||||
|
.args(["query", "spooler"])
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
if let Ok(out) = output {
|
||||||
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
||||||
|
if stdout.contains("RUNNING") {
|
||||||
|
return "RUNNING (Aktiv)".to_string();
|
||||||
|
} else if stdout.contains("STOPPED") {
|
||||||
|
return "STOPPED (Gestoppt)".to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"UNBEKANNT".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_queue_files() -> (usize, u64) {
|
||||||
|
let spool_dir = Path::new(r"C:\Windows\System32\spool\PRINTERS");
|
||||||
|
let mut count = 0;
|
||||||
|
let mut bytes = 0u64;
|
||||||
|
|
||||||
|
if spool_dir.exists() {
|
||||||
|
if let Ok(entries) = fs::read_dir(spool_dir) {
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
if entry.path().is_file() {
|
||||||
|
count += 1;
|
||||||
|
if let Ok(meta) = entry.metadata() {
|
||||||
|
bytes += meta.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(count, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch_detailed_printers() -> Vec<PrinterInfo> {
|
||||||
|
let output = Command::new("powershell")
|
||||||
|
.args(["-Command", "Get-Printer | Select-Object Name, PrinterStatus, Default | ConvertTo-Json"])
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.output();
|
||||||
|
|
||||||
|
if let Ok(out) = output {
|
||||||
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
||||||
|
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&stdout) {
|
||||||
|
let mut list = Vec::new();
|
||||||
|
if let Some(arr) = val.as_array() {
|
||||||
|
for item in arr {
|
||||||
|
let name = item["Name"].as_str().unwrap_or("Drucker").to_string();
|
||||||
|
let is_default = item["Default"].as_bool().unwrap_or(false);
|
||||||
|
list.push(PrinterInfo {
|
||||||
|
name,
|
||||||
|
status: "Bereit".to_string(),
|
||||||
|
is_default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if val.is_object() {
|
||||||
|
let name = val["Name"].as_str().unwrap_or("Drucker").to_string();
|
||||||
|
let is_default = val["Default"].as_bool().unwrap_or(false);
|
||||||
|
list.push(PrinterInfo {
|
||||||
|
name,
|
||||||
|
status: "Bereit".to_string(),
|
||||||
|
is_default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if !list.is_empty() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![PrinterInfo {
|
||||||
|
name: "Standard Windows Drucksystem".to_string(),
|
||||||
|
status: "Bereit".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 126 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Reference in New Issue
Block a user