commit f117cc98c48f9322bcb9be033e23c07da3081d38 Author: KanjiG Date: Tue Jul 28 20:42:54 2026 +0200 Initial commit: Veritas SpoolerReset Pro Windows Spooler Repair Module diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6836888 --- /dev/null +++ b/Cargo.toml @@ -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" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..8cbf23b --- /dev/null +++ b/build.rs @@ -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#" + + + + + + + + + +"#); + + if let Err(e) = res.compile() { + eprintln!("Warning: Failed to embed Windows resource icon / manifest: {}", e); + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a257f56 --- /dev/null +++ b/src/main.rs @@ -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, +} + +#[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 { + 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::(&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>>, + login_email: String, + login_pass: String, + remember_me: bool, + auth_alert: Arc>>, + hwid: String, + is_admin: bool, + log_messages: Arc>>, + active_tab: ActiveTab, + repair_mode: RepairMode, + app_theme: AppTheme, + spooler_status: String, + queue_file_count: usize, + queue_bytes: u64, + installed_printers: Vec, + is_repairing: bool, + is_authenticating: Arc>, + icon_texture: Option, + 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::().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 = None; + + if let Ok(d_resp) = dash_res { + if let Ok(dash_data) = d_resp.json::().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::().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 { + 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::(&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, + }] +} diff --git a/veritas_icon.ico b/veritas_icon.ico new file mode 100644 index 0000000..1cc413c Binary files /dev/null and b/veritas_icon.ico differ diff --git a/veritas_icon.png b/veritas_icon.png new file mode 100644 index 0000000..cb3c6c9 Binary files /dev/null and b/veritas_icon.png differ