#!/usr/bin/env python3 """ MYP (Manage Your Printers) Installer ===================================== Universeller Installer für das MYP Druckerverwaltungssystem. Unterstützt Installation, Kiosk-Modus und Desktop-Icon-Erstellung. Aufruf vom Stammverzeichnis aus: python install.py # Interaktive Installation python install.py --kiosk # Direktinstallation mit Kiosk-Modus python install.py --desktop-icon # Nur Desktop-Icon erstellen python install.py --help # Hilfe anzeigen Autor: Till Tomczak Version: 2.0 """ import os import sys import subprocess import argparse import shutil import stat from pathlib import Path class MYPInstaller: """ Hauptinstaller-Klasse für das MYP System. Verwaltet die komplette Installation inklusive: - Systemabhängigkeiten - Python-Packages - Kiosk-Modus-Konfiguration - Desktop-Integration - Service-Setup """ def __init__(self): self.root_dir = Path(__file__).parent self.backend_dir = self.root_dir / "backend" self.setup_dir = self.backend_dir / "setup" self.is_root = os.geteuid() == 0 if hasattr(os, 'geteuid') else False # System-Erkennung self.is_raspberry_pi = self._detect_raspberry_pi() self.is_debian = self._detect_debian() print(f""" {'='*70} 🏭 MYP (MANAGE YOUR PRINTERS) INSTALLER {'='*70} 🎯 Mercedes-Benz Druckerverwaltungssystem 🔧 Universal-Installer für alle Plattformen 📍 Arbeitsverzeichnis: {self.root_dir} 🔍 SYSTEM-ERKENNUNG: • Raspberry Pi: {'✅ JA' if self.is_raspberry_pi else '❌ NEIN'} • Debian/Ubuntu: {'✅ JA' if self.is_debian else '❌ NEIN'} • Root-Rechte: {'✅ JA' if self.is_root else '❌ NEIN'} {'='*70} """) def _detect_raspberry_pi(self): """Erkennt Raspberry Pi Hardware""" try: with open('/proc/cpuinfo', 'r') as f: cpuinfo = f.read() return 'raspberry pi' in cpuinfo.lower() or 'bcm' in cpuinfo.lower() except: return False def _detect_debian(self): """Erkennt Debian-basierte Systeme""" try: return os.path.exists('/etc/debian_version') except: return False def check_prerequisites(self): """ Prüft Systemvoraussetzungen für die Installation. Returns: bool: True wenn alle Voraussetzungen erfüllt sind """ print("🔍 VORAUSSETZUNGSPRÜFUNG") print("-" * 40) checks = [] # Python-Version if sys.version_info >= (3, 8): checks.append("✅ Python 3.8+ verfügbar") else: checks.append("❌ Python 3.8+ erforderlich") return False # Git verfügbar try: subprocess.run(['git', '--version'], capture_output=True, check=True) checks.append("✅ Git verfügbar") except: checks.append("❌ Git nicht gefunden") # Projekt-Struktur required_paths = [ self.backend_dir, self.backend_dir / "app.py", self.backend_dir / "requirements.txt", self.setup_dir ] for path in required_paths: if path.exists(): checks.append(f"✅ {path.name}") else: checks.append(f"❌ {path.name} fehlt") return False # Systemtools (nur auf Linux) if self.is_debian: tools = ['systemctl', 'nginx', 'ufw'] for tool in tools: try: subprocess.run(['which', tool], capture_output=True, check=True) checks.append(f"✅ {tool} verfügbar") except: checks.append(f"⚠️ {tool} nicht gefunden (wird installiert)") for check in checks: print(f" {check}") print("✅ Voraussetzungen erfüllt\n") return True def install_system_dependencies(self): """ Installiert System-Abhängigkeiten über den Package-Manager. """ if not self.is_debian: print("⚠️ Überspringe System-Dependencies (nicht Debian-basiert)") return True print("📦 SYSTEM-ABHÄNGIGKEITEN INSTALLIEREN") print("-" * 40) # Basis-Pakete packages = [ 'python3-pip', 'python3-venv', 'nodejs', 'npm', 'nginx', 'ufw', 'sqlite3', 'git', 'curl', 'wget', 'unzip' ] # Raspberry Pi spezifische Pakete if self.is_raspberry_pi: packages.extend([ 'chromium-browser', 'xorg', 'openbox', 'lightdm', 'python3-gpiozero' # Für Hardware-Integration ]) try: print(" Aktualisiere Package-Listen...") subprocess.run(['sudo', 'apt', 'update'], check=True) print(f" Installiere {len(packages)} Pakete...") subprocess.run(['sudo', 'apt', 'install', '-y'] + packages, check=True) print("✅ System-Abhängigkeiten installiert\n") return True except subprocess.CalledProcessError as e: print(f"❌ Fehler bei System-Installation: {e}") return False def install_python_dependencies(self): """ Installiert Python-Dependencies aus requirements.txt. """ print("🐍 PYTHON-ABHÄNGIGKEITEN INSTALLIEREN") print("-" * 40) requirements_file = self.backend_dir / "requirements.txt" try: # Virtual Environment erstellen (optional, aber empfohlen) venv_path = self.backend_dir / "venv" if not venv_path.exists(): print(" Erstelle Virtual Environment...") subprocess.run([sys.executable, '-m', 'venv', str(venv_path)], check=True) # Requirements installieren print(" Installiere Python-Packages...") if self.is_debian: # Auf Debian: --break-system-packages für pip subprocess.run([ sys.executable, '-m', 'pip', 'install', '-r', str(requirements_file), '--break-system-packages' ], check=True, cwd=self.backend_dir) else: subprocess.run([ sys.executable, '-m', 'pip', 'install', '-r', str(requirements_file) ], check=True, cwd=self.backend_dir) print("✅ Python-Dependencies installiert\n") return True except subprocess.CalledProcessError as e: print(f"❌ Fehler bei Python-Installation: {e}") return False def build_frontend_assets(self): """ Baut Frontend-Assets (CSS, JavaScript). """ print("🎨 FRONTEND-ASSETS BAUEN") print("-" * 40) try: # npm-Dependencies installieren print(" Installiere npm-Dependencies...") subprocess.run(['npm', 'install'], check=True, cwd=self.backend_dir) # TailwindCSS bauen print(" Baue TailwindCSS...") subprocess.run(['npm', 'run', 'build'], check=True, cwd=self.backend_dir) print("✅ Frontend-Assets gebaut\n") return True except subprocess.CalledProcessError as e: print(f"❌ Fehler beim Asset-Build: {e}") return False def setup_database(self): """ Initialisiert die SQLite-Datenbank. """ print("🗄️ DATENBANK INITIALISIEREN") print("-" * 40) try: # Instance-Verzeichnis erstellen instance_dir = self.backend_dir / "instance" instance_dir.mkdir(exist_ok=True) # Datenbank initialisieren print(" Initialisiere SQLite-Datenbank...") subprocess.run([ sys.executable, '-c', 'from models import init_database; init_database()' ], check=True, cwd=self.backend_dir) print("✅ Datenbank initialisiert\n") return True except subprocess.CalledProcessError as e: print(f"❌ Fehler bei Datenbank-Setup: {e}") return False def setup_kiosk_mode(self): """ Konfiguriert Kiosk-Modus für Raspberry Pi. """ if not self.is_raspberry_pi: print("⚠️ Kiosk-Modus nur auf Raspberry Pi verfügbar") return True print("🖥️ KIOSK-MODUS KONFIGURIEREN") print("-" * 40) try: # Kiosk-Skript erstellen kiosk_script = Path("/home/pi/kiosk.sh") kiosk_content = """#!/bin/bash # MYP Kiosk-Modus Startskript # Startet Chromium im Vollbild-Modus # Warten auf Netzwerk sleep 10 # Bildschirmschoner deaktivieren xset s off xset -dpms xset s noblank # Chromium im Kiosk-Modus starten chromium-browser \\ --no-sandbox \\ --disable-infobars \\ --disable-restore-session-state \\ --disable-session-crashed-bubble \\ --disable-features=TranslateUI \\ --kiosk \\ --app=https://localhost/ """ with open(kiosk_script, 'w') as f: f.write(kiosk_content) # Ausführbar machen kiosk_script.chmod(stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH) # Autostart konfigurieren autostart_dir = Path("/home/pi/.config/autostart") autostart_dir.mkdir(parents=True, exist_ok=True) desktop_entry = autostart_dir / "myp-kiosk.desktop" desktop_content = f"""[Desktop Entry] Type=Application Name=MYP Kiosk Exec={kiosk_script} Hidden=false NoDisplay=false X-GNOME-Autostart-enabled=true """ with open(desktop_entry, 'w') as f: f.write(desktop_content) print(" ✅ Kiosk-Skript erstellt") print(" ✅ Autostart konfiguriert") print("✅ Kiosk-Modus konfiguriert\n") return True except Exception as e: print(f"❌ Fehler bei Kiosk-Setup: {e}") return False def create_desktop_icon(self): """ Erstellt Desktop-Icon für MYP. """ print("🖱️ DESKTOP-ICON ERSTELLEN") print("-" * 40) try: # Desktop-Verzeichnis finden desktop_dirs = [ Path.home() / "Desktop", Path.home() / "Schreibtisch", Path("/home/pi/Desktop") ] desktop_dir = None for dir_path in desktop_dirs: if dir_path.exists(): desktop_dir = dir_path break if not desktop_dir: print("⚠️ Desktop-Verzeichnis nicht gefunden") return True # Icon-Datei kopieren icon_source = self.backend_dir / "static" / "favicon.svg" icon_dest = desktop_dir / "myp-icon.svg" if icon_source.exists(): shutil.copy2(icon_source, icon_dest) # Desktop-Entry erstellen desktop_file = desktop_dir / "MYP-Druckerverwaltung.desktop" desktop_content = f""" [Desktop Entry] Version=1.0 Type=Application Name=MYP Druckerverwaltung Comment=Mercedes-Benz Druckerverwaltungssystem Icon={icon_dest} Exec=python3 {self.backend_dir}/app.py Terminal=false Categories=Application;Office; StartupNotify=true """ with open(desktop_file, 'w') as f: f.write(desktop_content) # Ausführbar machen desktop_file.chmod(stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH) print(f" ✅ Desktop-Icon erstellt: {desktop_file}") print("✅ Desktop-Integration abgeschlossen\n") return True except Exception as e: print(f"❌ Fehler bei Desktop-Icon: {e}") return False def setup_systemd_services(self): """ Installiert systemd-Services für automatischen Start. """ if not self.is_debian or not self.is_root: print("⚠️ Service-Setup erfordert Debian + Root-Rechte") return True print("⚙️ SYSTEMD-SERVICES INSTALLIEREN") print("-" * 40) try: # Service-Dateien kopieren systemd_dir = self.backend_dir / "systemd" if systemd_dir.exists(): for service_file in systemd_dir.glob("*.service"): dest = Path("/etc/systemd/system") / service_file.name shutil.copy2(service_file, dest) print(f" ✅ {service_file.name} installiert") # Services aktivieren services = ["myp-https.service"] if self.is_raspberry_pi: services.append("myp-kiosk.service") for service in services: subprocess.run(['systemctl', 'daemon-reload'], check=True) subprocess.run(['systemctl', 'enable', service], check=True) print(f" ✅ {service} aktiviert") print("✅ SystemD-Services installiert\n") return True except subprocess.CalledProcessError as e: print(f"❌ Fehler bei Service-Setup: {e}") return False def run_full_installation(self): """ Führt die komplette Installation durch. """ print("🚀 VOLLSTÄNDIGE INSTALLATION STARTEN") print("=" * 50) steps = [ ("Voraussetzungen prüfen", self.check_prerequisites), ("System-Abhängigkeiten", self.install_system_dependencies), ("Python-Dependencies", self.install_python_dependencies), ("Frontend-Assets", self.build_frontend_assets), ("Datenbank", self.setup_database), ("SystemD-Services", self.setup_systemd_services) ] for step_name, step_func in steps: print(f"\n📋 SCHRITT: {step_name}") if not step_func(): print(f"\n❌ INSTALLATION FEHLGESCHLAGEN bei: {step_name}") return False print(f""" {'='*70} 🎉 INSTALLATION ERFOLGREICH ABGESCHLOSSEN! {'='*70} ✅ MYP Druckerverwaltungssystem wurde installiert 🔧 NÄCHSTE SCHRITTE: 1. System neustarten (empfohlen) 2. MYP-Service starten: sudo systemctl start myp-https 3. Browser öffnen: https://localhost/ 📝 WICHTIGE HINWEISE: • Standard-Admin: admin / admin123 • Logs: {self.backend_dir}/logs/ • Konfiguration: {self.backend_dir}/config/ 🎯 MERCEDES-BENZ DRUCKERVERWALTUNG BEREIT! {'='*70} """) return True def run_kiosk_installation(self): """ Führt Installation mit Kiosk-Modus durch. """ print("🖥️ KIOSK-INSTALLATION STARTEN") print("=" * 40) # Basis-Installation if not self.run_full_installation(): return False # Kiosk-Modus konfigurieren if not self.setup_kiosk_mode(): return False print(f""" 🎉 KIOSK-INSTALLATION ABGESCHLOSSEN! 🖥️ Das System startet automatisch im Kiosk-Modus • Vollbild-Browser mit MYP • Automatischer Start nach Boot • Bildschirmschoner deaktiviert 🔄 Neustart erforderlich für Kiosk-Aktivierung """) return True def main(): """Haupt-Installer-Funktion mit Argument-Parsing""" parser = argparse.ArgumentParser( description="MYP Druckerverwaltungssystem Installer", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" BEISPIELE: python install.py # Interaktive Installation python install.py --kiosk # Installation mit Kiosk-Modus python install.py --desktop-icon # Nur Desktop-Icon erstellen python install.py --full --kiosk # Vollinstallation + Kiosk SYSTEMANFORDERUNGEN: • Python 3.8+ • Debian/Ubuntu (empfohlen) • Root-Rechte für System-Services • Raspberry Pi für Kiosk-Modus """ ) parser.add_argument('--full', action='store_true', help='Vollständige Installation durchführen') parser.add_argument('--kiosk', action='store_true', help='Kiosk-Modus aktivieren') parser.add_argument('--desktop-icon', action='store_true', help='Desktop-Icon erstellen') parser.add_argument('--no-deps', action='store_true', help='System-Dependencies überspringen') parser.add_argument('--force', action='store_true', help='Installation trotz Warnungen fortsetzen') args = parser.parse_args() # Installer initialisieren installer = MYPInstaller() # Kein Argument = Interaktive Installation if not any([args.full, args.kiosk, args.desktop_icon]): print(""" 🤔 INSTALLATIONSART WÄHLEN: 1) Vollständige Installation (empfohlen) 2) Installation mit Kiosk-Modus (Raspberry Pi) 3) Nur Desktop-Icon erstellen 4) Abbrechen Ihre Wahl [1-4]: """, end="") try: choice = input().strip() if choice == '1': args.full = True elif choice == '2': args.kiosk = True elif choice == '3': args.desktop_icon = True else: print("Installation abgebrochen.") return except KeyboardInterrupt: print("\nInstallation abgebrochen.") return # Installation ausführen success = True if args.desktop_icon: success = installer.create_desktop_icon() elif args.kiosk: success = installer.run_kiosk_installation() elif args.full: success = installer.run_full_installation() if success and installer.is_raspberry_pi: create_kiosk = input("\n🖥️ Kiosk-Modus aktivieren? [j/N]: ").lower().startswith('j') if create_kiosk: installer.setup_kiosk_mode() # Ergebnis if success: print("\n🎉 Installation erfolgreich!") # Desktop-Icon anbieten falls nicht bereits erstellt if not args.desktop_icon and not args.kiosk: create_icon = input("🖱️ Desktop-Icon erstellen? [j/N]: ").lower().startswith('j') if create_icon: installer.create_desktop_icon() sys.exit(0) else: print("\n❌ Installation fehlgeschlagen!") sys.exit(1) if __name__ == "__main__": main()