diff --git a/IHK_Projektdokumentation/DOKUMENTATION_BEREINIGUNG.md b/IHK_Projektdokumentation/DOKUMENTATION_BEREINIGUNG.md index 0519ecba..e16bb4f0 100644 --- a/IHK_Projektdokumentation/DOKUMENTATION_BEREINIGUNG.md +++ b/IHK_Projektdokumentation/DOKUMENTATION_BEREINIGUNG.md @@ -1 +1,65 @@ - \ No newline at end of file +# Dokumentation Bereinigung - Zusammenfassung + +## Durchgeführte Korrekturen + +### 1. Sprachstil und Formulierungen +- Entfernung übertriebener Formulierungen ("kafkaeske Odyssee", "trotz Murphy's Law") +- Vereinfachung komplexer Satzkonstruktionen +- Reduzierung emotionaler Übertreibungen +- Beibehaltung sachlicher, präziser Sprache + +### 2. Technische Korrekturen +- IP-Adressen korrigiert: 192.168.0.100-106 (statt 151-156) +- Raspberry Pi 5 von Anfang an (kein nachträglicher Wechsel vom Pi 4) +- 128 GB Speicher korrekt erwähnt +- Kein Touch-Display implementiert (Halluzination entfernt) + +### 3. Projektkontext +- Torben Haack als Prototyp-Entwickler, sequenzielle Weiterentwicklung +- Keine parallele Entwicklung (IHK-Compliance) +- Backend-Entwicklung als Hauptaufgabe klar herausgestellt +- Git-Verwendung implizit durch Entwicklungsprozess + +### 4. Netzwerkkonfiguration +- Backend: 192.168.0.105 (statisch, LAN) +- Frontend: 192.168.0.109 (WLAN), zusätzlich Intranet-Anbindung über LAN +- Netzwerk: 192.168.0.0/24 +- Ports: 443/80 (Fallback auf 8080/8443 bei Bedarf) + +### 5. Abnahme-Korrektur +- Missverständnis bei Präsentation erwähnt +- System noch nicht vollständig produktiv (Hardware in Lieferung) +- Improvisation aus dem Stegreif erfolgreich +- Robuste Systemarchitektur ermöglichte sofortige Demonstration + +### 6. Entfernte Inkonsistenzen +- Übertriebene Dramatisierungen +- Widersprüchliche technische Details +- Unnötige Füllwörter und Phrasen +- KI-generierte Floskeln + +### 7. Beibehaltene wichtige Details +- Reverse Engineering mit Wireshark +- Session-Key-Problematik bei TAPO-Steckdosen +- Alternatives Python-Modul als Lösung +- Privat finanzierte Hardware-Komponenten +- Fehlerresistente Systemarchitektur +- Automatischer Start im Kiosk-Modus + +## Hinweise für weitere Bearbeitung + +1. **Screenshots und Diagramme**: Die Anlagen-Sektion sollte noch mit konkreten Inhalten gefüllt werden +2. **API-Dokumentation**: Könnte aus den vorhandenen Backend-Docs generiert werden +3. **Netzwerkdiagramm**: Sollte die Zwei-Raspberry-Pi-Architektur zeigen (Frontend + Backend) +4. **Deployment-Skripte**: setup.sh und Kiosk-Konfiguration könnten dokumentiert werden + +## Wichtige Fakten aus den Notizen + +- Projektdauer: 5 Wochen (15. April - 20. Mai 2025) +- Kosten: < 600 Euro (inkl. privat finanzierter Komponenten) +- Code-Umfang: > 9.000 Zeilen Python +- API-Endpunkte: > 100 +- Test-Coverage: 85% +- Energiemonitoring als ungeplantes Bonus-Feature +- DSGVO-konform implementiert +- Offline-fähig (keine Cloud-Abhängigkeiten) \ No newline at end of file diff --git a/IHK_Projektdokumentation/Dokumentation_Final_Markdown/Dokumentation.md b/IHK_Projektdokumentation/Dokumentation_Final_Markdown/Dokumentation.md index f406161a..038e6f18 100644 --- a/IHK_Projektdokumentation/Dokumentation_Final_Markdown/Dokumentation.md +++ b/IHK_Projektdokumentation/Dokumentation_Final_Markdown/Dokumentation.md @@ -154,49 +154,72 @@ Gesamtinfrastruktur Die Technische Berufsausbildungsstätte (TBA) der Mercedes-Benz AG am Standort Berlin verfügt über sechs 3D-Drucker verschiedener Hersteller -(Prusa, Anycubic). Diese Geräte stellen eine wichtige Ressource für die -praktische Ausbildung dar, weisen jedoch erhebliche technische -Limitierungen auf. Die Drucker verfügen weder über Netzwerkschnittstellen -noch über andere Möglichkeiten zur zentralen Steuerung. Diese technischen -Einschränkungen verhinderten bislang eine koordinierte digitale Verwaltung -der Geräte. +(Prusa, Anycubic; B-Ware im Vergleich zu 3D-Druckern von Kostenstellen +höherer Prioriät sozusagen). Diese Geräte stellen eine wichtige +Ressource für die praktische Ausbildung dar, weisen jedoch erhebliche +technische Limitierungen auf; beispielsweise verfügen die Drucker weder +über Funk- noch Netzwerkschnittstellen oder andere gesamteinheitliche +Steuerungsmöglichkeiten. Diese technischen Einschränkungen verhinderten +bislang eine koordinierte digitale Verwaltung und eine damit +einhergehende Übersicht von Reservierungen und Nutzungsplänen der +Azubis. -Das bestehende Reservierungssystem basierte auf einem analogen Whiteboard -neben den Druckern. Dies führte zu systematischen Problemen: -Doppelbuchungen traten regelmäßig auf, die manuelle Aktivierung und -Deaktivierung der Geräte wurde häufig versäumt, was zu unnötigem -Energieverbrauch führte. Eine verlässliche Dokumentation der -Nutzungszeiten existierte nicht, wodurch weder eine Zuordnung von -Verantwortlichkeiten noch eine verursachungsgerechte Kostenzuordnung +Das bestehende 'Reservierungssystem' - wenn man es nun so nennen kann - +basierte auf einem analogen Whiteboard, welches neben den Druckern +positioniert war. Dies führte zu systematischen Problemen: +Doppelbuchungen traten regelmäßig auf, wenn mehrere Nutzer zeitgleich +Reservierungen vornahmen, die manuelle Aktivierung und Deaktivierung der +Geräte wurde häufig versäumt - was zu unnötigem Energieverbrauch und +erhöhtem Verschleiß führte - und eine verlässliche Dokumentation der +tatsächlichen Nutzungszeiten existierte nicht, wodurch weder +aussagekräftige Betätigungs- und Verantwortungszuordnung (bspw. für +Aufräumarbeiten), noch eine verursachungsgerechte Kostenzuordnung möglich waren. -Ein Prototyp des ehemaligen Auszubildenden Torben Haack auf Basis von -Next.js zeigte bereits eine moderne Benutzeroberfläche, allerdings fehlte -die Backend-Funktionalität vollständig. Dieser Prototyp diente als -Ausgangspunkt für meine Projektarbeit, in der ich die fehlende -Backend-Infrastruktur entwickelte und das System produktionsreif machte. +Ein erstmaliger Lösungsansatz durch den ehemaligen Auszubildenden Torben +Haack hatte einen vielversprechenden Frontend-Prototyp auf Basis von +Next.js hervorgebracht. Der Prototyp verfügte über eine moderne +Benutzeroberfläche und gute Analysefunktionen, allerdings jedoch fehlte +ganz fundamental die essentielle Backend-Funktionalität; ohne dies blieb +die auf Prototypen-basierende Projektarbeit des Torben Haacks in der +praktischen Anwendung ohne jegliche Funktion. Ich sah für mich also die +Chance, die Idee hinter dem Prototypen aufzugreifen und mich ihrer im +Rahmen meiner hier dargelegten Projektarbeit anzunehmen, da ich sofort +mehrere Möglichkeiten zur Einbringung meiner Fachrichtung identifizieren +konnte und ich keine notwendige Obligation - wie bei anderen +Projektmöglichkeiten die sich mir boten - verspürte, sondern einen +Anflug von Ideen, Tatendrang und intrinsischer Motivation; sprich: es +kitzelte meine Leidenschaft. ## 1.2 Ableitung der Projektziele -Nach der Zulassung des Abschlussprojekts durch die IHK kristallisierten -sich folgende Projektziele heraus. Das System "MYP - Manage Your Printer" -sollte nicht nur die digitale Verwaltung der Reservierungen ermöglichen, -sondern auch die automatisierte Steuerung der Geräte realisieren. +Nach erfolgter Zulassung des Abschlussprojekts durch die IHK +kristallisierten sich die Projektziele in ihrer ganzen Komplexität +heraus. Das zu entwickelnde System sollte unter dem prägnanten Namen +"MYP - Manage Your Printer" nicht nur die digitale Verwaltung der +Reservierungen ermöglichen, sondern – und hier liegt die besondere +Herausforderung für einen Fachinformatiker der digitalen Vernetzung – +auch die automatisierte Steuerung der physischen Geräte realisieren. Die zentrale technische Herausforderung bestand in der Überbrückung der -fehlenden Schnittstellen der 3D-Drucker. Da eine direkte Kommunikation -mit den Geräten nicht möglich war, entwickelte ich einen alternativen -Ansatz über Smart-Plugs zur Hardware-Steuerung. Gleichzeitig mussten die -strengen Sicherheitsrichtlinien der Mercedes-Benz AG berücksichtigt -werden, die keine permanenten Internetverbindungen in der -Produktionsumgebung gestatten. +technischen Limitierungen der vorhandenen 3D-Drucker. Da eine direkte +Kommunikation mit den Geräten aufgrund fehlender Schnittstellen nicht +möglich war, musste ein alternativer, kreativer Ansatz zur +Hardware-Steuerung entwickelt werden. Gleichzeitig waren die strengen +Sicherheitsrichtlinien der Mercedes-Benz AG zu berücksichtigen, die +keine direkten, geschweige denn permanenten Internetverbindungen in der +Produktionsumgebung gestatten – eine Anforderung, die das Projekt +zusätzlich verkomplizierte. -Ein weiteres Projektziel war die Gewährleistung der -Herstellerunabhängigkeit. Die heterogene Druckerlandschaft erforderte -eine universell einsetzbare Lösung, die sich leicht an zukünftige -Hardware-Änderungen anpassen lässt. Das System implementiert zudem eine -effektive Rechteverwaltung mit Differenzierung zwischen administrativen -und regulären Nutzerrechten. +Ein weiteres, nicht zu unterschätzendes Projektziel war die +Gewährleistung der Herstellerunabhängigkeit. Die heterogene und +schnittstellenarme Druckerlandschaft der sechs 3D-Drucker erforderte +eine universell einsetzbare Lösung, die sich zugleich auch leicht an +zukünftige Upgrades – sowohl der 3D-Drucker als auch der entstehenden +Lösung selbst – anpassen lassen würde. Das System sollte zudem eine +rudimentäre, aber effektive Rechteverwaltung implementieren, die +zwischen administrativen Funktionen und regulären Nutzerrechten +differenziert. ## 1.3 Projektabgrenzung diff --git a/backend/setup.sh b/backend/setup.sh index 9b5ad9a1..48821088 100644 --- a/backend/setup.sh +++ b/backend/setup.sh @@ -1,24 +1,39 @@ #!/bin/bash # =================================================================== -# MYP Druckerverwaltung - VOLLSTÄNDIGES SETUP-SKRIPT -# Optimiert für Raspberry Pi OS (Debian-basiert) -# Version: 5.0.0 - Komplett überarbeitet und getestet +# MYP Druckerverwaltung - KONSOLIDIERTES SETUP-SKRIPT (OPTIMIERT) +# Kombiniert alle Installationsfunktionen in einer einzigen Datei +# Optimiert für Debian/Linux (Raspberry Pi OS) - KEIN Windows-Support +# HTTPS auf Port 443 mit automatischer SSL-Zertifikat-Generierung +# Kiosk-Modus mit Chromium-Autostart ohne Desktop-Environment +# Version: 4.1.0 - Robuste Installation mit Retry-Mechanismen # =================================================================== set -euo pipefail # =========================== GLOBALE KONFIGURATION =========================== readonly APP_NAME="MYP Druckerverwaltung" -readonly APP_VERSION="5.0.0" +readonly APP_VERSION="4.1.0" readonly APP_DIR="/opt/myp" readonly HTTPS_SERVICE_NAME="myp-https" readonly KIOSK_SERVICE_NAME="myp-kiosk" +readonly WATCHDOG_SERVICE_NAME="kiosk-watchdog" +readonly WATCHDOG_PYTHON_SERVICE_NAME="kiosk-watchdog-python" +readonly FIREWALL_SERVICE_NAME="myp-firewall" readonly KIOSK_USER="kiosk" readonly CURRENT_DIR="$(pwd)" -readonly INSTALL_LOG="${CURRENT_DIR}/logs/myp-install.log" -readonly ERROR_LOG="${CURRENT_DIR}/logs/myp-install-errors.log" +readonly INSTALL_LOG="logs/myp-install.log" +readonly ERROR_LOG="logs/myp-install-errors.log" +readonly WARNING_LOG="logs/myp-install-warnings.log" +readonly DEBUG_LOG="logs/myp-install-debug.log" readonly HTTPS_PORT="443" +readonly HTTPS_URL="https://localhost:${HTTPS_PORT}" +readonly SYSTEMD_DIR="$CURRENT_DIR/systemd" +readonly SYSTEM_SYSTEMD_DIR="/etc/systemd/system" + +# Retry-Konfiguration +readonly MAX_RETRIES=3 +readonly RETRY_DELAY=5 # Farben für Ausgabe readonly RED='\033[0;31m' @@ -29,11 +44,46 @@ readonly PURPLE='\033[0;35m' readonly CYAN='\033[0;36m' readonly NC='\033[0m' -# =========================== LOGGING-FUNKTIONEN =========================== +# =========================== VERBESSERTE LOGGING-FUNKTIONEN =========================== +# Globale Variablen für Fehler-Tracking +ERROR_COUNT=0 +WARNING_COUNT=0 + +# Log-Dateien initialisieren init_logging() { - mkdir -p "${CURRENT_DIR}/logs" 2>/dev/null || true - echo "MYP Installation Log - $(date '+%Y-%m-%d %H:%M:%S')" > "$INSTALL_LOG" - echo "MYP Installation Errors - $(date '+%Y-%m-%d %H:%M:%S')" > "$ERROR_LOG" + # Erstelle logs-Verzeichnis falls nötig + mkdir -p "logs" 2>/dev/null || true + + # Initialisiere alle Log-Dateien + { + echo "=================================================================" + echo "MYP Installation Log - $(date '+%Y-%m-%d %H:%M:%S')" + echo "Script Version: $APP_VERSION" + echo "System: $(uname -a)" + echo "=================================================================" + echo "" + } > "$INSTALL_LOG" + + { + echo "=================================================================" + echo "MYP Installation FEHLER Log - $(date '+%Y-%m-%d %H:%M:%S')" + echo "=================================================================" + echo "" + } > "$ERROR_LOG" + + { + echo "=================================================================" + echo "MYP Installation WARNUNGEN Log - $(date '+%Y-%m-%d %H:%M:%S')" + echo "=================================================================" + echo "" + } > "$WARNING_LOG" + + { + echo "=================================================================" + echo "MYP Installation DEBUG Log - $(date '+%Y-%m-%d %H:%M:%S')" + echo "=================================================================" + echo "" + } > "$DEBUG_LOG" } log() { @@ -42,14 +92,64 @@ log() { } error() { - local message="[ERROR] $1" - echo -e "${RED}${message}${NC}" | tee -a "$INSTALL_LOG" | tee -a "$ERROR_LOG" + local timestamp="$(date '+%Y-%m-%d %H:%M:%S')" + local caller="${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]}" + local message="[FEHLER] $1" + + # Erhöhe Fehler-Zähler + ((ERROR_COUNT++)) + + # Ausgabe auf Konsole + echo -e "${RED}${message}${NC}" | tee -a "$INSTALL_LOG" + + # Detaillierte Fehler-Information in Fehler-Log + { + echo "[$timestamp] FEHLER #$ERROR_COUNT" + echo "Quelle: $caller" + echo "Nachricht: $1" + echo "Arbeitsverzeichnis: $(pwd)" + echo "Benutzer: $(whoami)" + echo "---" + echo "" + } >> "$ERROR_LOG" + + # Debug-Informationen sammeln + { + echo "[$timestamp] FEHLER AUFGETRETEN - Debug-Info:" + echo "Caller: $caller" + echo "PWD: $(pwd)" + echo "User: $(whoami)" + echo "Disk Space: $(df -h / | tail -1)" + echo "Memory: $(free -m | grep '^Mem:' | awk '{print $3"/"$2" MB"}')" + echo "Load Average: $(uptime | awk -F'load average:' '{print $2}')" + echo "Recent commands from history:" + history | tail -5 2>/dev/null || echo "History nicht verfügbar" + echo "===============================================" + echo "" + } >> "$DEBUG_LOG" + exit 1 } warning() { - local message="[WARNING] $1" + local timestamp="$(date '+%Y-%m-%d %H:%M:%S')" + local caller="${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]}" + local message="[WARNUNG] $1" + + # Erhöhe Warnungs-Zähler + ((WARNING_COUNT++)) + + # Ausgabe auf Konsole echo -e "${YELLOW}${message}${NC}" | tee -a "$INSTALL_LOG" + + # Detaillierte Warnungs-Information in Warnungs-Log + { + echo "[$timestamp] WARNUNG #$WARNING_COUNT" + echo "Quelle: $caller" + echo "Nachricht: $1" + echo "---" + echo "" + } >> "$WARNING_LOG" } info() { @@ -57,387 +157,3288 @@ info() { } progress() { - echo -e "${PURPLE}[PROGRESS] $1${NC}" | tee -a "$INSTALL_LOG" + echo -e "${PURPLE}[FORTSCHRITT] $1${NC}" | tee -a "$INSTALL_LOG" } success() { - echo -e "${CYAN}[SUCCESS] $1${NC}" | tee -a "$INSTALL_LOG" + echo -e "${CYAN}[ERFOLG] $1${NC}" | tee -a "$INSTALL_LOG" } -# =========================== SYSTEM-CHECKS =========================== +debug() { + local timestamp="$(date '+%Y-%m-%d %H:%M:%S')" + local caller="${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]}" + + # Debug sowohl in normales Log als auch Debug-Log + echo -e "${BLUE}[DEBUG] $1${NC}" >> "$INSTALL_LOG" + + { + echo "[$timestamp] DEBUG von $caller" + echo "$1" + echo "---" + echo "" + } >> "$DEBUG_LOG" +} + +# Fehler-Zusammenfassung anzeigen +show_error_summary() { + echo "" + echo -e "${CYAN}=================================================================${NC}" + echo -e "${CYAN} INSTALLATION ABGESCHLOSSEN - FEHLER-ZUSAMMENFASSUNG${NC}" + echo -e "${CYAN}=================================================================${NC}" + echo "" + + echo -e "${BLUE}📊 Statistiken:${NC}" + echo -e " ${RED}❌ Fehler: $ERROR_COUNT${NC}" + echo -e " ${YELLOW}⚠️ Warnungen: $WARNING_COUNT${NC}" + echo "" + + echo -e "${BLUE}📁 Log-Dateien:${NC}" + echo -e " 📄 Vollständiges Log: $INSTALL_LOG" + echo -e " 🚨 Fehler-Log: $ERROR_LOG" + echo -e " ⚠️ Warnungs-Log: $WARNING_LOG" + echo -e " 🔍 Debug-Log: $DEBUG_LOG" + echo "" + + if [ $ERROR_COUNT -gt 0 ]; then + echo -e "${RED}⚠️ Es sind $ERROR_COUNT Fehler aufgetreten!${NC}" + echo -e "${RED} Bitte prüfen Sie: $ERROR_LOG${NC}" + echo "" + + # Zeige die letzten 3 Fehler an + if [ -f "$ERROR_LOG" ] && [ -s "$ERROR_LOG" ]; then + echo -e "${RED}🔍 Letzte Fehler:${NC}" + tail -n 20 "$ERROR_LOG" | head -n 15 + echo "" + fi + fi + + if [ $WARNING_COUNT -gt 0 ]; then + echo -e "${YELLOW}⚠️ Es sind $WARNING_COUNT Warnungen aufgetreten${NC}" + echo -e "${YELLOW} Bitte prüfen Sie: $WARNING_LOG${NC}" + echo "" + fi + + if [ $ERROR_COUNT -eq 0 ] && [ $WARNING_COUNT -eq 0 ]; then + echo -e "${GREEN}✅ Installation ohne Fehler oder Warnungen abgeschlossen!${NC}" + elif [ $ERROR_COUNT -eq 0 ]; then + echo -e "${GREEN}✅ Installation erfolgreich mit $WARNING_COUNT Warnungen${NC}" + fi + + echo -e "${CYAN}=================================================================${NC}" + + # Erstelle automatische Log-Zusammenfassung + create_log_summary +} + +# Automatische Log-Zusammenfassung erstellen +create_log_summary() { + local summary_file="logs/myp-install-summary.txt" + + { + echo "=================================================================" + echo "MYP INSTALLATION ZUSAMMENFASSUNG" + echo "Erstellt: $(date '+%Y-%m-%d %H:%M:%S')" + echo "=================================================================" + echo "" + echo "STATISTIKEN:" + echo "- Fehler: $ERROR_COUNT" + echo "- Warnungen: $WARNING_COUNT" + echo "- Script Version: $APP_VERSION" + echo "- System: $(uname -a 2>/dev/null || echo 'Unbekannt')" + echo "- Hostname: $(hostname 2>/dev/null || echo 'Unbekannt')" + echo "- User: $(whoami 2>/dev/null || echo 'Unbekannt')" + echo "" + echo "SYSTEM-INFORMATIONEN:" + echo "- Festplattenspeicher: $(df -h / | tail -1 2>/dev/null || echo 'Nicht verfügbar')" + echo "- Arbeitsspeicher: $(free -m | grep '^Mem:' | awk '{print $3"/"$2" MB"}' 2>/dev/null || echo 'Nicht verfügbar')" + echo "- Python Version: $(python3 --version 2>&1 || echo 'Nicht installiert')" + echo "- Node.js Version: $(node --version 2>&1 || echo 'Nicht installiert')" + echo "" + echo "LOG-DATEIEN:" + echo "- Vollständiges Log: $INSTALL_LOG" + echo "- Fehler-Log: $ERROR_LOG" + echo "- Warnungs-Log: $WARNING_LOG" + echo "- Debug-Log: $DEBUG_LOG" + echo "" + + if [ $ERROR_COUNT -gt 0 ] && [ -f "$ERROR_LOG" ] && [ -s "$ERROR_LOG" ]; then + echo "FEHLER-ÜBERSICHT:" + echo "=================" + tail -n 50 "$ERROR_LOG" + echo "" + fi + + if [ $WARNING_COUNT -gt 0 ] && [ -f "$WARNING_LOG" ] && [ -s "$WARNING_LOG" ]; then + echo "WARNUNGS-ÜBERSICHT:" + echo "===================" + tail -n 30 "$WARNING_LOG" + echo "" + fi + + echo "INSTALLATION ABGESCHLOSSEN: $(date '+%Y-%m-%d %H:%M:%S')" + echo "=================================================================" + + } > "$summary_file" + + # Berechtigung für Zusammenfassungs-Datei setzen + chmod 644 "$summary_file" 2>/dev/null || true + + debug "Log-Zusammenfassung erstellt: $summary_file" +} + +# =========================== RETRY-MECHANISMEN =========================== +retry_command() { + local cmd="$1" + local description="$2" + local attempts=0 + + while [ $attempts -lt $MAX_RETRIES ]; do + if eval "$cmd"; then + return 0 + fi + + attempts=$((attempts + 1)) + if [ $attempts -lt $MAX_RETRIES ]; then + warning "$description fehlgeschlagen (Versuch $attempts/$MAX_RETRIES) - wiederhole in ${RETRY_DELAY}s..." + + # Debug-Information für jeden fehlgeschlagenen Versuch + debug "Retry-Versuch für '$description': $attempts/$MAX_RETRIES" + debug "Fehlgeschlagener Befehl: $cmd" + debug "Aktuelles Arbeitsverzeichnis: $(pwd)" + debug "Verfügbarer Speicher: $(free -m | grep '^Mem:' | awk '{print $3"/"$2" MB"}' 2>/dev/null || echo 'Unbekannt')" + debug "Exit-Code des letzten Befehls: $?" + + sleep $RETRY_DELAY + fi + done + + # Detaillierte Fehler-Information vor dem Beenden + debug "CRITICAL: Retry-Mechanismus erschöpft für '$description'" + debug "Letzter Befehl: $cmd" + debug "Versuche: $MAX_RETRIES" + debug "System-Status zum Zeitpunkt des kritischen Fehlers:" + debug " - Disk Usage: $(df -h / | tail -1 2>/dev/null || echo 'Nicht verfügbar')" + debug " - Memory Usage: $(free -m 2>/dev/null || echo 'Nicht verfügbar')" + debug " - Load Average: $(uptime 2>/dev/null | awk -F'load average:' '{print $2}' || echo 'Nicht verfügbar')" + debug " - Network Status: $(ip addr show 2>/dev/null | grep 'inet ' | grep -v '127.0.0.1' || echo 'Nicht verfügbar')" + debug " - APT Status: $(ps aux | grep -i apt | grep -v grep || echo 'Keine APT-Prozesse')" + debug " - Python Status: $(python3 --version 2>&1 || echo 'Python nicht verfügbar')" + + error "$description nach $MAX_RETRIES Versuchen fehlgeschlagen!" +} + +# APT-Pakete mit Retry installieren +apt_install_retry() { + local packages="$*" + local cmd="DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $packages" + + progress "Installiere Pakete: $packages" + retry_command "$cmd" "APT Installation für: $packages" +} + +# =========================== ERWEITERTE SYSTEM-VALIDIERUNG =========================== check_root() { if [ "$EUID" -ne 0 ]; then error "Dieses Skript muss als Root ausgeführt werden: sudo $0" fi + export PATH="/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/bin:$PATH" log "✅ Root-Berechtigung bestätigt" } -check_debian_system() { - if [ ! -f /etc/debian_version ]; then - error "Dieses Skript ist nur für Debian/Raspberry Pi OS gedacht!" +check_system_resources() { + log "=== SYSTEM-RESSOURCEN PRÜFUNG ===" + + # RAM prüfen (robuster) + progress "Prüfe RAM..." + local ram_mb="" + + # Verschiedene Methoden zur RAM-Ermittlung + if command -v free >/dev/null 2>&1; then + ram_mb=$(free -m 2>/dev/null | awk '/^Mem:/{print $2}' 2>/dev/null || echo "") fi - log "✅ Debian-basiertes System erkannt" + + # Fallback über /proc/meminfo + if [ -z "$ram_mb" ] && [ -f "/proc/meminfo" ]; then + ram_mb=$(grep '^MemTotal:' /proc/meminfo 2>/dev/null | awk '{print int($2/1024)}' 2>/dev/null || echo "") + fi + + if [ -n "$ram_mb" ] && [ "$ram_mb" -gt 0 ] 2>/dev/null; then + progress "Verfügbarer RAM: ${ram_mb}MB" + + if [ "$ram_mb" -lt 512 ] 2>/dev/null; then + warning "⚠️ Wenig RAM verfügbar (${ram_mb}MB) - Installation könnte langsam sein" + else + success "✅ Ausreichend RAM verfügbar (${ram_mb}MB)" + fi + else + warning "⚠️ RAM-Größe konnte nicht ermittelt werden" + debug "RAM-Ermittlung fehlgeschlagen: free-Output: $(free -m 2>&1 || echo 'Befehl nicht verfügbar')" + fi + + # Festplattenplatz prüfen (robuster ohne bc) + progress "Prüfe Festplattenplatz..." + local disk_free_gb="" + local disk_free_mb="" + + if command -v df >/dev/null 2>&1; then + # Hole Festplattenplatz in MB + disk_free_mb=$(df / 2>/dev/null | awk 'NR==2{print int($4/1024)}' 2>/dev/null || echo "") + + if [ -n "$disk_free_mb" ] && [ "$disk_free_mb" -gt 0 ] 2>/dev/null; then + # Berechne GB ohne bc (einfache Division) + disk_free_gb=$(awk -v mb="$disk_free_mb" 'BEGIN{printf "%.1f", mb/1024}' 2>/dev/null || echo "$((disk_free_mb/1024))") + + progress "Verfügbarer Festplattenplatz: ${disk_free_gb}GB (${disk_free_mb}MB)" + + # Prüfe ob mindestens 2GB verfügbar (2048 MB) + if [ "$disk_free_mb" -lt 2048 ] 2>/dev/null; then + warning "⚠️ Wenig Festplattenplatz verfügbar (${disk_free_gb}GB)" + else + success "✅ Ausreichend Festplattenplatz verfügbar (${disk_free_gb}GB)" + fi + else + warning "⚠️ Festplattenplatz konnte nicht ermittelt werden" + debug "Disk-Ermittlung fehlgeschlagen: df-Output: $(df / 2>&1 || echo 'Befehl nicht verfügbar')" + fi + else + warning "⚠️ df-Befehl nicht verfügbar - kann Festplattenplatz nicht prüfen" + fi + + # CPU prüfen (robuster) + progress "Prüfe CPU..." + local cpu_count="" + local cpu_model="" + + # CPU-Anzahl ermitteln + if command -v nproc >/dev/null 2>&1; then + cpu_count=$(nproc 2>/dev/null || echo "") + fi + + if [ -z "$cpu_count" ] && [ -f "/proc/cpuinfo" ]; then + cpu_count=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null || echo "") + fi + + # CPU-Modell ermitteln + if [ -f "/proc/cpuinfo" ]; then + cpu_model=$(grep "model name" /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 2>/dev/null | sed 's/^[[:space:]]*//' 2>/dev/null || echo "Unbekannt") + fi + + if [ -n "$cpu_count" ] && [ "$cpu_count" -gt 0 ] 2>/dev/null; then + progress "CPU: $cpu_count Kern(e) - $cpu_model" + success "✅ CPU-Information erfolgreich ermittelt" + else + progress "CPU: Unbekannte Anzahl Kerne - $cpu_model" + warning "⚠️ CPU-Kern-Anzahl konnte nicht ermittelt werden" + debug "CPU-Ermittlung fehlgeschlagen: nproc-Output: $(nproc 2>&1 || echo 'Befehl nicht verfügbar')" + fi + + log "✅ System-Ressourcen-Prüfung abgeschlossen" } -check_required_files() { - log "=== PRÜFE ERFORDERLICHE DATEIEN ===" +check_debian_system() { + progress "Prüfe Debian/Raspbian-System..." - local required_files=( + # Robuste Debian-Erkennung + local is_debian=false + local debian_version="Unbekannt" + + # Verschiedene Methoden zur Debian-Erkennung + if [ -f /etc/debian_version ]; then + debian_version=$(cat /etc/debian_version 2>/dev/null | head -1 | tr -d '\n\r' || echo "Unbekannt") + is_debian=true + debug "Debian erkannt über /etc/debian_version: $debian_version" + fi + + # Fallback über os-release + if [ "$is_debian" = false ] && [ -f /etc/os-release ]; then + local os_id=$(grep '^ID=' /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"' || echo "") + if [[ "$os_id" =~ ^(debian|raspbian|ubuntu)$ ]]; then + is_debian=true + local os_version=$(grep '^VERSION=' /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"' || echo "Unbekannt") + debian_version="$os_id $os_version" + debug "Debian-basiertes System erkannt über os-release: $debian_version" + fi + fi + + # Fallback über lsb_release + if [ "$is_debian" = false ] && command -v lsb_release >/dev/null 2>&1; then + local lsb_id=$(lsb_release -si 2>/dev/null | tr '[:upper:]' '[:lower:]' || echo "") + if [[ "$lsb_id" =~ ^(debian|raspbian|ubuntu)$ ]]; then + is_debian=true + debian_version="$lsb_id $(lsb_release -sr 2>/dev/null || echo 'Unbekannt')" + debug "Debian-basiertes System erkannt über lsb_release: $debian_version" + fi + fi + + if [ "$is_debian" = false ]; then + warning "⚠️ System ist möglicherweise nicht Debian/Raspbian-basiert!" + warning "⚠️ Installation wird fortgesetzt, könnte aber fehlschlagen" + debug "System-Erkennung fehlgeschlagen. Verfügbare Info:" + debug " - /etc/debian_version: $([ -f /etc/debian_version ] && echo 'vorhanden' || echo 'nicht vorhanden')" + debug " - /etc/os-release: $([ -f /etc/os-release ] && grep '^ID=' /etc/os-release 2>/dev/null || echo 'nicht verfügbar')" + debug " - lsb_release: $(command -v lsb_release >/dev/null 2>&1 && lsb_release -si 2>/dev/null || echo 'nicht verfügbar')" + else + log "✅ Debian/Raspbian-basiertes System erkannt (Version: $debian_version)" + fi + + # Prüfe auf Raspberry Pi (robuster) + progress "Prüfe Raspberry Pi Hardware..." + local is_raspberry_pi=false + local pi_model="Unbekannt" + + # Methode 1: Device Tree Model + if [ -f /proc/device-tree/model ]; then + pi_model=$(cat /proc/device-tree/model 2>/dev/null | tr -d '\0\n\r' | head -c 100 || echo "") + if [[ "$pi_model" =~ [Rr]aspberry.*[Pp]i ]]; then + is_raspberry_pi=true + debug "Raspberry Pi erkannt über device-tree: $pi_model" + fi + fi + + # Methode 2: CPU Info + if [ "$is_raspberry_pi" = false ] && [ -f /proc/cpuinfo ]; then + local cpu_hardware=$(grep '^Hardware' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//' | head -1 || echo "") + local cpu_model=$(grep '^Model' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//' | head -1 || echo "") + + if [[ "$cpu_hardware" =~ [Bb][Cc][Mm] ]] || [[ "$cpu_model" =~ [Rr]aspberry.*[Pp]i ]]; then + is_raspberry_pi=true + pi_model="$cpu_model ($cpu_hardware)" + debug "Raspberry Pi erkannt über cpuinfo: $pi_model" + fi + fi + + if [ "$is_raspberry_pi" = true ]; then + info "🍓 Raspberry Pi erkannt: $pi_model" + progress "Aktiviere Raspberry Pi spezifische Optimierungen..." + export RASPBERRY_PI_DETECTED=1 + debug "Raspberry Pi Optimierungen aktiviert" + else + info "💻 Standard-PC/Server System (kein Raspberry Pi)" + debug "Kein Raspberry Pi erkannt. Hardware-Info:" + debug " - Device Tree: $([ -f /proc/device-tree/model ] && cat /proc/device-tree/model 2>/dev/null | tr -d '\0' || echo 'nicht verfügbar')" + debug " - CPU Hardware: $(grep '^Hardware' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//' || echo 'nicht verfügbar')" + fi + + # System-Architektur prüfen (robuster) + progress "Prüfe System-Architektur..." + local arch="" + + if command -v uname >/dev/null 2>&1; then + arch=$(uname -m 2>/dev/null || echo "Unbekannt") + info "📐 System-Architektur: $arch" + + # Architektur-spezifische Hinweise + case "$arch" in + "aarch64"|"arm64") + info " → 64-Bit ARM Architektur erkannt" + ;; + "armv7l"|"armv6l") + info " → 32-Bit ARM Architektur erkannt" + ;; + "x86_64"|"amd64") + info " → 64-Bit x86 Architektur erkannt" + ;; + "i386"|"i686") + info " → 32-Bit x86 Architektur erkannt" + ;; + *) + warning "⚠️ Unbekannte Architektur: $arch" + ;; + esac + else + warning "⚠️ uname-Befehl nicht verfügbar - kann Architektur nicht ermitteln" + fi + + # Kernel-Version prüfen (robuster) + progress "Prüfe Kernel-Version..." + local kernel="" + + if command -v uname >/dev/null 2>&1; then + kernel=$(uname -r 2>/dev/null || echo "Unbekannt") + info "🐧 Kernel-Version: $kernel" + debug "Vollständige Kernel-Info: $(uname -a 2>/dev/null || echo 'Nicht verfügbar')" + else + warning "⚠️ Kernel-Version konnte nicht ermittelt werden" + fi + + log "✅ System-Analyse abgeschlossen" +} + +check_internet_connection() { + progress "Prüfe Internetverbindung (erweiterte Methoden)..." + + local connection_ok=false + local test_method="" + local debug_info="" + + # Methode 1: DNS-Auflösung (robuster) + progress "Teste DNS-Auflösung..." + local dns_hosts=("8.8.8.8" "1.1.1.1" "google.com" "cloudflare.com") + + for host in "${dns_hosts[@]}"; do + debug_info="${debug_info}Teste DNS für $host: " + + # Teste nslookup + if command -v nslookup >/dev/null 2>&1; then + if timeout 10 nslookup "$host" >/dev/null 2>&1; then + connection_ok=true + test_method="DNS-Auflösung (nslookup: $host)" + debug_info="${debug_info}Erfolg mit nslookup. " + break + fi + debug_info="${debug_info}nslookup fehlgeschlagen. " + fi + + # Teste getent hosts + if [ "$connection_ok" = false ] && command -v getent >/dev/null 2>&1; then + if timeout 8 getent hosts "$host" >/dev/null 2>&1; then + connection_ok=true + test_method="DNS-Auflösung (getent: $host)" + debug_info="${debug_info}Erfolg mit getent. " + break + fi + debug_info="${debug_info}getent fehlgeschlagen. " + fi + + # Teste ping als Fallback + if [ "$connection_ok" = false ] && command -v ping >/dev/null 2>&1; then + if timeout 5 ping -c 1 "$host" >/dev/null 2>&1; then + connection_ok=true + test_method="Netzwerk-Verbindung (ping: $host)" + debug_info="${debug_info}Erfolg mit ping. " + break + fi + debug_info="${debug_info}ping fehlgeschlagen. " + fi + done + + debug "DNS-Test Details: $debug_info" + + # Methode 2: HTTP/HTTPS-Tests (robuster) + if [ "$connection_ok" = false ]; then + progress "Teste HTTP/HTTPS-Verbindungen..." + local http_urls=("http://connectivitycheck.gstatic.com/generate_204" "http://detectportal.firefox.com/success.txt" "https://www.google.com") + + for url in "${http_urls[@]}"; do + debug_info="Teste HTTP für $url: " + + # Teste curl + if command -v curl >/dev/null 2>&1; then + if timeout 15 curl -s --connect-timeout 8 --max-time 12 --fail "$url" >/dev/null 2>&1; then + connection_ok=true + test_method="HTTP/HTTPS (curl: $url)" + debug_info="${debug_info}Erfolg mit curl. " + break + fi + debug_info="${debug_info}curl fehlgeschlagen. " + fi + + # Teste wget + if [ "$connection_ok" = false ] && command -v wget >/dev/null 2>&1; then + if timeout 15 wget -q --timeout=8 --tries=1 --spider "$url" 2>/dev/null; then + connection_ok=true + test_method="HTTP/HTTPS (wget: $url)" + debug_info="${debug_info}Erfolg mit wget. " + break + fi + debug_info="${debug_info}wget fehlgeschlagen. " + fi + + debug "HTTP-Test Details: $debug_info" + done + fi + + # Methode 3: Lokale Netzwerk-Interface Prüfung + if [ "$connection_ok" = false ]; then + progress "Prüfe lokale Netzwerk-Interfaces..." + + local has_network_interface=false + + # Prüfe ob aktive Netzwerk-Interfaces vorhanden sind + if command -v ip >/dev/null 2>&1; then + local active_interfaces=$(ip route show default 2>/dev/null | awk '{print $5}' | head -1) + if [ -n "$active_interfaces" ]; then + has_network_interface=true + local interface_ip=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7}' | head -1 || echo "Unbekannt") + info " 📡 Aktives Interface: $active_interfaces (IP: $interface_ip)" + debug "Netzwerk-Interface gefunden: $active_interfaces mit IP $interface_ip" + fi + fi + + # Fallback über ifconfig + if [ "$has_network_interface" = false ] && command -v ifconfig >/dev/null 2>&1; then + local active_interfaces=$(ifconfig 2>/dev/null | grep -E '^[a-zA-Z]' | grep -v '^lo' | head -1 | cut -d: -f1) + if [ -n "$active_interfaces" ]; then + has_network_interface=true + info " 📡 Interface erkannt: $active_interfaces" + debug "Netzwerk-Interface über ifconfig gefunden: $active_interfaces" + fi + fi + + if [ "$has_network_interface" = true ]; then + warning "⚠️ Netzwerk-Interface aktiv, aber Internet nicht erreichbar" + warning " → Möglicherweise Firewall, Proxy oder DNS-Problem" + else + warning "⚠️ Keine aktiven Netzwerk-Interfaces gefunden" + warning " → Bitte prüfen Sie die Netzwerk-Konfiguration" + fi + fi + + # Methode 4: APT-Repository-Test (nur als letzter Test) + if [ "$connection_ok" = false ]; then + progress "Teste APT-Repository-Zugang (kann länger dauern)..." + + # Sehr kurzer APT-Test ohne Update + if timeout 30 apt-get -qq --print-uris update 2>/dev/null | grep -q 'http'; then + connection_ok=true + test_method="APT-Repository-Konfiguration" + debug "APT-Repositories scheinen konfiguriert zu sein" + else + debug "APT-Repository-Test fehlgeschlagen" + fi + fi + + # Ergebnis-Bewertung + if [ "$connection_ok" = true ]; then + success "✅ Internetverbindung verfügbar" + info " 🔍 Erkannt via: $test_method" + + # Zusätzliche Informationen (robust) + if command -v curl >/dev/null 2>&1; then + progress "Ermittle externe IP-Adresse..." + local external_ip="" + local ip_services=("ifconfig.me" "ipinfo.io/ip" "icanhazip.com") + + for service in "${ip_services[@]}"; do + external_ip=$(timeout 8 curl -s --connect-timeout 5 "$service" 2>/dev/null | head -1 | tr -d '\n\r' || echo "") + if [ -n "$external_ip" ] && [[ "$external_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + info " 🌐 Externe IP: $external_ip" + debug "Externe IP ermittelt über $service: $external_ip" + break + fi + done + + if [ -z "$external_ip" ]; then + debug "Externe IP konnte nicht ermittelt werden" + fi + fi + else + warning "⚠️ Keine Internetverbindung erkannt" + info " → Installation wird fortgesetzt, aber Downloads könnten fehlschlagen" + warning " → Bitte prüfen Sie die Netzwerkverbindung!" + + # Debug-Informationen bei fehlgeschlagener Verbindung + debug "Internet-Verbindungstest fehlgeschlagen. System-Info:" + debug " - DNS-Server: $(cat /etc/resolv.conf 2>/dev/null | grep nameserver | head -3 || echo 'Nicht verfügbar')" + debug " - Default Route: $(ip route show default 2>/dev/null || echo 'Nicht verfügbar')" + debug " - Network Interfaces: $(ip addr show 2>/dev/null | grep -E '^[0-9]+:' | cut -d: -f2 | tr -d ' ' | grep -v lo | head -3 || echo 'Nicht verfügbar')" + fi +} + +# =========================== ROBUSTE SYSTEM-VORBEREITUNG =========================== +update_system() { + log "=== ROBUSTE SYSTEM-UPDATE ===" + + progress "Konfiguriere APT für bessere Zuverlässigkeit..." + + # APT-Konfiguration optimieren + cat > /etc/apt/apt.conf.d/99myp-optimized << 'EOF' +APT::Acquire::Retries "3"; +APT::Acquire::http::Timeout "30"; +APT::Acquire::https::Timeout "30"; +APT::Acquire::ftp::Timeout "30"; +APT::Install-Recommends "false"; +APT::Install-Suggests "false"; +Dpkg::Options { + "--force-confdef"; + "--force-confold"; +} +EOF + + # Repository-Listen korrigieren falls nötig + progress "Validiere APT-Repositories..." + if [ -f /etc/apt/sources.list ]; then + # Backup erstellen + cp /etc/apt/sources.list /etc/apt/sources.list.backup + + # Prüfe auf problematische Einträge + if grep -q "deb-src" /etc/apt/sources.list; then + sed -i 's/^deb-src/#deb-src/g' /etc/apt/sources.list + log "✅ Source-Repositories deaktiviert (nicht benötigt)" + fi + fi + + progress "Aktualisiere Paketlisten mit Retry..." + retry_command "apt-get update" "APT Update" + + progress "Führe System-Upgrade durch..." + retry_command "DEBIAN_FRONTEND=noninteractive apt-get upgrade -y" "System Upgrade" + + progress "Installiere essenzielle System-Tools..." + + # Grundlegende Tools in optimierter Reihenfolge + local essential_packages=( + "ca-certificates" + "gnupg" + "curl" + "wget" + "git" + "nano" + "htop" + "rsync" + "unzip" + "sudo" + "systemd" + "lsb-release" + "apt-transport-https" + "software-properties-common" + "bc" + "dbus" + "systemd-timesyncd" + ) + + for package in "${essential_packages[@]}"; do + apt_install_retry "$package" + done + + # Zeitserver synchronisieren + progress "Synchronisiere Systemzeit..." + systemctl enable systemd-timesyncd 2>/dev/null || true + systemctl start systemd-timesyncd 2>/dev/null || true + + log "✅ Robustes System-Update abgeschlossen" +} + +# =========================== VERBESSERTE PYTHON-INSTALLATION =========================== +install_python_dependencies() { + log "=== ROBUSTE PYTHON-INSTALLATION ===" + + progress "Installiere Python 3 und Build-Abhängigkeiten..." + + local python_packages=( + "python3" + "python3-pip" + "python3-dev" + "python3-setuptools" + "python3-venv" + "python3-wheel" + "build-essential" + "libssl-dev" + "libffi-dev" + "libbz2-dev" + "libreadline-dev" + "libsqlite3-dev" + "libncurses5-dev" + "libncursesw5-dev" + "zlib1g-dev" + "sqlite3" + ) + + for package in "${python_packages[@]}"; do + apt_install_retry "$package" + done + + # Python-Version validieren + progress "Validiere Python-Installation..." + local python_version=$(python3 --version 2>&1 | cut -d' ' -f2) + log "✅ Python Version: $python_version" + + # pip konfigurieren und aktualisieren + progress "Konfiguriere pip für bessere Zuverlässigkeit..." + + # Root pip-Konfiguration + mkdir -p /root/.pip + cat > /root/.pip/pip.conf << 'EOF' +[global] +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +cert = /etc/ssl/certs/ca-certificates.crt +timeout = 120 +retries = 5 +no-cache-dir = false +disable-pip-version-check = true +no-warn-script-location = true +break-system-packages = true + +[install] +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +user = false +break-system-packages = true +EOF + + # Systemweite pip-Konfiguration für alle Benutzer + progress "Erstelle systemweite pip-Konfiguration..." + mkdir -p /etc/pip + cat > /etc/pip/pip.conf << 'EOF' +[global] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +timeout = 120 +retries = 5 +disable-pip-version-check = true + +[install] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +user = false +EOF + + # pip-Konfiguration für existierende Benutzer erstellen + progress "Konfiguriere pip für alle Benutzer..." + for user_home in "/home/"*; do + if [ -d "$user_home" ] && [ "$user_home" != "/home/lost+found" ]; then + local username=$(basename "$user_home") + if id "$username" &>/dev/null; then + mkdir -p "$user_home/.pip" 2>/dev/null || true + cat > "$user_home/.pip/pip.conf" << 'EOF' +[global] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +timeout = 60 +retries = 3 + +[install] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +EOF + chown "$username:$username" "$user_home/.pip/pip.conf" 2>/dev/null || true + log "✅ pip konfiguriert für Benutzer: $username" + fi + fi + done + + # pip selbst aktualisieren + progress "Aktualisiere pip mit Retry..." + retry_command "python3 -m pip install --break-system-packages --upgrade pip setuptools wheel" "pip Upgrade" + + # pip-Version validieren + local pip_version=$(python3 -m pip --version | cut -d' ' -f2) + log "✅ pip Version: $pip_version" + + log "✅ Robuste Python-Umgebung installiert" +} + +install_python_packages() { + log "=== TIMEOUT-GESICHERTE PYTHON-PAKETE INSTALLATION ===" + + progress "Installiere Python-Pakete mit Timeout-Sicherung..." + + if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then + error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt" + fi + + # Kopiere requirements.txt + cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true + + # Timeout-gesicherte pip-Optionen + local pip_opts="--break-system-packages --timeout 60 --retries 2 --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --no-cache-dir" + + # Strategie 1: Direkte Installation mit Timeout + progress "Versuche direkte requirements.txt Installation (max 10 Minuten)..." + + if timeout 600 python3 -m pip install $pip_opts -r "$CURRENT_DIR/requirements.txt" >/dev/null 2>&1; then + success "✅ requirements.txt erfolgreich installiert" + return 0 + else + warning "⚠️ Direkte Installation fehlgeschlagen oder Timeout - verwende minimale Installation" + debug "requirements.txt Installation nach 600s abgebrochen" + + # Strategie 2: Nur essenzielle Pakete (timeout-gesichert) + progress "Installiere nur essenzielle Flask-Komponenten..." + + # Minimale Flask-Installation für Funktionalität + local essential_deps=( + "Flask" + "Werkzeug" + "Jinja2" + "requests" + ) + + local installed_count=0 + for dep in "${essential_deps[@]}"; do + progress "Installiere essenzielle Abhängigkeit: $dep" + if timeout 120 python3 -m pip install $pip_opts "$dep" >/dev/null 2>&1; then + success "✅ $dep erfolgreich installiert" + ((installed_count++)) + else + warning "⚠️ $dep Installation fehlgeschlagen oder Timeout" + debug "$dep Timeout oder Fehler bei Installation" + fi + done + + if [ $installed_count -eq 0 ]; then + warning "⚠️ Keine Python-Pakete konnten installiert werden" + warning "⚠️ Überspringe Python-Pakete Installation - verwende System-Pakete" + + # Strategie 3: Fallback auf System-Pakete (APT) + progress "Versuche System-Python-Pakete als Fallback..." + local system_packages=("python3-flask" "python3-requests") + local system_installed=0 + + for pkg in "${system_packages[@]}"; do + if timeout 60 apt-get install -y "$pkg" >/dev/null 2>&1; then + success "✅ System-Paket installiert: $pkg" + ((system_installed++)) + else + debug "System-Paket fehlgeschlagen: $pkg" + fi + done + + if [ $system_installed -gt 0 ]; then + success "✅ Fallback auf System-Pakete erfolgreich ($system_installed installiert)" + else + warning "⚠️ Auch System-Pakete Installation fehlgeschlagen" + info " → System läuft mit vorhandenen Python-Paketen" + info " → Manuell: pip3 install --break-system-packages Flask requests" + info " → Oder: apt install python3-flask python3-requests" + fi + else + success "✅ $installed_count von ${#essential_deps[@]} essenziellen Paketen installiert" + fi + fi + + # Schnelle Validierung nur der essenziellen Pakete (timeout-gesichert) + progress "Validiere essenzielle Python-Module..." + + local essential_modules=("flask" "requests") + local validation_success=true + + for module in "${essential_modules[@]}"; do + if timeout 10 python3 -c "import $module; print(f'✅ $module verfügbar')" 2>/dev/null; then + debug "$module erfolgreich importiert" + else + warning "⚠️ $module nicht verfügbar" + validation_success=false + fi + done + + if [ "$validation_success" = true ]; then + success "✅ Essenzielle Python-Module verfügbar" + else + warning "⚠️ Einige essenzielle Module fehlen" + info " → System funktioniert möglicherweise trotzdem" + info " → Fehlende Pakete: pip3 install --break-system-packages Flask requests" + fi + + log "✅ Timeout-gesicherte Python-Pakete Installation abgeschlossen" + debug "Python-Installation ohne hängende Prozesse beendet" +} + +# =========================== ROBUSTE NODE.JS INSTALLATION =========================== +install_nodejs_npm() { + log "=== ROBUSTE NODE.JS UND NPM INSTALLATION ===" + + # Alte Installationen entfernen + progress "Bereinige alte Node.js-Installationen..." + apt-get remove --purge -y nodejs npm 2>/dev/null || true + apt-get autoremove -y 2>/dev/null || true + rm -rf /usr/local/bin/node /usr/local/bin/npm 2>/dev/null || true + + # NodeSource Repository mit Fallback + progress "Installiere Node.js mit Fallback-Strategie..." + + local nodejs_installed=false + + # Strategie 1: NodeSource Repository + if curl -fsSL https://deb.nodesource.com/setup_lts.x 2>/dev/null | bash - 2>/dev/null; then + if apt-get update -y && apt_install_retry nodejs; then + nodejs_installed=true + log "✅ Node.js via NodeSource Repository installiert" + fi + fi + + # Strategie 2: Snap (falls verfügbar) + if [ "$nodejs_installed" = false ] && command -v snap >/dev/null 2>&1; then + progress "Versuche Node.js Installation via Snap..." + if snap install node --classic 2>/dev/null; then + nodejs_installed=true + log "✅ Node.js via Snap installiert" + fi + fi + + # Strategie 3: Debian Repository (Fallback) + if [ "$nodejs_installed" = false ]; then + progress "Verwende Debian Repository als Fallback..." + apt_install_retry nodejs npm + nodejs_installed=true + log "✅ Node.js via Debian Repository installiert" + fi + + # Validierung + progress "Validiere Node.js Installation..." + if command -v node >/dev/null 2>&1; then + local node_version=$(node --version) + log "✅ Node.js Version: $node_version" + else + error "❌ Node.js nicht verfügbar nach Installation" + fi + + if command -v npm >/dev/null 2>&1; then + local npm_version=$(npm --version) + log "✅ npm Version: $npm_version" + + # npm optimieren + progress "Optimiere npm-Konfiguration..." + npm config set fund false 2>/dev/null || true + npm config set audit-level moderate 2>/dev/null || true + npm config set progress false 2>/dev/null || true + npm config set loglevel warn 2>/dev/null || true + else + # Versuche npm separat zu installieren + progress "Installiere npm separat..." + apt_install_retry npm + fi + + log "✅ Node.js und npm erfolgreich installiert" +} + +# =========================== NETZWERK-SICHERHEIT =========================== +configure_network_security() { + log "=== KONFIGURIERE ROBUSTE NETZWERK-SICHERHEIT ===" + + # IPv6 vorsichtig deaktivieren + progress "Deaktiviere IPv6 (robust)..." + + # IPv6 in GRUB deaktivieren (nur wenn GRUB vorhanden) + if [ -f /etc/default/grub ] && command -v update-grub >/dev/null 2>&1; then + progress "Deaktiviere IPv6 in GRUB..." + if cp /etc/default/grub /etc/default/grub.backup 2>/dev/null; then + # Prüfe ob ipv6.disable bereits gesetzt ist + if ! grep -q "ipv6.disable=1" /etc/default/grub; then + sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="[^"]*/& ipv6.disable=1/' /etc/default/grub 2>/dev/null || true + sed -i 's/GRUB_CMDLINE_LINUX="[^"]*/& ipv6.disable=1/' /etc/default/grub 2>/dev/null || true + + if timeout 30 update-grub >/dev/null 2>&1; then + success "✅ IPv6 in GRUB deaktiviert" + else + warning "⚠️ GRUB-Update fehlgeschlagen" + debug "GRUB-Update Fehler: $(update-grub 2>&1 || echo 'Befehl fehlgeschlagen')" + fi + else + info "IPv6 bereits in GRUB deaktiviert" + fi + else + warning "⚠️ GRUB-Backup konnte nicht erstellt werden" + fi + else + info "GRUB nicht verfügbar oder kein update-grub - überspringe" + fi + + # IPv6 und Netzwerk-Sicherheit in sysctl konfigurieren (robust) + progress "Erstelle robuste sysctl-Konfiguration..." + + # Backup der bestehenden sysctl.conf + if [ -f /etc/sysctl.conf ]; then + cp /etc/sysctl.conf /etc/sysctl.conf.backup.$(date +%Y%m%d_%H%M%S) 2>/dev/null || true + fi + + # Erstelle separate sysctl-Datei für MYP (sicherer) + local myp_sysctl_file="/etc/sysctl.d/99-myp-security.conf" + + # Nur kritische und kompatible Einstellungen setzen + cat > "$myp_sysctl_file" << 'EOF' +# =================================================================== +# MYP Basis-Sicherheitskonfiguration (kompatibel) +# =================================================================== + +# IPv6 deaktivieren (nur wenn unterstützt) +net.ipv6.conf.all.disable_ipv6 = 1 +net.ipv6.conf.default.disable_ipv6 = 1 + +# Grundlegende Netzwerk-Sicherheit +net.ipv4.ip_forward = 0 +net.ipv4.tcp_syncookies = 1 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 + +# ICMP-Sicherheit +net.ipv4.icmp_echo_ignore_broadcasts = 1 +net.ipv4.icmp_ignore_bogus_error_responses = 1 + +# Source Routing deaktivieren +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.default.accept_source_route = 0 + +EOF + + # Teste ob die Datei geschrieben werden konnte + if [ -f "$myp_sysctl_file" ]; then + success "✅ Basis-sysctl-Konfiguration erstellt" + debug "sysctl-Konfiguration erstellt: $myp_sysctl_file" + else + warning "⚠️ sysctl-Konfigurationsdatei konnte nicht erstellt werden" + return + fi + + # Optional: Erweiterte Einstellungen nur wenn Raspberry Pi + if [ "${RASPBERRY_PI_DETECTED:-0}" = "1" ]; then + progress "Füge Raspberry Pi spezifische Optimierungen hinzu..." + + cat >> "$myp_sysctl_file" << 'EOF' + +# =================================================================== +# RASPBERRY PI PERFORMANCE-OPTIMIERUNGEN (optional) +# =================================================================== + +# Memory Management für schwache Hardware +vm.swappiness = 10 +vm.dirty_ratio = 15 +vm.dirty_background_ratio = 5 +vm.vfs_cache_pressure = 50 + +# Filesystem Performance +vm.dirty_expire_centisecs = 500 +vm.dirty_writeback_centisecs = 100 + +EOF + debug "Raspberry Pi Optimierungen zur sysctl-Konfiguration hinzugefügt" + fi + + # Sysctl-Einstellungen vorsichtig anwenden (non-blocking) + progress "Wende sysctl-Einstellungen an (non-blocking)..." + + # Teste unsere spezielle sysctl-Datei zuerst (mit mehreren Fallbacks) + if [ -f "$myp_sysctl_file" ]; then + local sysctl_success=false + + # Strategie 1: Komplette Datei mit Timeout anwenden + progress "Versuche komplette sysctl-Datei anzuwenden..." + if timeout 10 sysctl -p "$myp_sysctl_file" >/dev/null 2>&1; then + success "✅ MYP sysctl-Einstellungen erfolgreich angewendet" + sysctl_success=true + else + warning "⚠️ Komplette sysctl-Datei fehlgeschlagen" + debug "sysctl -p $myp_sysctl_file Fehlerausgabe: $(timeout 5 sysctl -p "$myp_sysctl_file" 2>&1 || echo 'Timeout oder Fehler')" + fi + + # Strategie 2: Nur IPv6-Deaktivierung (kritisch) + if [ "$sysctl_success" = false ]; then + progress "Versuche nur IPv6-Deaktivierung..." + local ipv6_settings=( + "net.ipv6.conf.all.disable_ipv6=1" + "net.ipv6.conf.default.disable_ipv6=1" + ) + + local ipv6_applied=0 + for setting in "${ipv6_settings[@]}"; do + if timeout 3 sysctl -w "$setting" >/dev/null 2>&1; then + ((ipv6_applied++)) + debug "IPv6-Setting angewendet: $setting" + else + debug "IPv6-Setting fehlgeschlagen: $setting" + fi + done + + if [ $ipv6_applied -gt 0 ]; then + success "✅ IPv6-Deaktivierung teilweise erfolgreich ($ipv6_applied/2)" + sysctl_success=true + fi + fi + + # Strategie 3: Sicherheits-Grundeinstellungen + if [ "$sysctl_success" = false ]; then + progress "Versuche Basis-Sicherheitseinstellungen..." + local security_settings=( + "net.ipv4.ip_forward=0" + "net.ipv4.tcp_syncookies=1" + ) + + local security_applied=0 + for setting in "${security_settings[@]}"; do + if timeout 3 sysctl -w "$setting" >/dev/null 2>&1; then + ((security_applied++)) + debug "Sicherheits-Setting angewendet: $setting" + else + debug "Sicherheits-Setting fehlgeschlagen: $setting" + fi + done + + if [ $security_applied -gt 0 ]; then + success "✅ Basis-Sicherheitseinstellungen angewendet ($security_applied/2)" + sysctl_success=true + fi + fi + + # Strategie 4: Vollständig überspringen (Graceful Degradation) + if [ "$sysctl_success" = false ]; then + warning "⚠️ Alle sysctl-Anwendungen fehlgeschlagen" + warning "⚠️ Einstellungen werden beim nächsten Neustart automatisch aktiv" + info " 📁 Konfiguration verfügbar in: $myp_sysctl_file" + info " 🔧 Manuelle Anwendung: sudo sysctl -p $myp_sysctl_file" + debug "Sysctl komplett übersprungen - System läuft mit Standard-Einstellungen" + fi + else + warning "⚠️ MYP sysctl-Konfigurationsdatei nicht gefunden" + debug "Überspringe sysctl-Anwendung komplett" + fi + + # Niemals das System-weite sysctl -p ausführen (zu problematisch) + debug "Überspringe system-weites sysctl -p (zu riskant)" + + # IPv6 in Netzwerk-Interfaces deaktivieren (robust) + progress "Deaktiviere IPv6 in Netzwerk-Interfaces (vorsichtig)..." + + # Für systemd-networkd (nur wenn aktiv) + if systemctl is-enabled systemd-networkd >/dev/null 2>&1 && systemctl is-active systemd-networkd >/dev/null 2>&1; then + progress "Konfiguriere systemd-networkd für IPv6-Deaktivierung..." + if mkdir -p /etc/systemd/network 2>/dev/null; then + cat > /etc/systemd/network/99-disable-ipv6.network << 'EOF' +[Match] +Name=* + +[Network] +IPv6AcceptRA=no +LinkLocalAddressing=no +EOF + if systemctl restart systemd-networkd >/dev/null 2>&1; then + success "✅ systemd-networkd IPv6 deaktiviert" + else + warning "⚠️ systemd-networkd Neustart fehlgeschlagen" + fi + else + warning "⚠️ systemd-networkd Verzeichnis konnte nicht erstellt werden" + fi + else + debug "systemd-networkd nicht aktiv - überspringe" + fi + + # Für NetworkManager (nur wenn aktiv) + if systemctl is-enabled NetworkManager >/dev/null 2>&1 && systemctl is-active NetworkManager >/dev/null 2>&1; then + progress "Konfiguriere NetworkManager für IPv6-Deaktivierung..." + if mkdir -p /etc/NetworkManager/conf.d 2>/dev/null; then + cat > /etc/NetworkManager/conf.d/99-disable-ipv6.conf << 'EOF' +[main] +plugins=keyfile + +[keyfile] +unmanaged-devices=none + +[connection] +ipv6.method=ignore +EOF + if systemctl restart NetworkManager >/dev/null 2>&1; then + success "✅ NetworkManager IPv6 deaktiviert" + else + warning "⚠️ NetworkManager Neustart fehlgeschlagen" + fi + else + warning "⚠️ NetworkManager Verzeichnis konnte nicht erstellt werden" + fi + else + debug "NetworkManager nicht aktiv - überspringe" + fi + + # IPv6 in /etc/hosts auskommentieren (vorsichtig) + if [ -f /etc/hosts ]; then + if sed -i.backup 's/^::1/#::1/' /etc/hosts 2>/dev/null; then + debug "IPv6 Einträge in /etc/hosts auskommentiert" + else + debug "IPv6 Einträge in /etc/hosts konnten nicht geändert werden" + fi + fi + + # Abschließende Zusammenfassung + log "✅ Robuste Netzwerk-Sicherheit konfiguriert:" + log " 🚫 IPv6 Deaktivierung konfiguriert" + log " 🛡️ Netzwerk-Sicherheitsregeln gesetzt" + log " 🔒 Basis-Firewall-Schutz aktiviert" + log " 📝 Sysctl-Konfiguration erstellt: $myp_sysctl_file" + log " 🔧 Netzwerk-Services entsprechend konfiguriert" + log " ⚙️ Einstellungen werden beim nächsten Boot aktiv" + + # Alternative für sysctl-Probleme dokumentieren + debug "Alternative sysctl-Anwendung:" + debug " - Manuell: sysctl -p $myp_sysctl_file" + debug " - Automatisch: Beim nächsten Neustart aktiv" + debug " - Verifikation: sysctl net.ipv6.conf.all.disable_ipv6" +} + +# =========================== DESKTOP-ENVIRONMENT ENTFERNUNG =========================== +remove_desktop_environments() { + log "=== ENTFERNE DESKTOP ENVIRONMENTS FÜR KIOSK-MODUS ===" + + progress "Stoppe alle Desktop-Services..." + local desktop_services=("lightdm" "gdm3" "sddm" "xdm" "nodm") + + for service in "${desktop_services[@]}"; do + systemctl stop "$service" 2>/dev/null || true + systemctl disable "$service" 2>/dev/null || true + done + + progress "Entferne Desktop-Pakete vollständig..." + + # Raspberry Pi OS Desktop-Pakete + apt-get remove --purge -y \ + raspberrypi-ui-mods \ + pi-package \ + desktop-base \ + lxde* \ + xfce4* \ + gnome* \ + kde* \ + mate* \ + cinnamon* \ + openbox \ + pcmanfm \ + file-manager* \ + task-lxde-desktop \ + task-xfce-desktop \ + task-gnome-desktop \ + task-kde-desktop \ + 2>/dev/null || true + + # Display Manager entfernen + apt-get remove --purge -y \ + lightdm* \ + gdm3* \ + sddm* \ + xdm* \ + nodm* \ + 2>/dev/null || true + + # Unnötige Anwendungen entfernen + apt-get remove --purge -y \ + libreoffice* \ + thunderbird* \ + firefox* \ + vlc* \ + gimp* \ + scratch* \ + minecraft-pi \ + sonic-pi \ + 2>/dev/null || true + + # Aufräumen + apt-get autoremove --purge -y + apt-get autoclean + + log "✅ Desktop Environments vollständig entfernt" +} + +# =========================== MINIMALE X11-UMGEBUNG =========================== +install_minimal_x11() { + log "=== INSTALLIERE MINIMALE X11-UMGEBUNG FÜR KIOSK ===" + + progress "Installiere minimale X11-Pakete..." + apt-get install -y \ + xserver-xorg-core \ + xserver-xorg-input-all \ + xserver-xorg-video-fbdev \ + xserver-xorg-video-vesa \ + xinit \ + x11-xserver-utils \ + xdotool \ + unclutter \ + openbox \ + || error "X11 Installation fehlgeschlagen" + + # Browser-Installation mit Fallback-Mechanismus + progress "Installiere Browser für Kiosk-Modus..." + local browser_installed=false + + # Versuche Chromium zu installieren + if apt-get install -y chromium 2>/dev/null; then + log "✅ Chromium erfolgreich installiert" + browser_installed=true + elif apt-get install -y chromium-browser 2>/dev/null; then + log "✅ Chromium-Browser erfolgreich installiert" + browser_installed=true + elif apt-get install -y firefox-esr 2>/dev/null; then + warning "⚠️ Chromium nicht verfügbar - Firefox ESR als Fallback installiert" + browser_installed=true + fi + + if [ "$browser_installed" = false ]; then + error "❌ Kein Browser verfügbar (chromium, chromium-browser, firefox-esr)" + fi + + log "✅ Minimale X11-Umgebung installiert" +} + +# =========================== KIOSK-BENUTZER MANAGEMENT =========================== +create_kiosk_user() { + log "=== KIOSK-BENUTZER SETUP ===" + + if ! id "$KIOSK_USER" &>/dev/null; then + progress "Erstelle Kiosk-Benutzer: $KIOSK_USER" + useradd -m -s /bin/bash "$KIOSK_USER" || error "Kann Kiosk-Benutzer nicht erstellen" + + # Gruppen hinzufügen + usermod -aG audio,video,input,dialout,plugdev,users "$KIOSK_USER" 2>/dev/null || true + + # pip-Konfiguration für Kiosk-Benutzer + local kiosk_home="/home/$KIOSK_USER" + mkdir -p "$kiosk_home/.pip" 2>/dev/null || true + cat > "$kiosk_home/.pip/pip.conf" << 'EOF' +[global] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +timeout = 60 +retries = 3 + +[install] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +EOF + chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.pip/pip.conf" 2>/dev/null || true + else + info "Kiosk-Benutzer $KIOSK_USER existiert bereits" + fi + + # Passwort entfernen für automatischen Login + passwd -d "$KIOSK_USER" || warning "Konnte Passwort nicht entfernen" + + log "✅ Kiosk-Benutzer konfiguriert: $KIOSK_USER" +} + +configure_autologin() { + log "=== KONFIGURIERE AUTOLOGIN FÜR KIOSK-BENUTZER ===" + + # Getty-Service für automatischen Login konfigurieren + progress "Konfiguriere automatischen Login auf tty1..." + + local getty_override_dir="/etc/systemd/system/getty@tty1.service.d" + mkdir -p "$getty_override_dir" + + cat > "$getty_override_dir/override.conf" << EOF +[Service] +ExecStart= +ExecStart=-/sbin/agetty --autologin $KIOSK_USER --noclear %I \$TERM +EOF + + # Systemd-Konfiguration neu laden + systemctl daemon-reload + systemctl enable getty@tty1.service + + log "✅ Autologin für $KIOSK_USER konfiguriert" +} + +configure_kiosk_autostart() { + log "=== KONFIGURIERE AUTOMATISCHEN KIOSK-START ===" + + # Erstelle .bashrc für automatischen X-Server und Browser-Start + progress "Konfiguriere automatischen Kiosk-Start für $KIOSK_USER..." + + local kiosk_home="/home/$KIOSK_USER" + + # .bashrc für automatischen Start erstellen + cat > "$kiosk_home/.bashrc" << 'EOF' +# Automatischer Kiosk-Start beim Login +if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" = "1" ]; then + echo "Starte Kiosk-Modus..." + + # X-Server im Hintergrund starten + startx /home/kiosk/.xinitrc -- :0 vt1 & + + # Warte bis X-Server bereit ist + sleep 5 + + # Setze DISPLAY-Variable + export DISPLAY=:0 + + # Warte auf HTTPS-Backend + echo "Warte auf HTTPS-Backend..." + for i in {1..60}; do + if curl -k -s https://localhost:443 >/dev/null 2>&1; then + echo "HTTPS-Backend erreichbar" + break + fi + echo "Warte... ($i/60)" + sleep 2 + done + + # Bildschirmschoner deaktivieren + xset s off + xset s noblank + xset -dpms + + # Mauszeiger verstecken + unclutter -idle 0.1 -root -noevents & + + # Browser im Kiosk-Modus starten + if command -v chromium >/dev/null 2>&1; then + BROWSER="chromium" + elif command -v chromium-browser >/dev/null 2>&1; then + BROWSER="chromium-browser" + else + BROWSER="firefox-esr" + fi + + echo "Starte $BROWSER im Kiosk-Modus..." + + if [[ "$BROWSER" == "chromium"* ]]; then + exec $BROWSER \ + --kiosk \ + --no-sandbox \ + --disable-infobars \ + --disable-session-crashed-bubble \ + --disable-restore-session-state \ + --disable-features=TranslateUI \ + --disable-extensions \ + --disable-plugins \ + --disable-popup-blocking \ + --disable-prompt-on-repost \ + --disable-sync \ + --disable-translate \ + --noerrdialogs \ + --no-first-run \ + --no-default-browser-check \ + --autoplay-policy=no-user-gesture-required \ + --start-fullscreen \ + --start-maximized \ + --user-data-dir=/home/kiosk/.chromium-kiosk \ + --disable-background-mode \ + --force-device-scale-factor=1.0 \ + --disable-pinch \ + --overscroll-history-navigation=0 \ + --disable-dev-shm-usage \ + --memory-pressure-off \ + --max_old_space_size=512 \ + --disable-background-timer-throttling \ + --disable-backgrounding-occluded-windows \ + --disable-renderer-backgrounding \ + --disable-features=VizDisplayCompositor \ + --enable-features=OverlayScrollbar \ + --hide-scrollbars \ + --ignore-certificate-errors \ + --ignore-ssl-errors \ + --ignore-certificate-errors-spki-list \ + --disable-web-security \ + --allow-running-insecure-content \ + --unsafely-treat-insecure-origin-as-secure=https://localhost:443 \ + https://localhost:443 + else + exec firefox-esr \ + --kiosk \ + https://localhost:443 + fi +fi +EOF + + # .xinitrc für X-Server-Konfiguration erstellen + cat > "$kiosk_home/.xinitrc" << 'EOF' +#!/bin/bash +# Minimale X-Session für Kiosk-Modus +exec openbox-session +EOF + + # Berechtigungen setzen + chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.bashrc" + chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.xinitrc" + chmod +x "$kiosk_home/.xinitrc" + + # Erstelle Kiosk-Verzeichnisse + mkdir -p "$kiosk_home/.chromium-kiosk" + chown -R "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.chromium-kiosk" + + log "✅ Automatischer Kiosk-Start konfiguriert" + info "Der Kiosk-Modus startet automatisch beim Login des $KIOSK_USER" +} + +# =========================== ROBUSTE SSL-ZERTIFIKATE INSTALLATION =========================== +install_ssl_certificates() { + log "=== TIMEOUT-GESICHERTE SSL-ZERTIFIKATE KONFIGURATION ===" + + progress "Installiere SSL-Grundkomponenten..." + apt_install_retry ca-certificates openssl + + progress "Aktualisiere CA-Zertifikate (timeout-gesichert)..." + if timeout 30 update-ca-certificates >/dev/null 2>&1; then + success "✅ CA-Zertifikate erfolgreich aktualisiert" + else + warning "⚠️ CA-Zertifikate Update fehlgeschlagen oder Timeout" + debug "Erste CA-Update Timeout - System läuft mit bestehenden Zertifikaten" + fi + + # SSL-Verzeichnisse sicherstellen + if mkdir -p /usr/local/share/ca-certificates/myp 2>/dev/null; then + debug "SSL-Verzeichnis erstellt: /usr/local/share/ca-certificates/myp" + else + warning "⚠️ SSL-Verzeichnis konnte nicht erstellt werden" + fi + + # Mercedes Corporate Zertifikate (timeout-gesichert) + if [ -d "$CURRENT_DIR/certs/mercedes" ] && [ "$(ls -A $CURRENT_DIR/certs/mercedes 2>/dev/null)" ]; then + progress "Installiere Mercedes Corporate Zertifikate (timeout-gesichert)..." + + local cert_count=0 + local installed_count=0 + local max_certs=10 # Begrenze Anzahl verarbeiteter Zertifikate + + # Timeout für die gesamte Zertifikat-Verarbeitung + timeout 60 bash -c ' + cert_count=0 + installed_count=0 + max_certs=10 + + find "$1/certs/mercedes" -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cer" \) | head -$max_certs | while read cert_file; do + cert_count=$((cert_count + 1)) + cert_basename=$(basename "$cert_file") + cert_name="${cert_basename%.*}" + target_file="/usr/local/share/ca-certificates/myp/${cert_name}.crt" + + echo "Verarbeite Mercedes-Zertifikat ($cert_count/$max_certs): $cert_basename" + + # Timeout für einzelne Zertifikat-Operationen + if timeout 10 openssl x509 -in "$cert_file" -text -noout >/dev/null 2>&1; then + # PEM Format + if cp "$cert_file" "$target_file" 2>/dev/null; then + echo "✅ PEM-Zertifikat installiert: ${cert_name}.crt" + installed_count=$((installed_count + 1)) + fi + elif timeout 10 openssl x509 -in "$cert_file" -inform DER -text -noout >/dev/null 2>&1; then + # DER Format - zu PEM konvertieren + if timeout 10 openssl x509 -in "$cert_file" -inform DER -out "$target_file" -outform PEM 2>/dev/null; then + echo "✅ DER-Zertifikat konvertiert und installiert: ${cert_name}.crt" + installed_count=$((installed_count + 1)) + fi + else + echo "⚠️ Ungültiges Zertifikat übersprungen: $cert_file" + fi + + # Kurze Pause zwischen Zertifikaten + sleep 0.5 + done + + echo "Mercedes-Zertifikate verarbeitet: $installed_count von $cert_count" + ' -- "$CURRENT_DIR" 2>/dev/null || { + warning "⚠️ Mercedes-Zertifikate Verarbeitung abgebrochen (Timeout nach 60s)" + debug "Mercedes-Zertifikate Timeout - möglicherweise zu viele oder defekte Dateien" + } + + # Versuche CA-Update nur wenn Zertifikate installiert wurden + if [ "$(ls -A /usr/local/share/ca-certificates/myp/ 2>/dev/null)" ]; then + progress "Lade CA-Zertifikate nach Mercedes-Import neu (timeout-gesichert)..." + if timeout 30 update-ca-certificates >/dev/null 2>&1; then + success "✅ Mercedes-Zertifikate erfolgreich in CA-Store integriert" + else + warning "⚠️ CA-Zertifikate Update fehlgeschlagen oder Timeout" + debug "update-ca-certificates Timeout - CA-Store möglicherweise inkonsistent" + fi + else + info "Keine Mercedes-Zertifikate gefunden oder installiert" + fi + else + debug "Mercedes-Zertifikate-Verzeichnis nicht gefunden oder leer" + fi + + # SSL-Umgebungsvariablen systemweit setzen + progress "Konfiguriere SSL-Umgebungsvariablen..." + cat >> /etc/environment << 'EOF' + +# SSL Certificate Configuration für MYP +SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt +REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt +CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt +EOF + + # SSL-Umgebungsvariablen für aktuelle Session + export SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt" + export REQUESTS_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" + export CURL_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" + + # Validiere SSL-Setup + progress "Validiere SSL-Konfiguration..." + if [ -f "/etc/ssl/certs/ca-certificates.crt" ]; then + local cert_count=$(grep -c "BEGIN CERTIFICATE" /etc/ssl/certs/ca-certificates.crt 2>/dev/null || echo "0") + log "✅ SSL-Zertifikate verfügbar: $cert_count CA-Zertifikate" + else + warning "⚠️ CA-Zertifikate-Datei nicht gefunden" + fi + + # Finale SSL-Konfiguration (timeout-gesichert) + progress "Finalisiere SSL-Konfiguration..." + + # Finaler CA-Update (nur wenn wirklich nötig) + if [ "$(ls -A /usr/local/share/ca-certificates/myp/ 2>/dev/null)" ] && [ ! -f "/tmp/myp-ca-updated" ]; then + if timeout 20 update-ca-certificates >/dev/null 2>&1; then + touch "/tmp/myp-ca-updated" + success "✅ Finale CA-Zertifikate Integration abgeschlossen" + else + warning "⚠️ Finale CA-Integration fehlgeschlagen - Zertifikate beim nächsten Boot aktiv" + fi + fi + + log "✅ SSL-Zertifikate timeout-gesichert konfiguriert" + debug "SSL-Konfiguration abgeschlossen ohne hängende Prozesse" +} + +# =========================== ROBUSTES ANWENDUNGS-DEPLOYMENT =========================== +deploy_application() { + log "=== ROBUSTES ANWENDUNGS-DEPLOYMENT ===" + + progress "Erstelle sicheres Zielverzeichnis: $APP_DIR" + mkdir -p "$APP_DIR" || error "Konnte Zielverzeichnis nicht erstellen" + + # Validiere Source-Verzeichnis + progress "Validiere Source-Dateien..." + if [ ! -f "$CURRENT_DIR/app.py" ]; then + error "Kritische Datei nicht gefunden: $CURRENT_DIR/app.py" + fi + + if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then + error "Kritische Datei nicht gefunden: $CURRENT_DIR/requirements.txt" + fi + + progress "Kopiere Anwendungsdateien (robust)..." + + # Kritische Dateien zuerst (mit Validierung) + local critical_files=( "app.py" "models.py" "requirements.txt" ) - for file in "${required_files[@]}"; do - if [ ! -f "${CURRENT_DIR}/${file}" ]; then - error "Erforderliche Datei nicht gefunden: ${file}" + for file in "${critical_files[@]}"; do + if [ -f "$CURRENT_DIR/$file" ]; then + progress "Kopiere kritische Datei: $file" + if cp "$CURRENT_DIR/$file" "$APP_DIR/" 2>/dev/null; then + success "✅ $file erfolgreich kopiert" + else + error "❌ Fehler beim Kopieren der kritischen Datei: $file" + fi + else + error "❌ Kritische Datei fehlt: $file" fi - success "✅ ${file} gefunden" done -} - -# =========================== SYSTEM-UPDATE =========================== -update_system() { - log "=== SYSTEM UPDATE ===" - progress "Aktualisiere Paketlisten..." - apt-get update || error "Fehler beim Update der Paketlisten" - - progress "Aktualisiere System-Pakete..." - DEBIAN_FRONTEND=noninteractive apt-get upgrade -y || warning "Einige Pakete konnten nicht aktualisiert werden" - - success "✅ System aktualisiert" -} - -# =========================== ABHÄNGIGKEITEN INSTALLATION =========================== -install_base_dependencies() { - log "=== INSTALLIERE BASIS-ABHÄNGIGKEITEN ===" - - local packages=( - # Python und Entwicklung - "python3" - "python3-pip" - "python3-venv" - "python3-dev" - "build-essential" - - # System-Tools - "git" - "curl" - "wget" - "nano" - "htop" - "net-tools" - - # Datenbank - "sqlite3" - - # SSL/Sicherheit - "openssl" - "ca-certificates" - - # Kiosk-Modus - "chromium-browser" - "xserver-xorg" - "xinit" - "x11-xserver-utils" - "unclutter" - - # Remote-Zugang - "openssh-server" - "xrdp" - - # Firewall - "firewalld" + # Verzeichnisse mit robuster Behandlung + local directories=( + "blueprints" + "config" + "database" + "static" + "templates" + "uploads" + "utils" + "logs" + "certs" ) - progress "Installiere Pakete..." - for package in "${packages[@]}"; do - progress "Installiere ${package}..." - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$package" || warning "Fehler bei Installation von ${package}" + for dir in "${directories[@]}"; do + if [ -d "$CURRENT_DIR/$dir" ]; then + progress "Kopiere Verzeichnis: $dir" + if cp -r "$CURRENT_DIR/$dir" "$APP_DIR/" 2>/dev/null; then + success "✅ $dir erfolgreich kopiert" + else + warning "⚠️ Fehler beim Kopieren von $dir (möglicherweise nicht kritisch)" + fi + else + info "Verzeichnis nicht vorhanden: $dir" + fi done - success "✅ Basis-Abhängigkeiten installiert" -} - -# =========================== PYTHON-UMGEBUNG =========================== -setup_python_environment() { - log "=== PYTHON-UMGEBUNG EINRICHTEN ===" + # Optionale Dateien + local optional_files=( + "package.json" + "package-lock.json" + "tailwind.config.js" + "postcss.config.js" + "README.md" + ".gitignore" + ) - # App-Verzeichnis erstellen - progress "Erstelle App-Verzeichnis..." - mkdir -p "$APP_DIR" + for file in "${optional_files[@]}"; do + if [ -f "$CURRENT_DIR/$file" ]; then + progress "Kopiere optionale Datei: $file" + cp "$CURRENT_DIR/$file" "$APP_DIR/" 2>/dev/null || warning "⚠️ Kopieren von $file fehlgeschlagen" + fi + done - # Dateien kopieren - progress "Kopiere Anwendungsdateien..." - cp -r "${CURRENT_DIR}"/* "$APP_DIR/" 2>/dev/null || true + # Erstelle alle notwendigen Verzeichnisse mit korrekter Struktur + progress "Erstelle Verzeichnisstruktur..." + local required_dirs=( + "database/backups" + "logs/app" + "logs/auth" + "logs/errors" + "logs/system" + "uploads/temp" + "uploads/assets" + "uploads/avatars" + "uploads/backups" + "uploads/jobs" + "certs/localhost" + "instance" + "instance/ssl" + ) - # Python-Abhängigkeiten installieren - progress "Installiere Python-Abhängigkeiten..." - cd "$APP_DIR" + for dir in "${required_dirs[@]}"; do + mkdir -p "$APP_DIR/$dir" 2>/dev/null || warning "⚠️ Verzeichnis $dir konnte nicht erstellt werden" + done - if [ -f "requirements.txt" ]; then - pip3 install --no-cache-dir -r requirements.txt || error "Fehler beim Installieren der Python-Abhängigkeiten" - else - # Minimale Abhängigkeiten direkt installieren - pip3 install --no-cache-dir \ - Flask==2.3.3 \ - Flask-Login==0.6.2 \ - Flask-WTF==1.1.1 \ - SQLAlchemy==2.0.21 \ - Werkzeug==2.3.7 \ - python-dotenv==1.0.0 \ - PyP100==0.1.2 \ - || error "Fehler beim Installieren der Python-Pakete" + # Sichere Berechtigungen setzen (robuster) + progress "Setze sichere Berechtigungen..." + + # Basis-Berechtigungen + chown -R root:root "$APP_DIR" 2>/dev/null || warning "⚠️ Ownership konnte nicht gesetzt werden" + chmod 755 "$APP_DIR" 2>/dev/null || warning "⚠️ Verzeichnis-Permissions konnten nicht gesetzt werden" + + # Ausführbare Dateien + if [ -f "$APP_DIR/app.py" ]; then + chmod +x "$APP_DIR/app.py" 2>/dev/null || warning "⚠️ app.py Ausführberechtigung konnte nicht gesetzt werden" fi - cd "$CURRENT_DIR" - success "✅ Python-Umgebung eingerichtet" -} - -# =========================== NODE.JS INSTALLATION =========================== -install_nodejs() { - log "=== INSTALLIERE NODE.JS ===" + # Sensitive Verzeichnisse + chmod 750 "$APP_DIR/database" 2>/dev/null || true + chmod 750 "$APP_DIR/logs" 2>/dev/null || true + chmod 750 "$APP_DIR/certs" 2>/dev/null || true + chmod 750 "$APP_DIR/instance" 2>/dev/null || true - if command -v node >/dev/null 2>&1; then - log "Node.js bereits installiert: $(node --version)" - return - fi + # Upload-Verzeichnisse + chmod 755 "$APP_DIR/uploads" 2>/dev/null || true + chmod 755 "$APP_DIR/static" 2>/dev/null || true - progress "Installiere Node.js..." - curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - || error "Fehler beim Hinzufügen des Node.js Repository" - apt-get install -y nodejs || error "Fehler bei der Node.js Installation" + # Python-Umgebung robust konfigurieren + progress "Konfiguriere robuste Python-Umgebung..." - success "✅ Node.js installiert: $(node --version)" -} - -# =========================== FRONTEND BUILD =========================== -build_frontend() { - log "=== BAUE FRONTEND ===" - - cd "$APP_DIR" - - # Prüfe ob package.json existiert - if [ -f "package.json" ]; then - progress "Installiere Frontend-Abhängigkeiten..." - npm install || warning "Fehler bei npm install" + # Ermittle Python site-packages Verzeichnis (robust) + local python_site_packages="" + for possible_path in \ + "$(python3 -c "import site; print(site.getsitepackages()[0])" 2>/dev/null)" \ + "/usr/local/lib/python3/dist-packages" \ + "/usr/lib/python3/dist-packages" \ + "/usr/local/lib/python$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null)/dist-packages"; do - progress "Baue Frontend-Assets..." - npm run build || warning "Fehler beim Frontend-Build" + if [ -d "$possible_path" ]; then + python_site_packages="$possible_path" + break + fi + done + + if [ -n "$python_site_packages" ]; then + echo "$APP_DIR" > "$python_site_packages/myp-app.pth" 2>/dev/null || warning "⚠️ Python-Pfad konnte nicht konfiguriert werden" + log "✅ Python-Pfad konfiguriert: $python_site_packages/myp-app.pth" else - warning "package.json nicht gefunden - überspringe Frontend-Build" + warning "⚠️ Python site-packages Verzeichnis nicht gefunden" fi - cd "$CURRENT_DIR" - success "✅ Frontend-Build abgeschlossen" + # Systemweite Umgebungsvariablen robust setzen + progress "Konfiguriere Umgebungsvariablen..." + cat >> /etc/environment << EOF + +# MYP Application Environment +MYP_APP_DIR=$APP_DIR +PYTHONPATH=$APP_DIR:\${PYTHONPATH:-} +FLASK_APP=$APP_DIR/app.py +FLASK_ENV=production +EOF + + # Bash-Profile für alle User aktualisieren (robust) + progress "Aktualisiere Bash-Profile..." + local profile_updated=0 + + for user_home in "/root" "/home/"*; do + if [ -d "$user_home" ] && [ "$user_home" != "/home/lost+found" ] && [ -w "$user_home" ]; then + if ! grep -q "MYP Application Environment" "$user_home/.bashrc" 2>/dev/null; then + cat >> "$user_home/.bashrc" << 'EOF' + +# MYP Application Environment +if [ -d "/opt/myp" ]; then + export MYP_APP_DIR="/opt/myp" + export FLASK_APP="/opt/myp/app.py" + export FLASK_ENV="production" + if [ -z "${PYTHONPATH:-}" ]; then + export PYTHONPATH="/opt/myp" + else + export PYTHONPATH="/opt/myp:$PYTHONPATH" + fi +fi +EOF + ((profile_updated++)) + log "✅ Bash-Profile aktualisiert: $user_home/.bashrc" + fi + fi + done + + # Validiere Deployment + progress "Validiere Application Deployment..." + local validation_errors=0 + + # Prüfe kritische Dateien + for file in "app.py" "models.py" "requirements.txt"; do + if [ ! -f "$APP_DIR/$file" ]; then + warning "❌ Kritische Datei fehlt: $file" + ((validation_errors++)) + fi + done + + # Prüfe wichtige Verzeichnisse + for dir in "static" "templates" "blueprints"; do + if [ ! -d "$APP_DIR/$dir" ]; then + warning "❌ Wichtiges Verzeichnis fehlt: $dir" + ((validation_errors++)) + fi + done + + if [ $validation_errors -eq 0 ]; then + success "✅ Application Deployment vollständig validiert" + else + warning "⚠️ $validation_errors Probleme beim Deployment gefunden" + fi + + log "✅ Robustes Anwendungs-Deployment abgeschlossen" + log " 📁 App-Verzeichnis: $APP_DIR" + log " 🐍 Python-Pfad konfiguriert" + log " 🔧 $profile_updated Bash-Profile aktualisiert" + log " 🛡️ Sichere Berechtigungen gesetzt" } -# =========================== SSL-ZERTIFIKATE =========================== -setup_ssl_certificates() { - log "=== SSL-ZERTIFIKATE EINRICHTEN ===" +install_npm_dependencies() { + log "=== NPM-ABHÄNGIGKEITEN INSTALLATION ===" - local cert_dir="${APP_DIR}/instance/ssl" + if [ -f "$APP_DIR/package.json" ]; then + progress "Installiere npm-Abhängigkeiten..." + + cd "$APP_DIR" + + # npm install mit verschiedenen Fallback-Strategien + if npm install --no-optional --no-audit --no-fund 2>/dev/null; then + log "✅ npm install erfolgreich (Standard)" + elif npm install --legacy-peer-deps --no-optional 2>/dev/null; then + log "✅ npm install erfolgreich (Legacy-Modus)" + elif npm install --force 2>/dev/null; then + log "✅ npm install erfolgreich (Force-Modus)" + else + warning "⚠️ npm install fehlgeschlagen - überspringe" + fi + + cd "$CURRENT_DIR" + else + info "Keine package.json gefunden - überspringe npm-Installation" + fi + + log "✅ NPM-Abhängigkeiten verarbeitet" +} + +# =========================== SSL-ZERTIFIKAT GENERIERUNG =========================== +generate_ssl_certificate() { + log "=== SSL-ZERTIFIKAT GENERIERUNG ===" + + progress "Generiere selbstsigniertes SSL-Zertifikat für localhost..." + + local cert_dir="$APP_DIR/certs/localhost" mkdir -p "$cert_dir" - progress "Generiere selbstsigniertes SSL-Zertifikat..." - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout "${cert_dir}/key.pem" \ - -out "${cert_dir}/cert.pem" \ - -subj "/C=DE/ST=NRW/L=Local/O=MYP/CN=localhost" \ - 2>/dev/null || error "Fehler beim Generieren des SSL-Zertifikats" + # Generiere privaten Schlüssel + openssl genrsa -out "$cert_dir/localhost.key" 2048 || error "Fehler beim Generieren des privaten Schlüssels" - chmod 600 "${cert_dir}/key.pem" - chmod 644 "${cert_dir}/cert.pem" + # Generiere Zertifikat + openssl req -new -x509 -key "$cert_dir/localhost.key" -out "$cert_dir/localhost.crt" -days 365 \ + -subj "/C=DE/ST=Baden-Wuerttemberg/L=Stuttgart/O=Mercedes-Benz/OU=IT/CN=localhost" \ + || error "Fehler beim Generieren des SSL-Zertifikats" - success "✅ SSL-Zertifikate erstellt" + # Berechtigungen setzen + chmod 600 "$cert_dir/localhost.key" + chmod 644 "$cert_dir/localhost.crt" + + log "✅ SSL-Zertifikat erfolgreich generiert" } -# =========================== SYSTEMD SERVICES =========================== -setup_systemd_services() { - log "=== SYSTEMD SERVICES EINRICHTEN ===" +# =========================== ROBUSTE SYSTEMD-SERVICES INSTALLATION =========================== +install_systemd_services() { + log "=== ROBUSTE SYSTEMD-SERVICES INSTALLATION ===" - # HTTPS Service - progress "Erstelle HTTPS Service..." - cat > "/etc/systemd/system/${HTTPS_SERVICE_NAME}.service" << EOF -[Unit] -Description=MYP HTTPS Server -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=${APP_DIR} -Environment="PYTHONUNBUFFERED=1" -Environment="FLASK_ENV=production" -ExecStart=/usr/bin/python3 ${APP_DIR}/app.py -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target -EOF - - # Kiosk Service - progress "Erstelle Kiosk Service..." - cat > "/etc/systemd/system/${KIOSK_SERVICE_NAME}.service" << EOF -[Unit] -Description=MYP Kiosk Mode -After=network.target ${HTTPS_SERVICE_NAME}.service - -[Service] -Type=simple -User=${KIOSK_USER} -Environment="DISPLAY=:0" -ExecStart=/usr/bin/xinit /usr/bin/chromium-browser --kiosk --no-sandbox --disable-dev-shm-usage --disable-gpu --disable-software-rasterizer --disable-features=TranslateUI --disable-infobars https://localhost:${HTTPS_PORT} -- :0 -nocursor -dpms -Restart=always -RestartSec=10 - -[Install] -WantedBy=graphical.target -EOF - - systemctl daemon-reload - success "✅ Systemd Services erstellt" -} - -# =========================== KIOSK-BENUTZER =========================== -setup_kiosk_user() { - log "=== KIOSK-BENUTZER EINRICHTEN ===" - - if ! id "$KIOSK_USER" &>/dev/null; then - progress "Erstelle Kiosk-Benutzer..." - useradd -m -s /bin/bash "$KIOSK_USER" - usermod -aG video,audio "$KIOSK_USER" + # Validiere systemd-Verzeichnis + if [ ! -d "$SYSTEMD_DIR" ]; then + error "systemd-Verzeichnis nicht gefunden: $SYSTEMD_DIR" fi - # Auto-Login konfigurieren - mkdir -p /etc/systemd/system/getty@tty1.service.d/ - cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF -[Service] -ExecStart= -ExecStart=-/sbin/agetty --autologin ${KIOSK_USER} --noclear %I \$TERM -EOF - - success "✅ Kiosk-Benutzer konfiguriert" + progress "Validiere und kopiere Service-Dateien..." + + # Definiere Service-Dateien mit Priorität + local essential_services=( + "$HTTPS_SERVICE_NAME.service" + ) + + local optional_services=( + "$KIOSK_SERVICE_NAME.service" + "$WATCHDOG_SERVICE_NAME.service" + "$WATCHDOG_PYTHON_SERVICE_NAME.service" + "$FIREWALL_SERVICE_NAME.service" + ) + + local installed_services=0 + local essential_errors=0 + + # Essenzielle Services zuerst + for service_file in "${essential_services[@]}"; do + if [ -f "$SYSTEMD_DIR/$service_file" ]; then + progress "Kopiere essenziellen Service: $service_file" + if cp "$SYSTEMD_DIR/$service_file" "$SYSTEM_SYSTEMD_DIR/" 2>/dev/null; then + success "✅ $service_file erfolgreich installiert" + ((installed_services++)) + else + error "❌ Fehler beim Kopieren des essenziellen Service: $service_file" + ((essential_errors++)) + fi + else + error "❌ Essenzieller Service nicht gefunden: $service_file" + ((essential_errors++)) + fi + done + + # Optionale Services + for service_file in "${optional_services[@]}"; do + if [ -f "$SYSTEMD_DIR/$service_file" ]; then + progress "Kopiere optionalen Service: $service_file" + if cp "$SYSTEMD_DIR/$service_file" "$SYSTEM_SYSTEMD_DIR/" 2>/dev/null; then + success "✅ $service_file erfolgreich installiert" + ((installed_services++)) + else + warning "⚠️ Fehler beim Kopieren des optionalen Service: $service_file" + fi + else + info "Optionaler Service nicht gefunden: $service_file" + fi + done + + # Prüfe auf kritische Fehler + if [ $essential_errors -gt 0 ]; then + error "❌ $essential_errors essenzielle Services konnten nicht installiert werden!" + fi + + # Validiere kopierte Service-Dateien + progress "Validiere Service-Dateien..." + for service_file in "${essential_services[@]}" "${optional_services[@]}"; do + if [ -f "$SYSTEM_SYSTEMD_DIR/$service_file" ]; then + # Syntaxprüfung für systemd-Services + if systemd-analyze verify "$SYSTEM_SYSTEMD_DIR/$service_file" 2>/dev/null; then + success "✅ $service_file Syntax-Validierung erfolgreich" + else + warning "⚠️ $service_file hat möglicherweise Syntax-Probleme" + fi + fi + done + + # Systemd-Konfiguration neu laden + progress "Lade systemd-Konfiguration neu..." + retry_command "systemctl daemon-reload" "systemd daemon-reload" + + log "✅ Systemd-Services installiert: $installed_services Services" } -# =========================== FIREWALL =========================== -configure_firewall() { - log "=== FIREWALL KONFIGURIEREN ===" +enable_and_start_services() { + log "=== ROBUSTE SERVICES AKTIVIERUNG UND START ===" - systemctl start firewalld - systemctl enable firewalld + # Service-Status tracking + local successful_services=0 + local failed_services=0 - # HTTPS Port öffnen - firewall-cmd --permanent --add-port=${HTTPS_PORT}/tcp + # HTTPS-Service (kritisch) + progress "Aktiviere und starte HTTPS-Service (kritisch)..." - # SSH erlauben - firewall-cmd --permanent --add-service=ssh + if systemctl enable "$HTTPS_SERVICE_NAME" 2>/dev/null; then + success "✅ HTTPS-Service erfolgreich aktiviert" + + if systemctl start "$HTTPS_SERVICE_NAME" 2>/dev/null; then + success "✅ HTTPS-Service erfolgreich gestartet" + + # Warte und prüfe Status gründlich + local startup_timeout=15 + local check_interval=2 + local elapsed=0 + + while [ $elapsed -lt $startup_timeout ]; do + if systemctl is-active --quiet "$HTTPS_SERVICE_NAME"; then + success "✅ HTTPS-Service läuft stabil nach ${elapsed}s" + ((successful_services++)) + break + fi + sleep $check_interval + elapsed=$((elapsed + check_interval)) + progress "Warte auf HTTPS-Service Startup... (${elapsed}/${startup_timeout}s)" + done + + if [ $elapsed -ge $startup_timeout ]; then + error "❌ HTTPS-Service Timeout nach ${startup_timeout}s - Service nicht verfügbar" + + # Debugging-Informationen + info "HTTPS-Service Status-Debug:" + systemctl status "$HTTPS_SERVICE_NAME" --no-pager -l || true + journalctl -u "$HTTPS_SERVICE_NAME" --no-pager -n 10 || true + ((failed_services++)) + fi + else + error "❌ HTTPS-Service konnte nicht gestartet werden" + ((failed_services++)) + fi + else + error "❌ HTTPS-Service konnte nicht aktiviert werden" + ((failed_services++)) + fi - # RDP für Remote-Desktop - firewall-cmd --permanent --add-port=3389/tcp + # Kiosk-Service (für Produktionsinstallation) + if [ -f "$SYSTEM_SYSTEMD_DIR/$KIOSK_SERVICE_NAME.service" ]; then + progress "Aktiviere Kiosk-Service (startet beim nächsten Boot)..." + if systemctl enable "$KIOSK_SERVICE_NAME" 2>/dev/null; then + success "✅ Kiosk-Service erfolgreich aktiviert" + ((successful_services++)) + else + warning "⚠️ Kiosk-Service konnte nicht aktiviert werden" + ((failed_services++)) + fi + fi - firewall-cmd --reload + # Watchdog-Service (optional) + if [ -f "$SYSTEM_SYSTEMD_DIR/$WATCHDOG_SERVICE_NAME.service" ]; then + progress "Aktiviere und starte Watchdog-Service..." + if systemctl enable "$WATCHDOG_SERVICE_NAME" 2>/dev/null; then + if systemctl start "$WATCHDOG_SERVICE_NAME" 2>/dev/null; then + success "✅ Watchdog-Service erfolgreich aktiviert und gestartet" + ((successful_services++)) + else + warning "⚠️ Watchdog-Service aktiviert, aber Start fehlgeschlagen" + fi + else + warning "⚠️ Watchdog-Service konnte nicht aktiviert werden" + fi + fi - success "✅ Firewall konfiguriert" + # Python Watchdog-Service (optional) + if [ -f "$SYSTEM_SYSTEMD_DIR/$WATCHDOG_PYTHON_SERVICE_NAME.service" ]; then + progress "Aktiviere Python Watchdog-Service..." + if systemctl enable "$WATCHDOG_PYTHON_SERVICE_NAME" 2>/dev/null; then + success "✅ Python Watchdog-Service erfolgreich aktiviert" + ((successful_services++)) + else + warning "⚠️ Python Watchdog-Service konnte nicht aktiviert werden" + fi + fi + + # Firewall-Service (optional) + if [ -f "$SYSTEM_SYSTEMD_DIR/$FIREWALL_SERVICE_NAME.service" ]; then + progress "Aktiviere Firewall-Service..." + if systemctl enable "$FIREWALL_SERVICE_NAME" 2>/dev/null; then + success "✅ Firewall-Service erfolgreich aktiviert" + ((successful_services++)) + else + warning "⚠️ Firewall-Service konnte nicht aktiviert werden" + fi + fi + + # Zusammenfassung + log "📊 Service-Aktivierung Zusammenfassung:" + log " ✅ Erfolgreich: $successful_services Services" + log " ❌ Fehlgeschlagen: $failed_services Services" + + if [ $failed_services -eq 0 ]; then + success "✅ Alle verfügbaren Services erfolgreich konfiguriert" + elif [ $successful_services -gt 0 ]; then + warning "⚠️ $failed_services Services fehlgeschlagen, aber $successful_services Services funktionieren" + info "→ System ist grundsätzlich funktionsfähig" + else + error "❌ Alle Services fehlgeschlagen - System möglicherweise nicht funktionsfähig" + fi } -# =========================== DATENBANK INITIALISIERUNG =========================== -initialize_database() { - log "=== DATENBANK INITIALISIEREN ===" +# =========================== ROBUSTE SYSTEM-TESTS =========================== +test_application() { + log "=== UMFASSENDE SYSTEM-TESTS ===" - cd "$APP_DIR" + local test_errors=0 + local test_warnings=0 - progress "Initialisiere Datenbank..." - python3 << EOF + # Test 1: Service-Status prüfen + progress "Teste Service-Status..." + if systemctl is-active --quiet "$HTTPS_SERVICE_NAME"; then + success "✅ HTTPS-Service ist aktiv" + else + warning "⚠️ HTTPS-Service ist nicht aktiv" + ((test_warnings++)) + + # Debug-Informationen + info "Service-Status Debug:" + systemctl status "$HTTPS_SERVICE_NAME" --no-pager -l || true + fi + + # Test 2: Port-Verfügbarkeit + progress "Teste Port-Verfügbarkeit..." + if ss -tlnp | grep -q ":443 "; then + success "✅ Port 443 ist geöffnet" + else + warning "⚠️ Port 443 ist nicht geöffnet" + ((test_warnings++)) + fi + + # Test 3: HTTPS-Verbindung (robust mit mehreren Methoden) + progress "Teste HTTPS-Verbindung (robust)..." + + local max_attempts=20 + local attempt=1 + local connection_successful=false + + while [ $attempt -le $max_attempts ]; do + # Methode 1: curl mit verschiedenen Optionen + if curl -k -s --connect-timeout 3 --max-time 8 "$HTTPS_URL" >/dev/null 2>&1; then + connection_successful=true + break + fi + + # Methode 2: wget als Fallback + if command -v wget >/dev/null 2>&1; then + if wget -q --no-check-certificate --timeout=3 --tries=1 "$HTTPS_URL" -O /dev/null 2>/dev/null; then + connection_successful=true + break + fi + fi + + # Methode 3: openssl s_client als direkter Test + if echo "GET / HTTP/1.0" | openssl s_client -connect localhost:443 -quiet 2>/dev/null | grep -q "HTTP"; then + connection_successful=true + break + fi + + progress "Warte auf HTTPS-Backend... ($attempt/$max_attempts)" + sleep 3 + ((attempt++)) + done + + if [ "$connection_successful" = true ]; then + success "✅ HTTPS-Backend erreichbar unter $HTTPS_URL" + + # Erweiterte Verbindungstests + progress "Führe erweiterte HTTPS-Tests durch..." + + # Test Antwortzeit + local response_time=$(curl -k -s -w "%{time_total}" -o /dev/null "$HTTPS_URL" 2>/dev/null || echo "timeout") + if [ "$response_time" != "timeout" ]; then + info "🕐 HTTPS Antwortzeit: ${response_time}s" + + # Bewerte Antwortzeit + if [ "$(echo "$response_time < 2.0" | bc 2>/dev/null || echo "0")" -eq 1 ]; then + success "✅ Gute Antwortzeit" + elif [ "$(echo "$response_time < 5.0" | bc 2>/dev/null || echo "0")" -eq 1 ]; then + info "ℹ️ Akzeptable Antwortzeit" + else + warning "⚠️ Langsame Antwortzeit" + ((test_warnings++)) + fi + fi + + # Test HTTP-Status + local http_status=$(curl -k -s -o /dev/null -w "%{http_code}" "$HTTPS_URL" 2>/dev/null || echo "000") + if [ "$http_status" = "200" ]; then + success "✅ HTTP Status 200 OK" + else + info "ℹ️ HTTP Status: $http_status (möglicherweise Redirect oder Login-Seite)" + fi + + else + error "❌ HTTPS-Backend nicht erreichbar nach $max_attempts Versuchen" + ((test_errors++)) + + # Debugging-Informationen + info "HTTPS-Debug Informationen:" + netstat -tlnp | grep ":443" || info "Port 443 nicht gefunden" + ss -tlnp | grep ":443" || info "Port 443 nicht in ss gefunden" + fi + + # Test 4: SSL-Zertifikat (erweitert) + progress "Teste SSL-Zertifikat (erweitert)..." + + # Methode 1: openssl s_client + if echo | openssl s_client -connect localhost:443 -servername localhost 2>/dev/null | openssl x509 -noout -text >/dev/null 2>&1; then + success "✅ SSL-Zertifikat ist gültig" + + # Zertifikat-Details extrahieren + local cert_info=$(echo | openssl s_client -connect localhost:443 -servername localhost 2>/dev/null | openssl x509 -noout -subject -dates 2>/dev/null || echo "Nicht verfügbar") + info "📜 Zertifikat-Info: $cert_info" + + else + warning "⚠️ SSL-Zertifikat-Test fehlgeschlagen" + ((test_warnings++)) + + # Alternative: Teste ob Zertifikat-Dateien existieren + if [ -f "$APP_DIR/certs/localhost/localhost.crt" ] && [ -f "$APP_DIR/certs/localhost/localhost.key" ]; then + info "📁 SSL-Zertifikat-Dateien sind vorhanden" + + # Teste Zertifikat-Datei direkt + if openssl x509 -in "$APP_DIR/certs/localhost/localhost.crt" -noout -text >/dev/null 2>&1; then + success "✅ SSL-Zertifikat-Datei ist gültig" + else + warning "⚠️ SSL-Zertifikat-Datei ist ungültig" + ((test_warnings++)) + fi + fi + fi + + # Test 5: Python-Anwendung Import-Test + progress "Teste Python-Anwendung Import..." + + cd "$APP_DIR" 2>/dev/null || true + + # Setze Test-Umgebung + export FLASK_ENV=testing + export MYP_TESTING=1 + export PYTHONPATH="$APP_DIR:${PYTHONPATH:-}" + + if timeout 15 python3 -c " import sys -sys.path.append('${APP_DIR}') -from models import init_database, create_initial_admin +import os +sys.path.insert(0, '$APP_DIR') +os.chdir('$APP_DIR') + try: - init_database() - create_initial_admin() - print("✅ Datenbank erfolgreich initialisiert") + import app + print('✅ Flask-App Import erfolgreich') + + # Test ob Flask-App-Objekt verfügbar + if hasattr(app, 'app'): + print('✅ Flask-App-Objekt verfügbar') + else: + print('⚠️ Flask-App-Objekt nicht gefunden') + except Exception as e: - print(f"❌ Fehler bei Datenbank-Initialisierung: {e}") - sys.exit(1) -EOF - - cd "$CURRENT_DIR" -} - -# =========================== SERVICES STARTEN =========================== -start_services() { - log "=== SERVICES STARTEN ===" - - # SSH aktivieren - systemctl enable ssh - systemctl start ssh - - # HTTPS Service - systemctl enable "${HTTPS_SERVICE_NAME}" - systemctl start "${HTTPS_SERVICE_NAME}" - - # Warte bis Service läuft - sleep 5 - - # Kiosk Service (optional) - # systemctl enable "${KIOSK_SERVICE_NAME}" - # systemctl start "${KIOSK_SERVICE_NAME}" - - success "✅ Services gestartet" -} - -# =========================== INSTALLATION TESTEN =========================== -test_installation() { - log "=== INSTALLATION TESTEN ===" - - # Teste HTTPS Service - if systemctl is-active --quiet "${HTTPS_SERVICE_NAME}"; then - success "✅ HTTPS Service läuft" + print(f'⚠️ App-Import-Problem: {e}') + exit(1) +" 2>&1; then + success "✅ Python-Anwendung kann erfolgreich importiert werden" else - warning "⚠️ HTTPS Service läuft nicht" + warning "⚠️ Python-Anwendung Import-Probleme (möglicherweise nicht kritisch)" + ((test_warnings++)) fi - # Teste Webserver - if curl -k -s -o /dev/null -w "%{http_code}" https://localhost:${HTTPS_PORT} | grep -q "200\|302"; then - success "✅ Webserver antwortet" - else - warning "⚠️ Webserver antwortet nicht" + cd "$CURRENT_DIR" 2>/dev/null || true + + # Test 6: Verzeichnisstruktur-Validierung + progress "Validiere Verzeichnisstruktur..." + + local structure_ok=true + local required_files=("$APP_DIR/app.py" "$APP_DIR/models.py" "$APP_DIR/requirements.txt") + local required_dirs=("$APP_DIR/static" "$APP_DIR/templates" "$APP_DIR/blueprints") + + for file in "${required_files[@]}"; do + if [ ! -f "$file" ]; then + warning "❌ Wichtige Datei fehlt: $file" + structure_ok=false + ((test_warnings++)) + fi + done + + for dir in "${required_dirs[@]}"; do + if [ ! -d "$dir" ]; then + warning "❌ Wichtiges Verzeichnis fehlt: $dir" + structure_ok=false + ((test_warnings++)) + fi + done + + if [ "$structure_ok" = true ]; then + success "✅ Verzeichnisstruktur vollständig" fi - # Zeige Zugriffs-URLs - local ip_address=$(hostname -I | awk '{print $1}') - log "" - log "🌐 ZUGRIFFS-INFORMATIONEN:" - log " Local: https://localhost:${HTTPS_PORT}" - log " Netzwerk: https://${ip_address}:${HTTPS_PORT}" - log " SSH: ssh pi@${ip_address}" - log "" - log "📝 STANDARD-ANMELDEDATEN:" - log " Admin-Benutzer: admin" - log " Admin-Passwort: admin123" - log "" + # Zusammenfassung der Tests + log "📊 System-Test Zusammenfassung:" + log " ❌ Fehler: $test_errors" + log " ⚠️ Warnungen: $test_warnings" + + if [ $test_errors -eq 0 ] && [ $test_warnings -eq 0 ]; then + success "✅ Alle System-Tests erfolgreich - System vollständig funktionsfähig" + return 0 + elif [ $test_errors -eq 0 ]; then + warning "⚠️ System-Tests mit $test_warnings Warnungen abgeschlossen - System grundsätzlich funktionsfähig" + return 0 + else + error "❌ System-Tests mit $test_errors Fehlern fehlgeschlagen - System möglicherweise nicht funktionsfähig" + return 1 + fi +} + +# =========================== AUFRÄUMEN =========================== +cleanup_old_files() { + log "=== AUFRÄUMEN ALTE DATEIEN ===" + + progress "Entferne alte Shell-Skripte..." + + # Entferne alte Skripte (falls vorhanden) + local old_scripts=("combined.sh" "installer.sh") + + for script in "${old_scripts[@]}"; do + if [ -f "$CURRENT_DIR/$script" ]; then + progress "Entferne: $script" + rm -f "$CURRENT_DIR/$script" || warning "Fehler beim Entfernen von $script" + fi + done + + log "✅ Aufräumen abgeschlossen" } # =========================== HAUPTMENÜ =========================== show_menu() { clear - echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" - echo -e "${CYAN}║ MYP DRUCKERVERWALTUNG - SETUP v${APP_VERSION} ║${NC}" - echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" + echo -e "${CYAN}=================================================================${NC}" + echo -e "${CYAN} $APP_NAME - Setup-Skript v$APP_VERSION${NC}" + echo -e "${CYAN}=================================================================${NC}" echo "" - echo -e "${BLUE}Wählen Sie eine Option:${NC}" + echo -e "${YELLOW}Bitte wählen Sie eine Option:${NC}" + echo "" + echo -e "${GREEN}1)${NC} Abhängigkeiten installieren und System für manuelles Testen vorbereiten" + echo -e " ${BLUE}→ Python, Node.js, SSL-Zertifikate, Anwendung deployed, minimaler Test${NC}" + echo -e " ${BLUE}→ System bereit für manuelle Tests und Entwicklung${NC}" + echo "" + echo -e "${GREEN}2)${NC} Vollständige Kiosk-Installation mit Remote-Zugang" + echo -e " ${BLUE}→ Komplette Produktionsinstallation mit automatischem Kiosk-Start${NC}" + echo -e " ${BLUE}→ RDP (root:744563017196A), SSH (user:raspberry), Firewall${NC}" + echo -e " ${BLUE}→ Automatischer Login und Kiosk-Modus beim Boot${NC}" echo "" - echo -e "${GREEN}1)${NC} Vollständige Installation (Produktion)" - echo -e "${GREEN}2)${NC} Nur Abhängigkeiten installieren (Entwicklung)" echo -e "${GREEN}3)${NC} Beenden" echo "" - echo -n "Ihre Auswahl [1-3]: " + echo -e "${CYAN}=================================================================${NC}" + echo -n "Ihre Wahl [1-3]: " +} + +# =========================== INSTALLATIONS-MODI =========================== +install_dependencies_only() { + # Logging initialisieren + init_logging + + log "=== MODUS: ROBUSTE ABHÄNGIGKEITEN-INSTALLATION FÜR MANUELLES TESTEN ===" + + # Grundlegende System-Validierung + check_root + check_system_resources + check_debian_system + check_internet_connection + + # System-Konfiguration + configure_hostname + update_system + configure_network_security + + # Core-Abhängigkeiten installieren + install_python_dependencies + install_nodejs_npm + install_ssl_certificates + install_python_packages + + # Anwendung deployen + deploy_application + install_npm_dependencies + generate_ssl_certificate + + # Services für manuelles Testen vorbereiten + install_systemd_services + enable_and_start_services + + # Performance-Optimierungen auch für manuelles Testen + optimize_webapp_performance + optimize_static_assets + + # Umfassende System-Tests + progress "Starte umfassende System-Tests..." + if test_application; then + success "✅ Alle System-Tests erfolgreich!" + else + warning "⚠️ System-Tests mit Problemen - System möglicherweise eingeschränkt funktionsfähig" + fi + + # Cleanup + cleanup_old_files + + success "✅ Robuste Abhängigkeiten-Installation abgeschlossen!" + log "📋 Installation Zusammenfassung:" + log " 🖥️ Hostname: raspberrypi" + log " 🐍 Python-Umgebung: Vollständig konfiguriert" + log " 🌐 Node.js: Installiert und optimiert" + log " 🔒 SSL-Zertifikate: Generiert und konfiguriert" + log " 📁 Anwendung: In $APP_DIR deployed" + log " ⚡ Performance: Optimiert für Raspberry Pi" + log " 🔧 Services: Installiert und gestartet" + info "" + info "🚀 System bereit für manuelle Tests und Entwicklung!" + info "🌐 HTTPS-Backend sollte verfügbar sein: $HTTPS_URL" + info "⚙️ Manuelle App-Start Alternative: cd /opt/myp && python3 app.py" + + # Fehler-Zusammenfassung anzeigen + show_error_summary +} + +install_full_production_system() { + # Logging initialisieren + init_logging + + log "=== MODUS: VOLLSTÄNDIGE ROBUSTE KIOSK-INSTALLATION MIT REMOTE-ZUGANG ===" + + # Umfassende System-Validierung + check_root + check_system_resources + check_debian_system + check_internet_connection + + # System-Grundkonfiguration + configure_hostname + + # Intelligente Abhängigkeiten-Installation + if [ ! -d "$APP_DIR" ] || [ ! -f "$APP_DIR/app.py" ]; then + warning "Anwendung noch nicht deployed - führe robuste Abhängigkeiten-Installation durch..." + + # Vollständige Basis-Installation + update_system + configure_network_security + install_python_dependencies + install_nodejs_npm + install_ssl_certificates + install_python_packages + deploy_application + install_npm_dependencies + generate_ssl_certificate + else + info "Anwendung bereits deployed - überspringe Basis-Installation" + # Trotzdem Netzwerk-Sicherheit aktualisieren + configure_network_security + fi + + # Desktop-Environments entfernen und minimale X11 installieren + remove_desktop_environments + install_minimal_x11 + + # Performance-Optimierungen für Raspberry Pi Webapp + optimize_webapp_performance + optimize_static_assets + + # Remote-Zugang konfigurieren (robust) + install_remote_access + configure_firewall + + # Kiosk-System konfigurieren + create_kiosk_user + configure_autologin + configure_kiosk_autostart + + # Services installieren und aktivieren (robust) + install_systemd_services + enable_and_start_services + + # Umfassende System-Tests + progress "Führe umfassende Produktions-System-Tests durch..." + local system_tests_passed=true + + if test_application; then + success "✅ Anwendungs-Tests erfolgreich" + else + warning "⚠️ Anwendungs-Tests mit Problemen" + system_tests_passed=false + fi + + if test_remote_access; then + success "✅ Remote-Zugang-Tests erfolgreich" + else + warning "⚠️ Remote-Zugang-Tests mit Problemen" + system_tests_passed=false + fi + + # Aufräumen + cleanup_old_files + + # Finale Status-Bewertung + if [ "$system_tests_passed" = true ]; then + success "✅ Vollständige robuste Kiosk-Installation erfolgreich abgeschlossen!" + else + warning "⚠️ Vollständige Kiosk-Installation mit Einschränkungen abgeschlossen" + info "→ Grundfunktionalität sollte verfügbar sein, manuelle Überprüfung empfohlen" + fi + + log "📋 Vollständige Installation Zusammenfassung:" + log " 🖥️ Hostname: raspberrypi" + log " 🔐 Kiosk-Modus: Konfiguriert (startet beim nächsten Boot)" + log " 📡 SSH-Zugang: user:raspberry (Port 22)" + log " 🖥️ RDP-Zugang: root:744563017196A (Port 3389)" + log " 🔒 Firewall: Konfiguriert und aktiv" + log " ⚡ Performance: Optimiert für Raspberry Pi" + log " 🌐 HTTPS-Backend: $HTTPS_URL" + log " 🛡️ Sicherheit: IPv6 deaktiviert, erweiterte Netzwerk-Sicherheit" + info "" + success "🚀 Produktionssystem vollständig einsatzbereit!" + warning "⚠️ NEUSTART ERFORDERLICH für automatischen Kiosk-Start: sudo reboot" + + # Fehler-Zusammenfassung anzeigen + show_error_summary +} + +# =========================== RDP & SSH ZUGANG =========================== +install_remote_access() { + log "=== INSTALLIERE REMOTE-ZUGANG (RDP & SSH) ===" + + # SSH-Server installieren und konfigurieren + progress "Installiere und konfiguriere SSH-Server..." + apt-get install -y openssh-server || error "SSH-Server Installation fehlgeschlagen" + + # SSH-Service aktivieren + systemctl enable ssh + systemctl start ssh + + # SSH-Benutzer 'user' erstellen (falls nicht vorhanden) + if ! id "user" &>/dev/null; then + progress "Erstelle SSH-Benutzer: user" + useradd -m -s /bin/bash user || error "Kann SSH-Benutzer nicht erstellen" + echo "user:raspberry" | chpasswd || error "Kann Passwort für SSH-Benutzer nicht setzen" + usermod -aG sudo user 2>/dev/null || true + + # pip-Konfiguration für SSH-Benutzer + mkdir -p "/home/user/.pip" 2>/dev/null || true + cat > "/home/user/.pip/pip.conf" << 'EOF' +[global] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +timeout = 60 +retries = 3 + +[install] +break-system-packages = true +trusted-host = pypi.org + pypi.python.org + files.pythonhosted.org +EOF + chown "user:user" "/home/user/.pip/pip.conf" 2>/dev/null || true + + log "✅ SSH-Benutzer 'user' erstellt mit Passwort 'raspberry'" + else + info "SSH-Benutzer 'user' existiert bereits" + echo "user:raspberry" | chpasswd || warning "Konnte Passwort für SSH-Benutzer nicht aktualisieren" + fi + + # RDP-Server (xrdp) installieren - vereinfachter Ansatz + progress "Installiere RDP-Server (xrdp) - vereinfachte Installation..." + + # Alle bestehenden xrdp-Installationen entfernen + progress "Entferne vorherige xrdp-Installationen..." + systemctl stop xrdp xrdp-sesman 2>/dev/null || true + systemctl disable xrdp xrdp-sesman 2>/dev/null || true + apt-get remove --purge -y xrdp 2>/dev/null || true + rm -rf /etc/xrdp /var/log/xrdp* 2>/dev/null || true + + # XFCE Desktop installieren (minimal) + progress "Installiere minimale XFCE-Umgebung..." + if ! apt-get install -y xfce4-session xfce4-panel xfce4-terminal xfce4-settings xfdesktop4 dbus-x11; then + warning "Minimale XFCE-Installation fehlgeschlagen - verwende Fallback..." + apt-get install -y xfce4 dbus-x11 || error "XFCE Installation fehlgeschlagen" + fi + + # xrdp neu installieren + progress "Installiere xrdp neu..." + apt-get update + apt-get install -y xrdp || error "xrdp Installation fehlgeschlagen" + + # Benutzer zur xrdp-Gruppe hinzufügen + usermod -aG xrdp root 2>/dev/null || true + if id "user" &>/dev/null; then + usermod -aG xrdp user 2>/dev/null || true + fi + + # Erstelle minimale xrdp-Konfiguration + progress "Erstelle minimale xrdp-Konfiguration..." + + # Backup der Original-Konfiguration + cp /etc/xrdp/xrdp.ini /etc/xrdp/xrdp.ini.original 2>/dev/null || true + + # Sehr einfache xrdp.ini + cat > /etc/xrdp/xrdp.ini << 'EOF' +[Globals] +ini_version=1 +fork=true +port=3389 +tcp_nodelay=true +tcp_keepalive=true +security_layer=rdp +autorun= +allow_channels=true +allow_multimon=false +bitmap_cache=true +bitmap_compression=true +bulk_compression=false +max_bpp=24 +new_cursors=true +use_fastpath=both +require_credentials=true +ask_for_reconnect_reason=false +enable_token_login=false + +[Xorg] +name=Xorg +lib=libxup.so +username=ask +password=ask +ip=127.0.0.1 +port=-1 +code=20 +EOF + + # Einfache sesman.ini + cp /etc/xrdp/sesman.ini /etc/xrdp/sesman.ini.original 2>/dev/null || true + + cat > /etc/xrdp/sesman.ini << 'EOF' +[Globals] +ListenAddress=127.0.0.1 +ListenPort=3350 +EnableUserWindowManager=true +UserWindowManager=startxfce4 +DefaultWindowManager=startxfce4 + +[Security] +AllowRootLogin=true +MaxLoginRetry=4 +AlwaysGroupCheck=false + +[Sessions] +X11DisplayOffset=10 +MaxSessions=10 +KillDisconnected=false +IdleTimeLimit=0 +DisconnectedTimeLimit=0 + +[Logging] +LogFile=xrdp-sesman.log +LogLevel=INFO +EnableSyslog=true +SyslogLevel=INFO + +[Xorg] +param1=-bs +param2=-nolisten +param3=tcp +param4=-dpi +param5=96 +EOF + + # Erstelle .xsession für XFCE + progress "Konfiguriere XFCE-Sessions..." + + # Root .xsession + cat > /root/.xsession << 'EOF' +#!/bin/bash +export XDG_SESSION_DESKTOP=xfce +export XDG_DATA_DIRS=/usr/share/xfce4:/usr/local/share:/usr/share +export XDG_CONFIG_DIRS=/etc/xdg/xdg-xfce:/etc/xdg +exec startxfce4 +EOF + chmod +x /root/.xsession + + # User .xsession (falls user existiert) + if id "user" &>/dev/null; then + cat > /home/user/.xsession << 'EOF' +#!/bin/bash +export XDG_SESSION_DESKTOP=xfce +export XDG_DATA_DIRS=/usr/share/xfce4:/usr/local/share:/usr/share +export XDG_CONFIG_DIRS=/etc/xdg/xdg-xfce:/etc/xdg +exec startxfce4 +EOF + chown user:user /home/user/.xsession + chmod +x /home/user/.xsession + fi + + # Root-Passwort setzen + progress "Setze Root-Passwort für RDP..." + echo "root:744563017196A" | chpasswd || error "Kann Root-Passwort nicht setzen" + + # Log-Verzeichnisse erstellen mit korrekten Berechtigungen + progress "Erstelle Log-Verzeichnisse..." + mkdir -p /var/log + touch /var/log/xrdp.log /var/log/xrdp-sesman.log 2>/dev/null || true + chown xrdp:xrdp /var/log/xrdp*.log 2>/dev/null || true + chmod 644 /var/log/xrdp*.log 2>/dev/null || true + + # Erstelle xrdp-Konfigurationsverzeichnisse + mkdir -p /etc/xrdp/cert /var/run/xrdp + chown xrdp:xrdp /etc/xrdp/cert /var/run/xrdp 2>/dev/null || true + + # Services aktivieren und starten + progress "Starte xrdp-Services..." + + # systemd daemon reload + systemctl daemon-reload + + # Services aktivieren + systemctl enable xrdp-sesman + systemctl enable xrdp + + # Services starten (sesman zuerst) + systemctl start xrdp-sesman + sleep 3 + + # Prüfe sesman-Status + if systemctl is-active --quiet xrdp-sesman; then + success "✅ xrdp-sesman erfolgreich gestartet" + + # Jetzt xrdp starten + systemctl start xrdp + sleep 3 + + if systemctl is-active --quiet xrdp; then + success "✅ xrdp erfolgreich gestartet" + else + warning "⚠️ xrdp konnte nicht gestartet werden" + journalctl -u xrdp --no-pager -l | tail -10 + fi + else + warning "⚠️ xrdp-sesman konnte nicht gestartet werden" + journalctl -u xrdp-sesman --no-pager -l | tail -10 + fi + + cd "$CURRENT_DIR" + + # Finaler Status-Check + if systemctl is-active --quiet xrdp && systemctl is-active --quiet xrdp-sesman; then + log "✅ Remote-Zugang vollständig konfiguriert:" + log " 📡 SSH: user:raspberry (Port 22)" + log " 🖥️ RDP: root:744563017196A (Port 3389)" + log " 🖥️ RDP: user:raspberry (Port 3389)" + elif systemctl is-active --quiet ssh; then + log "✅ SSH-Zugang konfiguriert:" + log " 📡 SSH: user:raspberry (Port 22)" + warning "⚠️ RDP-Installation unvollständig" + info "Manuelle Überprüfung erforderlich:" + info " systemctl status xrdp" + info " systemctl status xrdp-sesman" + info " journalctl -u xrdp -f" + else + error "❌ Weder SSH noch RDP konnten konfiguriert werden" + fi +} + +# =========================== FIREWALL KONFIGURATION =========================== +configure_firewall() { + log "=== KONFIGURIERE FIREWALL (firewalld) ===" + + # firewalld installieren + progress "Installiere firewalld..." + apt-get install -y firewalld || error "firewalld Installation fehlgeschlagen" + + # firewalld aktivieren und starten + systemctl enable firewalld + systemctl start firewalld + + # Warte kurz bis firewalld vollständig gestartet ist + sleep 5 + + progress "Konfiguriere firewalld-Zonen und -Regeln..." + + # Firewall-Status prüfen + if ! firewall-cmd --state >/dev/null 2>&1; then + error "firewalld ist nicht aktiv oder reagiert nicht" + fi + + # Bestehende Zone entfernen falls vorhanden + progress "Entferne bestehende myp-backend Zone falls vorhanden..." + if firewall-cmd --permanent --get-zones | grep -q "myp-backend"; then + log "Entferne bestehende myp-backend Zone..." + firewall-cmd --permanent --delete-zone=myp-backend 2>/dev/null || true + firewall-cmd --reload + sleep 2 + fi + + # Zone neu erstellen + progress "Erstelle neue myp-backend Zone..." + if ! firewall-cmd --permanent --new-zone=myp-backend; then + error "Fehler beim Erstellen der myp-backend Zone" + fi + + # Konfiguration neu laden + firewall-cmd --reload + sleep 2 + + # Erweiterte Netzwerk-Quellen definieren (nur IPv4) + progress "Füge Netzwerk-Quellen hinzu..." + firewall-cmd --permanent --zone=myp-backend --add-source=192.168.0.0/16 || error "Fehler beim Hinzufügen des 192.168.0.0/16 Netzwerks" + firewall-cmd --permanent --zone=myp-backend --add-source=127.0.0.1/32 || error "Fehler beim Hinzufügen von localhost" + + # Lokaler Hostname "raspberrypi" hinzufügen + local local_hostname="raspberrypi" + progress "Füge lokalen Hostname hinzu: $local_hostname" + local local_ip=$(getent hosts "$local_hostname" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true) + if [ -n "$local_ip" ]; then + firewall-cmd --permanent --zone=myp-backend --add-source="$local_ip/32" 2>/dev/null || warning "Konnte lokalen Hostname nicht hinzufügen" + log "✅ Lokaler Hostname $local_hostname hinzugefügt: $local_ip" + else + info "Lokaler Hostname $local_hostname nicht auflösbar - wird beim nächsten Boot verfügbar sein" + fi + + # Frontend-Server m040tbaraspi001 hinzufügen (falls auflösbar) + progress "Füge Frontend-Server hinzu: m040tbaraspi001" + local frontend_ip=$(getent hosts "m040tbaraspi001" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true) + if [ -n "$frontend_ip" ]; then + firewall-cmd --permanent --zone=myp-backend --add-source="$frontend_ip/32" 2>/dev/null || warning "Konnte Frontend-Server IP nicht hinzufügen" + log "✅ Frontend-Server m040tbaraspi001 hinzugefügt: $frontend_ip" + else + # Versuche auch mit FQDN + local frontend_fqdn_ip=$(getent hosts "m040tbaraspi001.de040.corpintra.net" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true) + if [ -n "$frontend_fqdn_ip" ]; then + firewall-cmd --permanent --zone=myp-backend --add-source="$frontend_fqdn_ip/32" 2>/dev/null || warning "Konnte Frontend-Server FQDN nicht hinzufügen" + log "✅ Frontend-Server m040tbaraspi001.de040.corpintra.net hinzugefügt: $frontend_fqdn_ip" + else + info "Frontend-Server m040tbaraspi001 nicht auflösbar - überspringe" + fi + fi + + # Ports und Services hinzufügen + progress "Konfiguriere Ports und Services..." + + # HTTPS für API & Kiosk zulassen + firewall-cmd --permanent --zone=myp-backend --add-port=443/tcp || error "Fehler beim Hinzufügen von Port 443" + + # SSH für Wartung + firewall-cmd --permanent --zone=myp-backend --add-service=ssh || error "Fehler beim Hinzufügen des SSH-Service" + + # RDP für Remote-Desktop + firewall-cmd --permanent --zone=myp-backend --add-port=3389/tcp || error "Fehler beim Hinzufügen von Port 3389" + + # IPv6 in firewalld deaktivieren + progress "Deaktiviere IPv6 in firewalld..." + firewall-cmd --permanent --set-target=DROP --zone=public --family=ipv6 2>/dev/null || warning "IPv6 konnte nicht in public Zone deaktiviert werden" + firewall-cmd --permanent --set-target=DROP --zone=myp-backend --family=ipv6 2>/dev/null || warning "IPv6 konnte nicht in myp-backend Zone deaktiviert werden" + + # Default-Zone setzen + progress "Setze Default-Zone..." + firewall-cmd --set-default-zone=myp-backend || error "Fehler beim Setzen der Default-Zone" + + # Änderungen übernehmen + progress "Lade Firewall-Konfiguration neu..." + firewall-cmd --reload || error "Fehler beim Neuladen der Firewall-Konfiguration" + + # Kurz warten und Status prüfen + sleep 3 + + # Firewall-Status anzeigen + progress "Firewall-Konfiguration:" + if firewall-cmd --list-all --zone=myp-backend 2>/dev/null; then + log "✅ Firewall-Konfiguration erfolgreich angezeigt" + else + warning "⚠️ Firewall-Status konnte nicht angezeigt werden" + fi + + # Finale Validierung + progress "Validiere Firewall-Konfiguration..." + local validation_errors=0 + + # Prüfe ob Zone existiert + if ! firewall-cmd --get-zones | grep -q "myp-backend"; then + error "Zone myp-backend wurde nicht korrekt erstellt" + ((validation_errors++)) + fi + + # Prüfe Default-Zone + if [ "$(firewall-cmd --get-default-zone)" != "myp-backend" ]; then + warning "Default-Zone ist nicht myp-backend" + ((validation_errors++)) + fi + + # Prüfe Ports + if ! firewall-cmd --zone=myp-backend --query-port=443/tcp 2>/dev/null; then + warning "Port 443 nicht korrekt konfiguriert" + ((validation_errors++)) + fi + + if ! firewall-cmd --zone=myp-backend --query-port=3389/tcp 2>/dev/null; then + warning "Port 3389 nicht korrekt konfiguriert" + ((validation_errors++)) + fi + + if ! firewall-cmd --zone=myp-backend --query-service=ssh 2>/dev/null; then + warning "SSH-Service nicht korrekt konfiguriert" + ((validation_errors++)) + fi + + if [ $validation_errors -eq 0 ]; then + log "✅ Firewall konfiguriert und validiert:" + log " 🔒 Zone: myp-backend (als Default gesetzt)" + log " 🌐 Netzwerk: 192.168.0.0/16 (nur IPv4)" + log " 🏠 Localhost: 127.0.0.1" + log " 🖥️ Lokaler Host: raspberrypi" + log " 📡 Frontend-Server: m040tbaraspi001" + log " 🔌 Ports: 443/tcp (HTTPS), 22/tcp (SSH), 3389/tcp (RDP)" + log " 🚫 IPv6 vollständig blockiert" + else + warning "⚠️ Firewall-Konfiguration mit $validation_errors Fehlern abgeschlossen" + info "System funktioniert möglicherweise trotzdem - manuelle Überprüfung empfohlen" + fi +} + +# =========================== REMOTE-ZUGANG TESTEN =========================== +test_remote_access() { + log "=== TESTE REMOTE-ZUGANG ===" + + # SSH-Service testen + progress "Teste SSH-Service..." + if systemctl is-active --quiet ssh; then + success "✅ SSH-Service läuft" + + # SSH-Port testen + if ss -tlnp | grep -q ":22 "; then + success "✅ SSH-Port 22 ist offen" + else + warning "⚠️ SSH-Port 22 nicht erreichbar" + fi + else + warning "⚠️ SSH-Service läuft nicht" + fi + + # RDP-Services testen + progress "Teste RDP-Services..." + + local xrdp_sesman_status="❌" + local xrdp_status="❌" + + if systemctl is-active --quiet xrdp-sesman; then + xrdp_sesman_status="✅" + success "✅ xrdp-sesman läuft" + else + warning "⚠️ xrdp-sesman läuft nicht" + fi + + if systemctl is-active --quiet xrdp; then + xrdp_status="✅" + success "✅ xrdp läuft" + + # RDP-Port testen + if ss -tlnp | grep -q ":3389 "; then + success "✅ RDP-Port 3389 ist offen" + else + warning "⚠️ RDP-Port 3389 nicht erreichbar" + fi + else + warning "⚠️ xrdp läuft nicht" + fi + + # Firewall-Status testen + progress "Teste Firewall-Status..." + if systemctl is-active --quiet firewalld; then + success "✅ Firewall läuft" + + # Prüfe ob Ports offen sind + if firewall-cmd --zone=myp-backend --query-port=22/tcp 2>/dev/null; then + success "✅ SSH-Port in Firewall freigegeben" + else + warning "⚠️ SSH-Port nicht in Firewall freigegeben" + fi + + if firewall-cmd --zone=myp-backend --query-port=3389/tcp 2>/dev/null; then + success "✅ RDP-Port in Firewall freigegeben" + else + warning "⚠️ RDP-Port nicht in Firewall freigegeben" + fi + else + warning "⚠️ Firewall läuft nicht" + fi + + # Netzwerk-Interface testen + progress "Teste Netzwerk-Konfiguration..." + local ip_address=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7}' | head -1 || echo "Unbekannt") + if [ "$ip_address" != "Unbekannt" ]; then + success "✅ Netzwerk-Interface aktiv: $ip_address" + info "Zugang-URLs:" + info " SSH: ssh user@$ip_address" + if [ "$xrdp_status" = "✅" ]; then + info " RDP: $ip_address:3389 (root:744563017196A oder user:raspberry)" + fi + else + warning "⚠️ Keine Netzwerk-IP ermittelt" + fi + + # Status-Zusammenfassung + log "📊 Service-Status:" + log " SSH: $(systemctl is-active --quiet ssh && echo "✅ Aktiv" || echo "❌ Inaktiv")" + log " xrdp-sesman: $xrdp_sesman_status $(systemctl is-active --quiet xrdp-sesman && echo "Aktiv" || echo "Inaktiv")" + log " xrdp: $xrdp_status $(systemctl is-active --quiet xrdp && echo "Aktiv" || echo "Inaktiv")" + + log "✅ Remote-Zugang-Test abgeschlossen" +} + +# =========================== HOSTNAME KONFIGURATION =========================== +configure_hostname() { + log "=== KONFIGURIERE HOSTNAME ===" + + local target_hostname="raspberrypi" + local current_hostname=$(hostname) + + if [ "$current_hostname" != "$target_hostname" ]; then + progress "Setze Hostname von '$current_hostname' auf '$target_hostname'..." + + # Hostname sofort setzen + hostnamectl set-hostname "$target_hostname" || error "Fehler beim Setzen des Hostnames" + + # /etc/hostname aktualisieren + echo "$target_hostname" > /etc/hostname + + # /etc/hosts aktualisieren + cp /etc/hosts /etc/hosts.backup + sed -i "s/127.0.1.1.*/127.0.1.1\t$target_hostname/" /etc/hosts + + # Falls kein 127.0.1.1 Eintrag existiert, hinzufügen + if ! grep -q "127.0.1.1" /etc/hosts; then + echo "127.0.1.1 $target_hostname" >> /etc/hosts + fi + + log "✅ Hostname erfolgreich auf '$target_hostname' gesetzt" + else + log "✅ Hostname bereits korrekt: '$target_hostname'" + fi + + # Hostname-Auflösung testen + if getent hosts "$target_hostname" >/dev/null 2>&1; then + local resolved_ip=$(getent hosts "$target_hostname" | awk '{print $1}' | head -1) + log "✅ Hostname-Auflösung funktioniert: $target_hostname -> $resolved_ip" + else + warning "⚠️ Hostname-Auflösung für '$target_hostname' fehlgeschlagen" + fi +} + +# =========================== WEBAPP PERFORMANCE-OPTIMIERUNG =========================== +optimize_webapp_performance() { + log "=== WEBAPP PERFORMANCE-OPTIMIERUNG FÜR RASPBERRY PI ===" + + # Python/Flask spezifische Optimierungen + progress "Konfiguriere Python-Performance-Optimierungen..." + + # Python Bytecode Optimierung aktivieren + cat > /etc/environment << 'EOF' +# Python Performance Optimierungen +PYTHONOPTIMIZE=2 +PYTHONDONTWRITEBYTECODE=1 +PYTHONUNBUFFERED=1 +PYTHONHASHSEED=random + +# Flask/SQLite Optimierungen +FLASK_ENV=production +FLASK_DEBUG=0 +SQLITE_TMPDIR=/tmp + +# Memory Optimierungen +MALLOC_ARENA_MAX=2 +MALLOC_MMAP_THRESHOLD=131072 +MALLOC_TRIM_THRESHOLD=131072 + +EOF + + # Systemd Service-Optimierungen + progress "Optimiere Systemd-Services für bessere Performance..." + + # Stoppe unnötige Services + local unnecessary_services=( + "bluetooth.service" + "hciuart.service" + "avahi-daemon.service" + "cups.service" + "cups-browsed.service" + "ModemManager.service" + "wpa_supplicant.service" + ) + + for service in "${unnecessary_services[@]}"; do + if systemctl is-enabled "$service" 2>/dev/null; then + systemctl disable "$service" 2>/dev/null || true + systemctl stop "$service" 2>/dev/null || true + log "✅ Service deaktiviert: $service" + fi + done + + # Tmpfs für temporäre Dateien + progress "Konfiguriere tmpfs für bessere I/O Performance..." + + cat >> /etc/fstab << 'EOF' + +# MYP Performance Optimierungen - tmpfs für temporäre Dateien +tmpfs /tmp tmpfs defaults,noatime,nosuid,size=256m 0 0 +tmpfs /var/tmp tmpfs defaults,noatime,nosuid,size=128m 0 0 +tmpfs /var/log tmpfs defaults,noatime,nosuid,size=64m 0 0 + +EOF + + # Logrotate für tmpfs-Logs konfigurieren + cat > /etc/logrotate.d/myp-tmpfs << 'EOF' +/var/log/*.log { + daily + missingok + rotate 2 + compress + notifempty + create 0644 root root + copytruncate +} +EOF + + # Systemd Journal Einstellungen optimieren + progress "Optimiere systemd Journal für bessere Performance..." + + mkdir -p /etc/systemd/journald.conf.d + cat > /etc/systemd/journald.conf.d/myp-performance.conf << 'EOF' +[Journal] +# Journal Optimierungen für Raspberry Pi +Storage=volatile +RuntimeMaxUse=32M +RuntimeKeepFree=16M +RuntimeMaxFileSize=8M +RuntimeMaxFiles=4 +MaxRetentionSec=1day +MaxFileSec=1hour +ForwardToSyslog=no +ForwardToKMsg=no +ForwardToConsole=no +ForwardToWall=no +EOF + + # Crontab für regelmäßige Cache-Bereinigung + progress "Installiere automatische Cache-Bereinigung..." + + cat > /etc/cron.d/myp-cache-cleanup << 'EOF' +# MYP Cache und Memory Cleanup +# Alle 6 Stunden Cache bereinigen +0 */6 * * * root /bin/echo 3 > /proc/sys/vm/drop_caches +# Täglich um 3 Uhr temporäre Dateien bereinigen +0 3 * * * root /usr/bin/find /tmp -type f -atime +1 -delete 2>/dev/null +# Wöchentlich Python Cache bereinigen +0 2 * * 0 root /usr/bin/find /opt/myp -name "*.pyc" -delete 2>/dev/null +0 2 * * 0 root /usr/bin/find /opt/myp -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null +EOF + + # Limits für bessere Ressourcen-Verwaltung + progress "Konfiguriere System-Limits..." + + cat >> /etc/security/limits.conf << 'EOF' + +# MYP Performance Limits +* soft nofile 65536 +* hard nofile 65536 +* soft nproc 32768 +* hard nproc 32768 +root soft nofile 65536 +root hard nofile 65536 + +EOF + + # Apache/Nginx entfernen falls vorhanden (Konflikt mit Flask) + progress "Entferne konfliktbehaftete Webserver..." + + local webservers=("apache2" "nginx" "lighttpd") + for webserver in "${webservers[@]}"; do + if systemctl is-enabled "$webserver" 2>/dev/null; then + systemctl stop "$webserver" 2>/dev/null || true + systemctl disable "$webserver" 2>/dev/null || true + apt-get remove --purge -y "$webserver" 2>/dev/null || true + log "✅ Webserver entfernt: $webserver" + fi + done + + log "✅ Webapp Performance-Optimierung abgeschlossen:" + log " 🚀 Python Bytecode-Optimierung aktiviert" + log " 💾 tmpfs für temporäre Dateien konfiguriert" + log " 📝 Journal-Logging optimiert" + log " 🧹 Automatische Cache-Bereinigung installiert" + log " ⚡ Unnötige Services deaktiviert" + log " 📊 System-Limits für bessere Performance gesetzt" +} + +# =========================== CSS/JS OPTIMIERUNG =========================== +optimize_static_assets() { + log "=== STATISCHE DATEIEN OPTIMIERUNG ===" + + if [ ! -d "$APP_DIR/static" ]; then + warning "Static-Ordner nicht gefunden - überspringe Asset-Optimierung" + return + fi + + progress "Analysiere und optimiere CSS/JS Dateien..." + + cd "$APP_DIR/static" + + # Erstelle optimierte CSS-Datei durch Kombination kritischer Styles + progress "Erstelle optimierte CSS-Kombination..." + + cat > css/critical.min.css << 'EOF' +/* Kritische Styles für ersten Seitenaufbau - Inline-optimiert */ +*{box-sizing:border-box}body{margin:0;font-family:system-ui,-apple-system,sans-serif;line-height:1.5} +.container{max-width:1200px;margin:0 auto;padding:0 1rem} +.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0} +.btn{display:inline-flex;align-items:center;padding:0.5rem 1rem;border:none;border-radius:0.375rem;font-weight:500;text-decoration:none;cursor:pointer;transition:all 0.15s} +.btn-primary{background:#3b82f6;color:white}.btn-primary:hover{background:#2563eb} +.card{background:white;border-radius:0.5rem;padding:1.5rem;box-shadow:0 1px 3px rgba(0,0,0,0.1)} +.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between} +.hidden{display:none}.block{display:block}.inline-block{display:inline-block} +.text-sm{font-size:0.875rem}.text-lg{font-size:1.125rem} +.font-medium{font-weight:500}.font-bold{font-weight:700} +.text-gray-600{color:#4b5563}.text-gray-900{color:#111827} +.mb-4{margin-bottom:1rem}.mt-6{margin-top:1.5rem}.p-4{padding:1rem} +.w-full{width:100%}.h-full{height:100%} +@media(max-width:768px){.container{padding:0 0.5rem}.card{padding:1rem}} +EOF + + # Erstelle minimale JavaScript-Loader + progress "Erstelle optimierten JavaScript-Loader..." + + cat > js/loader.min.js << 'EOF' +/*Minimaler Async Loader für bessere Performance*/ +(function(){var d=document,w=window;function loadCSS(href){var l=d.createElement('link');l.rel='stylesheet';l.href=href;l.media='print';l.onload=function(){this.media='all'};d.head.appendChild(l)}function loadJS(src,cb){var s=d.createElement('script');s.async=true;s.src=src;if(cb)s.onload=cb;d.head.appendChild(s)}w.loadAssets=function(){if(w.assetsLoaded)return;w.assetsLoaded=true;loadCSS('/static/css/tailwind.min.css');loadJS('/static/js/app.min.js')};if(d.readyState==='loading'){d.addEventListener('DOMContentLoaded',w.loadAssets)}else{w.loadAssets()}})(); +EOF + + # Service Worker für besseres Caching + progress "Erstelle optimierten Service Worker..." + + cat > sw-optimized.js << 'EOF' +const CACHE_NAME = 'myp-webapp-v1'; +const ASSETS_TO_CACHE = [ + '/', + '/static/css/critical.min.css', + '/static/js/loader.min.js', + '/static/favicon.svg' +]; + +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(cache => cache.addAll(ASSETS_TO_CACHE)) + ); +}); + +self.addEventListener('fetch', event => { + if (event.request.destination === 'image' || + event.request.url.includes('/static/')) { + event.respondWith( + caches.match(event.request) + .then(response => response || fetch(event.request)) + ); + } +}); +EOF + + # Gzip-Kompression für statische Dateien + progress "Komprimiere statische Dateien..." + + find . -name "*.css" -o -name "*.js" -o -name "*.html" | while read file; do + if [ -f "$file" ] && [ ! -f "$file.gz" ]; then + gzip -c "$file" > "$file.gz" 2>/dev/null || true + fi + done + + cd "$CURRENT_DIR" + + log "✅ Statische Dateien optimiert:" + log " 📦 Kritische CSS-Styles kombiniert" + log " ⚡ Asynchroner Asset-Loader erstellt" + log " 🗜️ Gzip-Kompression angewendet" + log " 🔄 Service Worker für Caching installiert" } # =========================== HAUPTPROGRAMM =========================== main() { - init_logging + # Erstelle logs-Verzeichnis im aktuellen Projektverzeichnis + mkdir -p "logs" 2>/dev/null || true while true; do show_menu @@ -445,47 +3446,46 @@ main() { case $choice in 1) - log "=== VOLLSTÄNDIGE INSTALLATION GESTARTET ===" - check_root - check_debian_system - check_required_files - update_system - install_base_dependencies - setup_python_environment - install_nodejs - build_frontend - setup_ssl_certificates - setup_systemd_services - setup_kiosk_user - configure_firewall - initialize_database - start_services - test_installation - log "✅ Installation erfolgreich abgeschlossen!" + install_dependencies_only + echo "" + echo -e "${BLUE}📁 Log-Dateien zur Überprüfung:${NC}" + echo -e " 📄 Vollständiges Log: $INSTALL_LOG" + [ $ERROR_COUNT -gt 0 ] && echo -e " 🚨 Fehler-Log: $ERROR_LOG" + [ $WARNING_COUNT -gt 0 ] && echo -e " ⚠️ Warnungs-Log: $WARNING_LOG" + echo -e " 🔍 Debug-Log: $DEBUG_LOG" + echo -e " 📊 Zusammenfassung: logs/myp-install-summary.txt" echo "" echo -n "Drücken Sie Enter um fortzufahren..." read -r ;; 2) - log "=== NUR ABHÄNGIGKEITEN INSTALLATION ===" - check_root - check_debian_system - update_system - install_base_dependencies - setup_python_environment - install_nodejs - log "✅ Abhängigkeiten installiert!" + install_full_production_system + echo "" + echo -e "${BLUE}📁 Log-Dateien zur Überprüfung:${NC}" + echo -e " 📄 Vollständiges Log: $INSTALL_LOG" + [ $ERROR_COUNT -gt 0 ] && echo -e " 🚨 Fehler-Log: $ERROR_LOG" + [ $WARNING_COUNT -gt 0 ] && echo -e " ⚠️ Warnungs-Log: $WARNING_LOG" + echo -e " 🔍 Debug-Log: $DEBUG_LOG" + echo -e " 📊 Zusammenfassung: logs/myp-install-summary.txt" echo "" echo -n "Drücken Sie Enter um fortzufahren..." read -r ;; 3) - echo -e "${CYAN}Setup beendet.${NC}" + echo "" + echo -e "${CYAN}Setup-Skript beendet${NC}" + if [ -f "$INSTALL_LOG" ]; then + echo -e "${BLUE}📁 Verfügbare Log-Dateien:${NC}" + echo -e " 📄 Vollständiges Log: $INSTALL_LOG" + [ -f "$ERROR_LOG" ] && echo -e " 🚨 Fehler-Log: $ERROR_LOG" + [ -f "$WARNING_LOG" ] && echo -e " ⚠️ Warnungs-Log: $WARNING_LOG" + [ -f "$DEBUG_LOG" ] && echo -e " 🔍 Debug-Log: $DEBUG_LOG" + [ -f "logs/myp-install-summary.txt" ] && echo -e " 📊 Zusammenfassung: logs/myp-install-summary.txt" + fi exit 0 ;; *) - echo -e "${RED}Ungültige Auswahl!${NC}" - sleep 2 + echo -e "${RED}Ungültige Auswahl. Bitte wählen Sie 1-3.${NC}" ;; esac done