diff --git a/backend/app/app.py b/backend/app/app.py index be5e807a..53c10658 100644 --- a/backend/app/app.py +++ b/backend/app/app.py @@ -1883,7 +1883,9 @@ def api_admin_fix_errors(): cwd=os.path.dirname(os.path.abspath(__file__)), capture_output=True, text=True, - timeout=60 + timeout=60, + encoding='utf-8', + errors='replace' ) if result.returncode == 0: diff --git a/backend/app/database/myp.db b/backend/app/database/myp.db index 617e3bb8..b801598a 100644 Binary files a/backend/app/database/myp.db and b/backend/app/database/myp.db differ diff --git a/backend/app/database/myp.db-shm b/backend/app/database/myp.db-shm new file mode 100644 index 00000000..37569ebd Binary files /dev/null and b/backend/app/database/myp.db-shm differ diff --git a/backend/app/database/myp.db-wal b/backend/app/database/myp.db-wal new file mode 100644 index 00000000..ff80eecc Binary files /dev/null and b/backend/app/database/myp.db-wal differ diff --git a/backend/app/models.py b/backend/app/models.py index 7c1905a3..ae124232 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -33,7 +33,7 @@ _cache_lock = threading.Lock() _cache_ttl = {} # Time-to-live für Cache-Einträge # Alle exportierten Modelle -__all__ = ['User', 'Printer', 'Job', 'Stats', 'SystemLog', 'Base', 'GuestRequest', 'UserPermission', 'Notification', 'init_db', 'init_database', 'create_initial_admin', 'get_db_session', 'get_cached_session', 'clear_cache'] +__all__ = ['User', 'Printer', 'Job', 'Stats', 'SystemLog', 'Base', 'GuestRequest', 'UserPermission', 'Notification', 'init_db', 'init_database', 'create_initial_admin', 'get_db_session', 'get_cached_session', 'clear_cache', 'engine'] # ===== DATENBANK-KONFIGURATION MIT WAL UND OPTIMIERUNGEN ===== @@ -998,4 +998,12 @@ def create_initial_admin(email: str = "admin@mercedes-benz.com", password: str = except Exception as e: logger.error(f"Fehler beim Erstellen des Admin-Benutzers: {str(e)}") - return False \ No newline at end of file + return False + +# Engine für Export verfügbar machen +def get_engine(): + """Gibt die optimierte Datenbank-Engine zurück.""" + return create_optimized_engine() + +# Engine-Variable für direkten Import +engine = get_engine() \ No newline at end of file diff --git a/backend/app/utils/debug_cli.py b/backend/app/utils/debug_cli.py index de36ecbe..7e88a227 100644 --- a/backend/app/utils/debug_cli.py +++ b/backend/app/utils/debug_cli.py @@ -268,7 +268,8 @@ def scan_printer(ip_address, timeout=5): cmd = ['ping', '-c', '1', '-W', str(timeout), ip_address] print(f" 🏓 Ping-Test: ", end="") - result = subprocess.run(cmd, capture_output=True, text=True) + result = subprocess.run(cmd, capture_output=True, text=True, + encoding='utf-8', errors='replace') if result.returncode == 0: print(colorize("Erreichbar", "GREEN")) diff --git a/backend/app/utils/debug_drucker_erkennung.py b/backend/app/utils/debug_drucker_erkennung.py index 8e7e1494..4e88014d 100644 --- a/backend/app/utils/debug_drucker_erkennung.py +++ b/backend/app/utils/debug_drucker_erkennung.py @@ -148,10 +148,12 @@ def test_network_connectivity(): try: if platform.system().lower() == "windows": result = subprocess.run(['ping', '-n', '1', '-w', '3000', ip], - capture_output=True, text=True, timeout=5) + capture_output=True, text=True, timeout=5, + encoding='utf-8', errors='replace') else: result = subprocess.run(['ping', '-c', '1', '-W', '3', ip], - capture_output=True, text=True, timeout=5) + capture_output=True, text=True, timeout=5, + encoding='utf-8', errors='replace') if result.returncode == 0: log_message(f" ✅ Ping erfolgreich") diff --git a/backend/app/utils/queue_manager.py b/backend/app/utils/queue_manager.py index 12020dc0..a3583acd 100644 --- a/backend/app/utils/queue_manager.py +++ b/backend/app/utils/queue_manager.py @@ -55,7 +55,9 @@ def check_printer_status(ip_address: str, timeout: int = 5) -> Tuple[str, bool]: cmd, capture_output=True, text=True, - timeout=timeout + 1 + timeout=timeout + 1, + encoding='utf-8', + errors='replace' ) # Wenn Ping erfolgreich ist, als online betrachten diff --git a/backend/app/utils/update_requirements.py b/backend/app/utils/update_requirements.py index ece7447a..84d50ec1 100644 --- a/backend/app/utils/update_requirements.py +++ b/backend/app/utils/update_requirements.py @@ -93,7 +93,8 @@ def get_current_versions() -> Dict[str, str]: try: result = subprocess.run(['pip', 'list', '--format=freeze'], - capture_output=True, text=True) + capture_output=True, text=True, + encoding='utf-8', errors='replace') for line in result.stdout.strip().split('\n'): if '==' in line: @@ -113,7 +114,8 @@ def check_package_availability(package: str, version: str = None) -> bool: else: cmd = ['pip', 'show', package] - result = subprocess.run(cmd, capture_output=True, text=True) + result = subprocess.run(cmd, capture_output=True, text=True, + encoding='utf-8', errors='replace') return result.returncode == 0 except Exception: diff --git a/backend/app/utils/windows_fixes.py b/backend/app/utils/windows_fixes.py index f4d77905..c8c91d96 100644 --- a/backend/app/utils/windows_fixes.py +++ b/backend/app/utils/windows_fixes.py @@ -206,36 +206,148 @@ def is_flask_reloader_process() -> bool: return os.environ.get('WERKZEUG_RUN_MAIN') != 'true' def apply_all_windows_fixes(): - """ - Wendet alle Windows-spezifischen Fixes an. - Sichere Version ohne potentielle Rekursion. - """ + """Wendet alle Windows-spezifischen Fixes an.""" global _windows_fixes_applied - if os.name != 'nt': - windows_logger.debug("⏭️ Keine Windows-Fixes nötig (nicht Windows)") - return - if _windows_fixes_applied: - windows_logger.debug("⏭️ Windows-Fixes bereits angewendet") return + + try: + logger.info("🔧 Wende Windows-spezifische Fixes an...") - windows_logger.info("🔧 Wende Windows-spezifische Fixes an...") - - # Sichere Implementierung - setup_windows_environment() - apply_safe_socket_options() # Neue sichere Socket-Behandlung - fix_windows_socket_issues() # Vereinfachte Socket-Fixes - - # Thread-Manager initialisieren - thread_manager = get_windows_thread_manager() - - # Atexit-Handler nur einmal registrieren - atexit.register(thread_manager.shutdown_all) - - _windows_fixes_applied = True - windows_logger.info("✅ Alle Windows-Fixes erfolgreich angewendet") + # 1. Encoding-Fixes + apply_encoding_fixes() + + # 2. Threading-Fixes + apply_threading_fixes() + + # 3. Signal-Handler-Fixes + apply_signal_fixes() + + # 4. Subprocess-Patch für UTF-8 Encoding + patch_subprocess() + + # 5. Globaler Subprocess-Patch für bereits importierte Module + apply_global_subprocess_patch() + + _windows_fixes_applied = True + logger.info("✅ Alle Windows-Fixes erfolgreich angewendet") + + except Exception as e: + logger.error(f"❌ Fehler beim Anwenden der Windows-Fixes: {str(e)}") + raise e # Automatisch Windows-Fixes beim Import anwenden (nur einmal) if os.name == 'nt' and not _windows_fixes_applied: - apply_all_windows_fixes() \ No newline at end of file + # Sehr früher subprocess-Patch für sofortige Wirkung + try: + import subprocess + if not hasattr(subprocess, '_early_patched'): + patch_subprocess() + subprocess._early_patched = True + logger.info("✅ Früher subprocess-Patch beim Import angewendet") + except Exception as e: + logger.warning(f"⚠️ Früher subprocess-Patch fehlgeschlagen: {str(e)}") + + apply_all_windows_fixes() + +# ===== SICHERE SUBPROCESS-WRAPPER ===== + +def safe_subprocess_run(*args, **kwargs): + """ + Sicherer subprocess.run Wrapper für Windows mit UTF-8 Encoding. + Verhindert charmap-Fehler durch explizite Encoding-Einstellungen. + """ + import subprocess + + # Standard-Encoding für Windows setzen + if 'encoding' not in kwargs and kwargs.get('text', False): + kwargs['encoding'] = 'utf-8' + kwargs['errors'] = 'replace' + + # Timeout-Standard setzen falls nicht vorhanden + if 'timeout' not in kwargs: + kwargs['timeout'] = 30 + + try: + return subprocess.run(*args, **kwargs) + except subprocess.TimeoutExpired as e: + logger.warning(f"Subprocess-Timeout nach {kwargs.get('timeout', 30)}s: {' '.join(args[0]) if args and isinstance(args[0], list) else str(args)}") + raise e + except UnicodeDecodeError as e: + logger.error(f"Unicode-Decode-Fehler in subprocess: {str(e)}") + # Fallback ohne text=True + kwargs_fallback = kwargs.copy() + kwargs_fallback.pop('text', None) + kwargs_fallback.pop('encoding', None) + kwargs_fallback.pop('errors', None) + return subprocess.run(*args, **kwargs_fallback) + except Exception as e: + logger.error(f"Subprocess-Fehler: {str(e)}") + raise e + +# ===== SUBPROCESS-MONKEY-PATCH ===== + +def patch_subprocess(): + """ + Patcht subprocess.run und subprocess.Popen um automatisch sichere Encoding-Einstellungen zu verwenden. + """ + import subprocess + + # Original-Funktionen speichern + if not hasattr(subprocess, '_original_run'): + subprocess._original_run = subprocess.run + subprocess._original_popen = subprocess.Popen + + def patched_run(*args, **kwargs): + # Automatisch UTF-8 Encoding für text=True setzen + if kwargs.get('text', False) and 'encoding' not in kwargs: + kwargs['encoding'] = 'utf-8' + kwargs['errors'] = 'replace' + + return subprocess._original_run(*args, **kwargs) + + def patched_popen(*args, **kwargs): + # Automatisch UTF-8 Encoding für text=True setzen + if kwargs.get('text', False) and 'encoding' not in kwargs: + kwargs['encoding'] = 'utf-8' + kwargs['errors'] = 'replace' + + # Auch für universal_newlines (ältere Python-Versionen) + if kwargs.get('universal_newlines', False) and 'encoding' not in kwargs: + kwargs['encoding'] = 'utf-8' + kwargs['errors'] = 'replace' + + return subprocess._original_popen(*args, **kwargs) + + subprocess.run = patched_run + subprocess.Popen = patched_popen + logger.info("✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)") + +# ===== GLOBALER SUBPROCESS-PATCH ===== + +def apply_global_subprocess_patch(): + """ + Wendet den subprocess-Patch global an, auch für bereits importierte Module. + """ + import sys + import subprocess + + # Patch subprocess direkt + patch_subprocess() + + # Patch auch in bereits importierten Modulen + for module_name, module in sys.modules.items(): + if hasattr(module, 'subprocess') and module.subprocess is subprocess: + # Modul verwendet subprocess - patch es + module.subprocess = subprocess + logger.debug(f"✅ Subprocess in Modul {module_name} gepatcht") + + logger.info("✅ Globaler subprocess-Patch angewendet") + +# ===== EXPORT SAFE SUBPROCESS ===== + +# Sichere subprocess-Funktion exportieren +__all__.append('safe_subprocess_run') +__all__.append('patch_subprocess') +__all__.append('apply_global_subprocess_patch') \ No newline at end of file