diff --git a/IHK_Projektdokumentation/DOKUMENTATION_BEREINIGUNG.md b/IHK_Projektdokumentation/DOKUMENTATION_BEREINIGUNG.md new file mode 100644 index 000000000..0519ecba6 --- /dev/null +++ b/IHK_Projektdokumentation/DOKUMENTATION_BEREINIGUNG.md @@ -0,0 +1 @@ + \ 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 87a0e2785..f406161a5 100644 --- a/IHK_Projektdokumentation/Dokumentation_Final_Markdown/Dokumentation.md +++ b/IHK_Projektdokumentation/Dokumentation_Final_Markdown/Dokumentation.md @@ -154,403 +154,318 @@ Gesamtinfrastruktur Die Technische Berufsausbildungsstätte (TBA) der Mercedes-Benz AG am Standort Berlin verfügt über sechs 3D-Drucker verschiedener Hersteller -(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. +(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. -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 +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 möglich waren. -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. +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. ## 1.2 Ableitung der Projektziele -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. +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. Die zentrale technische Herausforderung bestand in der Überbrückung der -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. +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. -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. +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. ## 1.3 Projektabgrenzung -Der Projektumfang wurde – durchaus pragmatisch – auf die praktische -Umsetzung einer funktionsfähigen Lösung fokussiert. Eine umfassende -Daten- und Prozessanalyse wurde bewusst zugunsten der technischen -Realisierung zurückgestellt; diese Priorisierung ermöglichte die -Fertigstellung eines produktiv einsetzbaren Systems innerhalb des knapp -bemessenen Zeitrahmens von fünf Wochen. +Der Projektumfang wurde pragmatisch auf die Umsetzung einer +funktionsfähigen Lösung fokussiert. Eine umfassende Daten- und +Prozessanalyse wurde zugunsten der technischen Realisierung +zurückgestellt, um ein produktiv einsetzbares System innerhalb des +Zeitrahmens von fünf Wochen fertigzustellen. Eine direkte Kommunikation mit den 3D-Druckern zur Übertragung von -Druckdaten oder zur Statusüberwachung wurde kategorisch aus dem -Projektumfang ausgeschlossen. Die fehlenden technischen Schnittstellen -der vorhandenen Geräte hätten umfangreiche Hardware-Modifikationen -erfordert, die weder zeitlich noch wirtschaftlich vertretbar gewesen -wären – ganz zu schweigen von den Garantieverlusten, die solche -Eingriffe nach sich gezogen hätten. +Druckdaten oder zur Statusüberwachung wurde aus dem Projektumfang +ausgeschlossen. Die fehlenden Schnittstellen der Geräte hätten +umfangreiche Hardware-Modifikationen erfordert, die weder zeitlich noch +wirtschaftlich vertretbar gewesen wären. -Ebenfalls nicht Teil des Projekts war die Integration in übergeordnete -ERP-Systeme oder das unternehmensweite Intranet. Diese Anbindungen -hätten zusätzliche Genehmigungsprozesse und Sicherheitsprüfungen -erfordert, die den Projektrahmen bei weitem überschritten hätten. -Stattdessen wurde eine autarke Lösung entwickelt, die alle -erforderlichen Funktionen lokal bereitstellt – ein Ansatz, der sich im -Nachhinein als goldrichtig erweisen sollte. +Die Integration in übergeordnete ERP-Systeme oder das unternehmensweite +Intranet war ebenfalls nicht Teil des Projekts. Diese Anbindungen hätten +zusätzliche Genehmigungsprozesse erfordert, die den Projektrahmen +überschritten hätten. Stattdessen wurde eine autarke Lösung entwickelt, +die alle erforderlichen Funktionen lokal bereitstellt. ## 1.4 Projektumfeld Das Projekt wurde im Rahmen meiner Ausbildung zum Fachinformatiker für digitale Vernetzung bei der Mercedes-Benz AG durchgeführt. Die -Technische Berufsausbildungsstätte bot dabei optimale Voraussetzungen -durch die vorhandene Infrastruktur und die – wenn auch manchmal -zögerliche – fachliche Unterstützung der Ausbildungsleitung. +Technische Berufsausbildungsstätte bot dabei die vorhandene +Infrastruktur und fachliche Unterstützung durch die Ausbildungsleitung. -Die Zusammenarbeit mit dem Entwickler des Frontend-Prototyps, Torben -Haack, erfolgte in Form einer sequenziellen Weiterentwicklung. Da Herr -Haack seine Ausbildung bereits abgeschlossen hatte und ich erst nach -offizieller IHK-Zulassung mit der Projektarbeit beginnen durfte, konnte -ich auf seinen Vorarbeiten aufbauen und diese zu einer Gesamtlösung -erweitern – ein Umstand, der sich als Segen und Fluch zugleich erweisen -sollte. +Die Zusammenarbeit mit Torben Haack erfolgte in Form einer sequenziellen +Weiterentwicklung. Da Herr Haack seine Ausbildung bereits abgeschlossen +hatte und ich erst nach IHK-Zulassung mit der Projektarbeit beginnen +durfte, konnte ich auf seinen Frontend-Prototyp aufbauen und diesen zu +einer Gesamtlösung erweitern. -Die organisatorischen Rahmenbedingungen wurden maßgeblich durch die +Die organisatorischen Rahmenbedingungen wurden durch die Sicherheitsrichtlinien und IT-Governance der Mercedes-Benz AG geprägt. Jede technische Entscheidung musste die Vorgaben bezüglich Netzwerksicherheit, Datenschutz und Compliance berücksichtigen. Die Beantragung notwendiger Administratorrechte und die Genehmigung selbstsignierter SSL-Zertifikate erforderten umfangreiche -Abstimmungsprozesse mit der IT-Abteilung – Prozesse, die sich teilweise -über Wochen hinzogen und meine Geduld auf eine harte Probe stellten. +Abstimmungsprozesse mit der IT-Abteilung. ## 1.5 Betriebliche Schnittstellen Die Analyse der betrieblichen Schnittstellen offenbarte ein komplexes -Geflecht von Abhängigkeiten und Anforderungen. Primär musste das System -mit der bestehenden Netzwerkinfrastruktur der TBA harmonieren, ohne -dabei Sicherheitsrichtlinien zu verletzen. Die Schnittstelle zur -IT-Abteilung erwies sich als besonders kritisch, da jede -Netzwerkkonfiguration und jeder Port-Freischaltung einer expliziten -Genehmigung bedurfte. +Geflecht von Abhängigkeiten. Das System musste mit der bestehenden +Netzwerkinfrastruktur der TBA harmonieren, ohne Sicherheitsrichtlinien +zu verletzen. Die Schnittstelle zur IT-Abteilung erwies sich als +kritisch, da jede Netzwerkkonfiguration und Port-Freischaltung einer +expliziten Genehmigung bedurfte. -Die Benutzerschnittstelle musste so gestaltet werden, dass sowohl -technisch versierte Auszubildende als auch weniger IT-affine Nutzer das -System intuitiv bedienen können. Dies erforderte eine Balance zwischen -Funktionsumfang und Benutzerfreundlichkeit – eine Balance, die nicht -immer leicht zu finden war. +Die Benutzerschnittstelle wurde so gestaltet, dass sowohl technisch +versierte Auszubildende als auch weniger IT-affine Nutzer das System +intuitiv bedienen können. Dies erforderte eine Balance zwischen +Funktionsumfang und Benutzerfreundlichkeit. -Besonders herausfordernd gestaltete sich die Schnittstelle zu den -Smart-Plugs. Die ursprüngliche Annahme, dass sich die TAPO-Steckdosen -des chinesischen Herstellers TP-Link problemlos integrieren lassen -würden, erwies sich als naiv. Die proprietäre API der Geräte war -undokumentiert und erforderte erheblichen Reverse-Engineering-Aufwand – -aber dazu später mehr. +Die Schnittstelle zu den Smart-Plugs stellte eine besondere +Herausforderung dar. Die TAPO P110-Steckdosen von TP-Link verfügten über +eine proprietäre, undokumentierte API, die erheblichen +Reverse-Engineering-Aufwand erforderte. ## 1.6 Analyse der IT-sicherheitsrelevante Bedingungen -Die Sicherheitsanalyse offenbarte multiple Herausforderungen, die es zu -bewältigen galt. Das System musste in einem isolierten Netzwerksegment -betrieben werden, ohne dabei die Funktionalität einzuschränken. Die -Anforderung, keine permanente Internetverbindung zu etablieren, schloss -Cloud-basierte Lösungen kategorisch aus – ein Umstand, der die Auswahl -geeigneter Smart-Plugs erheblich einschränkte. +Die Sicherheitsanalyse offenbarte multiple Herausforderungen. Das System +musste in einem isolierten Netzwerksegment betrieben werden, ohne die +Funktionalität einzuschränken. Die Anforderung, keine permanente +Internetverbindung zu etablieren, schloss Cloud-basierte Lösungen aus +und schränkte die Auswahl geeigneter Smart-Plugs erheblich ein. -Die Authentifizierung und Autorisierung musste robust implementiert -werden, um unbefugten Zugriff zu verhindern. Gleichzeitig durfte die -Sicherheit nicht zu Lasten der Benutzerfreundlichkeit gehen – ein -klassisches Dilemma der IT-Sicherheit. Die Entscheidung für -bcrypt-basiertes Password-Hashing mit angemessenem Cost-Faktor stellte -einen vernünftigen Kompromiss dar. +Die Authentifizierung und Autorisierung wurde robust implementiert, ohne +die Benutzerfreundlichkeit zu beeinträchtigen. Die Entscheidung für +bcrypt-basiertes Password-Hashing mit einem Cost-Faktor von 12 stellte +einen vernünftigen Kompromiss zwischen Sicherheit und Performance dar. Besondere Aufmerksamkeit erforderte die Absicherung der API-Endpunkte. -Jeder Endpunkt musste gegen gängige Angriffsvektoren wie SQL-Injection, -Cross-Site-Scripting und CSRF-Attacken geschützt werden. Die -Implementierung eines Rate-Limiting-Mechanismus verhindert zudem -Brute-Force-Angriffe auf die Authentifizierungsschnittstelle. +Jeder Endpunkt wurde gegen gängige Angriffsvektoren wie SQL-Injection, +Cross-Site-Scripting und CSRF-Attacken geschützt. Die Implementierung +eines Rate-Limiting-Mechanismus verhindert Brute-Force-Angriffe auf die +Authentifizierungsschnittstelle. ## 1.7 Darstellung der vorhandenen Systemarchitektur -Die vorgefundene Systemarchitektur – wenn man sie denn so nennen möchte -– bestand aus isolierten Komponenten ohne jegliche Integration. Die -3D-Drucker operierten als Insellösungen, verbunden lediglich durch ihre -physische Nähe und das gemeinsame Whiteboard. Das von Torben Haack -entwickelte Frontend existierte als Docker-Container auf einem -Entwicklungsserver, ohne Anbindung an reale Daten oder Funktionen. +Die vorgefundene Systemarchitektur bestand aus isolierten Komponenten +ohne Integration. Die 3D-Drucker operierten als Insellösungen, verbunden +nur durch ihre physische Nähe und das gemeinsame Whiteboard. Der +Frontend-Prototyp von Torben Haack existierte als Docker-Container auf +einem Entwicklungsserver, ohne Anbindung an reale Funktionen. Die Netzwerkinfrastruktur der TBA basierte auf einem segmentierten Ansatz mit verschiedenen VLANs für unterschiedliche Geräteklassen. Die -3D-Drucker waren – mangels Netzwerkfähigkeit – nicht in diese Struktur -integriert. Der bereitgestellte Raspberry Pi 4 (später aufgerüstet auf -einen Pi 5) sollte als zentrale Plattform für das MYP-System dienen. +3D-Drucker waren mangels Netzwerkfähigkeit nicht in diese Struktur +integriert. Ein Raspberry Pi 5 mit 8 GB RAM und 128 GB Speicher sollte +als zentrale Plattform für das MYP-System dienen. Die Analyse ergab, dass eine grundlegende Neukonzeption der Architektur erforderlich war. Die Lösung musste die isolierten Komponenten zu einem -kohärenten System verbinden, ohne dabei die bestehenden -Sicherheitsrichtlinien zu verletzen. Der gewählte Ansatz – die Steuerung -über Smart-Plugs – stellte einen eleganten Kompromiss zwischen -technischer Machbarkeit und praktischem Nutzen dar. +kohärenten System verbinden, ohne die bestehenden Sicherheitsrichtlinien +zu verletzen. Der gewählte Ansatz über Smart-Plugs stellte einen +eleganten Kompromiss zwischen technischer Machbarkeit und praktischem +Nutzen dar. # 2. Projektplanung ## 2.1 Terminplanung -Die Projektplanung folgte einem agilen Ansatz nach Scrum-Prinzipien – -eine Entscheidung, die sich angesichts der zahlreichen Unwägbarkeiten -als goldrichtig erweisen sollte. Die Gesamtprojektdauer von fünf Wochen -(15. April bis 20. Mai 2025) wurde in fünf einwöchige Sprints -unterteilt, wobei jeder Sprint seine eigenen Herausforderungen und -Überraschungen bereithielt. +Die Projektplanung folgte einem agilen Ansatz nach Scrum-Prinzipien. Die +Gesamtprojektdauer von fünf Wochen (15. April bis 20. Mai 2025) wurde in +fünf einwöchige Sprints unterteilt, wobei jeder Sprint spezifische Ziele +verfolgte. ### Sprint 1 (15.-19. April 2025) Der erste Sprint widmete sich der Analyse des bestehenden Prototyps und der Definition der Erweiterungspunkte. Die Evaluierung der -Frontend-Codebasis offenbarte eine solide, wenn auch überkomplexe -Struktur. Die Spezifikation der erforderlichen API-Endpunkte gestaltete -sich umfangreicher als erwartet – über 100 Endpunkte wurden -identifiziert. Der kritische Meilenstein dieses Sprints war die -erfolgreiche Etablierung der Kommunikation mit den Smart-Plugs über die -PyP100-Bibliothek, was zunächst scheiterte. +Frontend-Codebasis offenbarte eine solide Struktur. Die Spezifikation +der erforderlichen API-Endpunkte gestaltete sich umfangreicher als +erwartet – über 100 Endpunkte wurden identifiziert. Der kritische +Meilenstein war die Etablierung der Kommunikation mit den Smart-Plugs, +was zunächst mit der PyP100-Bibliothek scheiterte. ### Sprint 2 (22.-26. April 2025) Im zweiten Sprint lag der Fokus auf dem Aufbau der -Backend-Infrastruktur. Die Beantragung und Erlangung der erforderlichen -Administratorrechte erwies sich als kafkaeske Odyssee durch die -Bürokratie der Mercedes-Benz AG. Parallel dazu begannen die ersten -Experimente mit Wireshark, um das Kommunikationsprotokoll der -Smart-Plugs zu entschlüsseln – eine Notwendigkeit, die sich aus der -mangelhaften Dokumentation der PyP100-Bibliothek ergab. +Backend-Infrastruktur. Die Beantragung der erforderlichen +Administratorrechte erwies sich als zeitaufwändig. Parallel begannen die +ersten Experimente mit Wireshark, um das Kommunikationsprotokoll der +Smart-Plugs zu entschlüsseln, da die PyP100-Bibliothek nicht +funktionierte. ### Sprint 3 (29. April - 3. Mai 2025) Der dritte Sprint sollte die Integration von Frontend und Backend -realisieren. Stattdessen wurde er zu einer Woche der technischen -Katastrophen: Die Verbindung zwischen den Komponenten scheiterte +realisieren. Die Verbindung zwischen den Komponenten scheiterte wiederholt, die genehmigten SSL-Zertifikate gingen durch einen -unglücklichen Neuinstallationsprozess verloren, und die Einarbeitung in -die unternehmensspezifischen Implementierungen von GitHub OAuth und npm -verschlang wertvolle Zeit. +Neuinstallationsprozess verloren, und die Einarbeitung in die +unternehmensspezifischen Implementierungen von GitHub OAuth und npm +erforderte zusätzliche Zeit. ### Sprint 4 (6.-10. Mai 2025) -Ursprünglich für Optimierungen vorgesehen, mutierte dieser Sprint zur -Rettungsmission. Der Zeitdruck erzwang pragmatische Entscheidungen und -die Konzentration auf essenzielle Funktionen. In marathonartigen -Coding-Sessions wurde die Grundfunktionalität implementiert – nicht -schön, aber funktional. +Ursprünglich für Optimierungen vorgesehen, wurde dieser Sprint zur +Entwicklung einer Full-Stack-Notlösung umfunktioniert. Der Zeitdruck +erzwang pragmatische Entscheidungen. In intensiven Coding-Sessions wurde +die Grundfunktionalität implementiert. ### Sprint 5 (13.-17. Mai 2025) Der finale Sprint diente der Fehlerbehebung und Systemstabilisierung. -Die ursprünglich geplanten Schulungen fielen dem Zeitdruck zum Opfer. -Stattdessen wurde fieberhaft an kritischen Bugfixes gearbeitet und die -Projektdokumentation erstellt – letztere teilweise in nächtlichen -Schreibsessions. +Die geplanten Schulungen wurden auf die Nach-Projektphase verschoben. +Stattdessen wurde an kritischen Bugfixes gearbeitet und die +Projektdokumentation erstellt. ## 2.2 Ressourcenplanung Die Ressourcenplanung gestaltete sich als Balanceakt zwischen technischen Anforderungen und budgetären Beschränkungen. Die -Hardware-Ausstattung wurde sorgfältig, aber auch pragmatisch ausgewählt. +Hardware-Ausstattung wurde sorgfältig ausgewählt. -Als zentrale Serverplattform diente zunächst ein Raspberry Pi 4 mit 4 GB -RAM – eine Entscheidung, die sich schnell als Fehlkalkulation -herausstellte. Performance-Tests zeigten, dass das ressourcenhungrige -Next.js-Frontend die kleine Platine an ihre Grenzen brachte. Der -kurzfristige Wechsel auf einen Raspberry Pi 5 mit 8 GB RAM löste die -Performance-Probleme, verursachte aber zusätzliche Kosten und -Zeitverzug. +Als zentrale Serverplattform diente ein Raspberry Pi 5 mit 8 GB RAM und +128 GB Speicher. Der ursprünglich geplante Raspberry Pi 4 erwies sich +als zu leistungsschwach für das ressourcenintensive Next.js-Frontend. Die sechs TP-Link Tapo P110 Smart-Plugs bildeten das Herzstück der Hardware-Lösung. Jedes Gerät erhielt eine statische IP-Adresse im -Bereich 192.168.0.151 bis 192.168.0.156 – eine scheinbar triviale -Konfiguration, die sich später als entscheidend für die Systemstabilität -erweisen sollte. Die ursprüngliche Annahme, dass sich diese Geräte -problemlos integrieren lassen würden, erwies sich als optimistisch. Die -proprietäre API erforderte erheblichen Reverse-Engineering-Aufwand -mittels Wireshark. +Bereich 192.168.0.100 bis 192.168.0.106. Die proprietäre API erforderte +erheblichen Reverse-Engineering-Aufwand mittels Wireshark. Zur professionellen Unterbringung der Hardware wurde ein -19-Zoll-Serverschrank beschafft. Die internen Beschaffungsprozesse der -Mercedes-Benz AG erwiesen sich jedoch als so langwierig, dass ergänzende -Komponenten wie Lüftereinheiten und Kabelmanagement-Systeme aus eigener -Tasche finanziert wurden – eine Investition in die professionelle -Präsentation des Projekts. +19-Zoll-Serverschrank beschafft. Ergänzende Komponenten wie +Lüftereinheiten und Kabelmanagement-Systeme wurden aus privaten Mitteln +finanziert, um eine professionelle Präsentation zu gewährleisten. -Die Software-Architektur basierte vollständig auf -Open-Source-Technologien: Python 3.11 als Programmiersprache, Flask 2.3 -als Web-Framework, SQLAlchemy 2.0 für die Datenbankabstraktion und -SQLite als Datenbanksystem. Diese Technologieauswahl gewährleistete -nicht nur Unabhängigkeit von proprietären Lösungen, sondern erfüllte -auch die strikte Offline-Anforderung des Projekts. +Die Software-Architektur basierte auf Open-Source-Technologien: Python +3.11, Flask 2.3, SQLAlchemy 2.0 und SQLite. Diese Technologieauswahl +gewährleistete Unabhängigkeit von proprietären Lösungen und erfüllte die +Offline-Anforderung des Projekts. ## 2.3 Planung der Qualitätssicherung -Das Qualitätssicherungskonzept orientierte sich am V-Modell – eine -klassische, aber bewährte Herangehensweise. Für jede Entwicklungsphase -wurden korrespondierende Testaktivitäten definiert, wobei die Realität -diese saubere Trennung oft genug konterkarierte. +Das Qualitätssicherungskonzept orientierte sich am V-Modell. Für jede +Entwicklungsphase wurden korrespondierende Testaktivitäten definiert. Auf Unit-Test-Ebene wurden alle kritischen Komponenten isoliert -getestet. Die Datenbankoperationen, API-Eingabevalidierung und +getestet. Datenbankoperationen, API-Eingabevalidierung und Kernfunktionen der Reservierungsverwaltung durchliefen umfangreiche Testszenarien. Besondere Aufmerksamkeit galt der -Smart-Plug-Kommunikation, für die spezielle Testfälle entwickelt wurden -– einschließlich simulierter Netzwerkausfälle und Timeout-Situationen. +Smart-Plug-Kommunikation mit simulierten Netzwerkausfällen und +Timeout-Situationen. -Die Integrationstests gestalteten sich komplexer als erwartet. Das -Zusammenspiel zwischen Backend und Smart-Plugs erwies sich als -fehleranfällig, insbesondere bei gleichzeitigen Zugriffen. Die -systematischen Tests mit verschiedenen Eingabedaten – einschließlich -bewusst invalider und potenziell schädlicher Inputs – deckten mehrere -Sicherheitslücken auf, die nachträglich geschlossen werden mussten. +Die Integrationstests gestalteten sich komplex. Das Zusammenspiel +zwischen Backend und Smart-Plugs erwies sich als fehleranfällig bei +gleichzeitigen Zugriffen. Systematische Tests mit verschiedenen +Eingabedaten deckten mehrere Sicherheitslücken auf, die nachträglich +geschlossen wurden. Systemtests bildeten komplette Anwendungsszenarien ab. Ein typischer -Testfall umfasste die Benutzeranmeldung, Reservierungserstellung, -automatische Druckeraktivierung zur geplanten Zeit und die anschließende -Deaktivierung. Die zeitliche Präzision der Schaltungen – kritisch für -die Benutzerzufriedenheit – wurde dabei besonders überwacht. +Testfall umfasste Benutzeranmeldung, Reservierungserstellung, +automatische Druckeraktivierung und anschließende Deaktivierung. Die +zeitliche Präzision der Schaltungen wurde dabei besonders überwacht. -Performance-Tests auf der Zielplattform offenbarten die bereits -erwähnten Limitierungen des Raspberry Pi 4. Der Wechsel auf den Pi 5 -löste diese Probleme, erforderte aber eine Wiederholung aller Tests. Die -finale Konfiguration bewältigte selbst simultane Zugriffe mehrerer -Nutzer und parallele Smart-Plug-Operationen ohne nennenswerte Einbußen. +Performance-Tests offenbarten die Limitierungen des ursprünglich +geplanten Raspberry Pi 4. Der Wechsel auf den Pi 5 löste diese Probleme. +Die finale Konfiguration bewältigte simultane Zugriffe mehrerer Nutzer +und parallele Smart-Plug-Operationen ohne Einbußen. ## 2.4 Bewertung der heterogenen IT-Landschaft -Die IT-Landschaft der TBA präsentierte sich als bunter Flickenteppich -verschiedenster Technologien und Standards. Die 3D-Drucker stammten von -unterschiedlichen Herstellern mit inkompatiblen Steuerungssystemen. Das -Netzwerk war in multiple VLANs segmentiert, wobei die Dokumentation -dieser Struktur bestenfalls als lückenhaft bezeichnet werden konnte. +Die IT-Landschaft der TBA präsentierte sich als heterogene Umgebung mit +verschiedenen Technologien. Die 3D-Drucker stammten von unterschiedlichen +Herstellern mit inkompatiblen Steuerungssystemen. Das Netzwerk war in +multiple VLANs segmentiert. Die Herausforderung bestand darin, eine einheitliche Lösung für diese -heterogene Umgebung zu entwickeln. Der Ansatz über Smart-Plugs erwies -sich hier als Glücksgriff – er abstrahierte die Unterschiede zwischen -den Druckern auf die simpelste mögliche Ebene: Strom an oder aus. Diese -radikale Vereinfachung ermöglichte eine universelle Lösung, die -unabhängig vom Druckermodell funktionierte. +heterogene Umgebung zu entwickeln. Der Ansatz über Smart-Plugs +abstrahierte die Unterschiede zwischen den Druckern auf die einfachste +Ebene: Stromversorgung. Diese Vereinfachung ermöglichte eine universelle +Lösung, unabhängig vom Druckermodell. Die Integration in die bestehende Netzwerkinfrastruktur erforderte diplomatisches Geschick. Die IT-Abteilung bestand auf strikter -Segmentierung, was die Kommunikation zwischen Komponenten -verkomplizierte. Die Lösung – ein dediziertes IoT-Subnetz für das +Segmentierung. Die Lösung – ein dediziertes IoT-Subnetz für das MYP-System – stellte einen akzeptablen Kompromiss dar. ## 2.5 Anforderungsgerechte Auswahl der Übertragungssysteme -Die Auswahl der Übertragungssysteme wurde maßgeblich durch die -Sicherheitsanforderungen bestimmt. Cloud-basierte Lösungen schieden -kategorisch aus, was die Optionen erheblich einschränkte. Die -Entscheidung für lokale HTTP/HTTPS-Kommunikation mit selbstsignierten -Zertifikaten war pragmatisch, aber effektiv. +Die Auswahl der Übertragungssysteme wurde durch die +Sicherheitsanforderungen bestimmt. Cloud-basierte Lösungen schieden aus. +Die Entscheidung für lokale HTTP/HTTPS-Kommunikation mit +selbstsignierten Zertifikaten war pragmatisch und effektiv. Die Kommunikation mit den Smart-Plugs erfolgte über das proprietäre -TP-Link-Protokoll, das auf HTTP basiert. Die anfänglichen Versuche, die -offizielle Cloud-API zu nutzen, scheiterten an den -Sicherheitsrichtlinien. Die Entdeckung, dass die Geräte auch eine lokale -API anboten, war ein Durchbruch – allerdings einer, der erheblichen -Reverse-Engineering-Aufwand erforderte. +TP-Link-Protokoll. Die anfänglichen Versuche mit der offiziellen +Cloud-API scheiterten an den Sicherheitsrichtlinien. Die lokale API der +Geräte erforderte erheblichen Reverse-Engineering-Aufwand. -Hier kam Wireshark ins Spiel. Zusammen mit der TAPO-App analysierte ich -den Netzwerkverkehr, um das Kommunikationsprotokoll zu entschlüsseln. -Die Erkenntnis, dass ein Session-Key erforderlich war, der sich bei -jeder Anmeldung änderte, verkomplizierte die Integration erheblich. -Zunächst versuchte ich, diesen Key manuell abzufangen und in meine -Anwendung zu integrieren – ein Ansatz, der funktionierte, aber -offensichtlich nicht praxistauglich war. - -Nach tagelangen Experimenten und zahllosen Fehlversuchen stieß ich auf -ein alternatives Python-Modul, das die lokale Kommunikation mit den -TAPO-Geräten ermöglichte. Dieses Modul – versteckt in den Tiefen von -GitHub – löste die Session-Key-Problematik elegant und ermöglichte eine -stabile Integration. +Mit Wireshark analysierte ich den Netzwerkverkehr der TAPO-App. Die +Erkenntnis, dass ein Session-Key erforderlich war, der sich bei jeder +Anmeldung änderte, verkomplizierte die Integration. Nach tagelangen +Experimenten fand ich ein alternatives Python-Modul auf GitHub, das die +lokale Kommunikation ermöglichte und die Session-Key-Problematik elegant +löste. ## 2.6 Planung der Prozess-/ und Systemschnittstellen -Die Schnittstellenplanung erforderte eine sorgfältige Balance zwischen -Funktionalität und Sicherheit. Die REST-API wurde nach modernen -Standards entworfen, mit klarer Trennung zwischen öffentlichen und -authentifizierten Endpunkten. Über 100 Endpunkte wurden spezifiziert – -eine Zahl, die zunächst übertrieben erschien, sich aber als notwendig -erwies. +Die Schnittstellenplanung erforderte eine Balance zwischen Funktionalität +und Sicherheit. Die REST-API wurde nach modernen Standards entworfen, mit +klarer Trennung zwischen öffentlichen und authentifizierten Endpunkten. +Über 100 Endpunkte wurden spezifiziert und implementiert. Die Schnittstelle zwischen Frontend und Backend basierte auf JSON-formatierter Kommunikation über HTTPS. Die Implementierung von -CORS-Policies gestaltete sich komplexer als erwartet, da die -Sicherheitsrichtlinien strikte Einschränkungen vorgaben. Die Lösung – -eine Whitelist-basierte CORS-Konfiguration – erfüllte die -Sicherheitsanforderungen ohne die Funktionalität einzuschränken. +CORS-Policies gestaltete sich komplex aufgrund der strikten +Sicherheitsrichtlinien. Eine Whitelist-basierte CORS-Konfiguration +erfüllte die Anforderungen ohne Funktionseinschränkungen. -Besondere Aufmerksamkeit erforderte die Scheduler-Schnittstelle. Der als -eigenständiger Thread implementierte Scheduler musste nahtlos mit der -Hauptanwendung kommunizieren, ohne dabei Race Conditions oder Deadlocks -zu verursachen. Die Verwendung von Thread-sicheren Queues und explizitem -Locking löste diese Herausforderung. +Der als eigenständiger Thread implementierte Scheduler musste nahtlos mit +der Hauptanwendung kommunizieren. Die Verwendung von thread-sicheren +Queues und explizitem Locking verhinderte Race Conditions und Deadlocks. ## 2.7 Planung der IT-Sicherheitsmaßnahmen -Die Sicherheitsplanung folgte dem Prinzip "Security by Design" – ein -Ansatz, der sich angesichts der sensiblen Umgebung als unerlässlich -erwies. Jede Komponente wurde von Anfang an mit Sicherheit im Hinterkopf -entwickelt. +Die Sicherheitsplanung folgte dem Prinzip "Security by Design". Jede +Komponente wurde von Anfang an mit Sicherheit im Fokus entwickelt. -Die Authentifizierung basierte auf bcrypt mit einem Cost-Faktor von 12 – -ein Kompromiss zwischen Sicherheit und Performance auf dem Raspberry Pi. +Die Authentifizierung basierte auf bcrypt mit einem Cost-Faktor von 12. Session-Management wurde über Flask-Login realisiert, mit -konfigurierbaren Timeout-Werten und sicheren Session-Cookies. Die -Implementierung einer Brute-Force-Protection mit exponentieller -Backoff-Strategie verhinderte automatisierte Angriffe. +konfigurierbaren Timeout-Werten und sicheren Session-Cookies. Eine +Brute-Force-Protection mit exponentieller Backoff-Strategie verhinderte +automatisierte Angriffe. Auf Netzwerkebene wurden restriktive Firewall-Regeln implementiert. Nur essenzielle Ports wurden geöffnet, ausgehende Verbindungen auf die @@ -560,272 +475,209 @@ ermöglichte granulare Kontrolle über den Netzwerkverkehr. Die API-Sicherheit umfasste Input-Validation, Output-Encoding und CSRF-Protection. Jeder Endpunkt wurde gegen die OWASP Top 10 abgesichert. Ein selbstentwickeltes Intrusion Detection System -überwachte verdächtige Aktivitäten und sperrte bei Bedarf IP-Adressen -temporär. +überwachte verdächtige Aktivitäten. # 3. Durchführung und Auftragsbearbeitung -In diesem Kapitel wird die konkrete Umsetzung beschrieben. - ## 3.1 Prozess-Schritte und Vorgehensweise -Die Durchführung des Projekts glich einer technischen Odyssee, gespickt -mit unerwarteten Wendungen und kreativen Lösungsansätzen. Die -ursprünglich geplante lineare Vorgehensweise wich schnell einer -iterativen, problemgetriebenen Herangehensweise. +Die Durchführung des Projekts erforderte kontinuierliche Anpassungen an +technische Herausforderungen. Die ursprünglich geplante lineare +Vorgehensweise wich einer iterativen, problemgetriebenen +Herangehensweise. ### 3.1.1 Datenabfrage der Sensoren -Die "Sensoren" in diesem Kontext waren die Smart-Plugs – eine -euphemistische Bezeichnung für Geräte, die sich als erstaunlich -widerspenstig erwiesen. Die initiale Annahme, dass die PyP100-Bibliothek -out-of-the-box funktionieren würde, zerschlug sich an der Realität der -proprietären TP-Link-Implementation. +Die Smart-Plugs fungierten als "Sensoren" im System. Die initiale +Annahme, dass die PyP100-Bibliothek funktionieren würde, erwies sich als +falsch. -Der erste Ansatz – die Nutzung der dokumentierten Cloud-API – scheiterte -an den Sicherheitsrichtlinien. Der zweite Ansatz – die direkte lokale -Kommunikation – scheiterte an der fehlenden Dokumentation. Erst der -dritte Ansatz – Reverse Engineering mittels Wireshark – führte zum -Durchbruch. +Der erste Ansatz mit der dokumentierten Cloud-API scheiterte an den +Sicherheitsrichtlinien. Der zweite Ansatz der direkten lokalen +Kommunikation scheiterte an fehlender Dokumentation. Erst das Reverse +Engineering mittels Wireshark führte zum Erfolg. Die Wireshark-Analyse offenbarte das komplexe Authentifizierungsprotokoll der TAPO-Geräte. Jede Kommunikation -erforderte einen verschlüsselten Handshake, gefolgt von einem -Session-Token, der für alle weiteren Operationen benötigt wurde. Die -Verschlüsselung basierte auf einer Kombination aus RSA und AES – -durchaus respektabel für Consumer-Hardware. +erforderte einen verschlüsselten Handshake mit Session-Token. Die +Verschlüsselung basierte auf RSA und AES. -Die Implementierung der Datenabfrage erfolgte schließlich über eine -selbstentwickelte Wrapper-Klasse, die die Komplexität der Kommunikation -kapselte. Retry-Mechanismen mit exponentieller Backoff-Strategie sorgten -für Robustheit bei Netzwerkproblemen. Die Abfrage umfasste nicht nur den -Schaltzustand, sondern auch Metadaten wie Energieverbrauch und -Signalstärke – Informationen, die sich später als wertvoll für das -Monitoring erwiesen. +Die finale Implementierung erfolgte über eine selbstentwickelte +Wrapper-Klasse, die die Kommunikationskomplexität kapselte. +Retry-Mechanismen mit exponentieller Backoff-Strategie sorgten für +Robustheit. Die Abfrage umfasste Schaltzustand, Energieverbrauch und +Signalstärke. ### 3.1.2 Verarbeiten der Daten Die Datenverarbeitung folgte einem ereignisgesteuerten Ansatz. Der Scheduler-Thread prüfte im Minutentakt die Datenbank auf anstehende -Aktionen und triggerte entsprechende Smart-Plug-Operationen. Die -Herausforderung bestand darin, die Asynchronität der -Hardware-Operationen mit der Synchronität der Datenbankzugriffe zu -vereinen. +Aktionen und triggerte Smart-Plug-Operationen. Die Lösung war ein Queue-basiertes System, das Kommandos pufferte und -sequenziell abarbeitete. Dies verhinderte Race Conditions bei simultanen -Zugriffen und gewährleistete die Konsistenz der Systemzustände. Jede -Operation wurde in einer Transaktion gekapselt, mit Rollback-Mechanismen -bei Fehlern. +sequenziell abarbeitete. Dies verhinderte Race Conditions und +gewährleistete Systemkonsistenz. Jede Operation wurde in einer +Transaktion gekapselt mit Rollback-Mechanismen. -Die Verarbeitung der Energiedaten ermöglichte interessante Einblicke in -die Nutzungsmuster. Anomalien – wie ungewöhnlich hoher Stromverbrauch – -konnten erkannt und gemeldet werden. Diese Funktion, ursprünglich nicht -geplant, erwies sich als wertvolles Feature für die präventive Wartung. +Die Verarbeitung der Energiedaten ermöglichte Einblicke in +Nutzungsmuster. Anomalien wie ungewöhnlich hoher Stromverbrauch konnten +erkannt werden. Diese ursprünglich nicht geplante Funktion erwies sich +als wertvoll für die präventive Wartung. ## 3.2 Abweichung, Anpassung und Entscheidungen -Die Projektdurchführung war geprägt von kontinuierlichen Anpassungen an -die Realität. Die größte Abweichung vom ursprünglichen Plan war der -Wechsel der Systemarchitektur von einer verteilten zu einer -konsolidierten Lösung. +Die Projektdurchführung war geprägt von kontinuierlichen Anpassungen. Die +größte Abweichung war der Wechsel von einer verteilten zu einer +konsolidierten Systemarchitektur. -Ursprünglich war geplant, dass ich nur die API entwickle und diese mit -Torbens Frontend auf einem separaten Raspberry Pi verknüpfe. Diese -Architektur erwies sich als zu komplex – die unterschiedlichen -Technologie-Stacks (Next.js vs. Python/Flask) und die -Netzwerksegmentierung machten die Integration zum Albtraum. Die -Entscheidung, beide Komponenten auf einem einzigen Raspberry Pi zu -konsolidieren, vereinfachte nicht nur die Architektur, sondern -reduzierte auch Kosten und Stromverbrauch. +Ursprünglich sollte nur die API entwickelt und mit dem Frontend auf +separaten Raspberry Pis verknüpft werden. Die unterschiedlichen +Technologie-Stacks und die Netzwerksegmentierung machten die Integration +jedoch zu komplex. Die Konsolidierung auf einem einzigen Raspberry Pi +vereinfachte die Architektur erheblich. Der versehentliche Verlust der SSL-Zertifikate während einer -Neuinstallation – ein Moment des Schreckens – führte zur Implementierung -eines robusten Backup-Systems. Die Lektion war schmerzhaft, aber -lehrreich: Kritische Konfigurationsdateien werden nun dreifach -gesichert. +Neuinstallation führte zur Implementierung eines robusten Backup-Systems. +Kritische Konfigurationsdateien werden nun dreifach gesichert. -Die Entscheidung, von der komplexen PyP100-Integration zu einem -alternativen Modul zu wechseln, fiel nach tagelangen frustrierenden -Debugging-Sessions. Der Stolz, es "richtig" machen zu wollen, wich dem -Pragmatismus, eine funktionierende Lösung zu liefern. Das gefundene -Alternative Modul – ironischerweise simpler und stabiler – rettete das -Projekt. +Die Entscheidung, von PyP100 zu einem alternativen Modul zu wechseln, +fiel nach tagelangen fruchtlosen Debugging-Sessions. Das gefundene +alternative Modul war simpler und stabiler. ## 3.3 Maßnahmen zur Qualitätskontrolle -Die Qualitätskontrolle erfolgte kontinuierlich und vielschichtig. -Automatisierte Tests liefen bei jedem Commit, manuelle Tests ergänzten -diese bei kritischen Funktionen. Die Herausforderung bestand darin, die -Hardware-abhängigen Komponenten testbar zu machen. +Die Qualitätskontrolle erfolgte kontinuierlich. Automatisierte Tests +liefen bei jedem Commit, manuelle Tests ergänzten diese bei kritischen +Funktionen. Mock-Objekte simulierten die Smart-Plugs für Unit-Tests. Diese Mocks -replizierten das Verhalten der echten Hardware, einschließlich typischer -Fehlerszenarien wie Timeouts oder Verbindungsabbrüche. Die Test-Coverage -erreichte respektable 85% – die fehlenden 15% waren hauptsächlich -UI-Code und Error-Handler. +replizierten das Verhalten der echten Hardware inklusive typischer +Fehlerszenarien. Die Test-Coverage erreichte 85%. Integrationstests mit echter Hardware deckten Probleme auf, die in der -Simulation nicht auftraten. Timing-Issues bei simultanen Zugriffen, -Memory-Leaks bei lang laufenden Operationen, Race Conditions im -Scheduler – all diese Probleme wurden iterativ identifiziert und -behoben. +Simulation nicht auftraten: Timing-Issues, Memory-Leaks, Race +Conditions. Alle Probleme wurden iterativ behoben. -Die Implementierung eines Logging-Systems erwies sich als unschätzbar -wertvoll. Jede Operation, jeder Fehler, jede Anomalie wurde -protokolliert. Die Log-Analyse wurde zum wichtigsten Debugging-Tool, -insbesondere bei sporadisch auftretenden Problemen. +Die Implementierung eines umfassenden Logging-Systems erwies sich als +unschätzbar wertvoll. Jede Operation wurde protokolliert. Die +Log-Analyse wurde zum wichtigsten Debugging-Tool. ## 3.4 Implementierung, Konfiguration und Inbetriebnahme von Schnittstellen und unterschiedlicher Prozesse und Systeme -Die Implementierung der verschiedenen Schnittstellen erfolgte modular -und iterativ. Die REST-API wurde Blueprint-basiert strukturiert, was -eine klare Trennung der Funktionsbereiche ermöglichte. Authentication, -User Management, Printer Management und Job Management erhielten jeweils -eigene Blueprints. +Die Implementierung der Schnittstellen erfolgte modular. Die REST-API +wurde Blueprint-basiert strukturiert mit klarer Trennung der +Funktionsbereiche: Authentication, User Management, Printer Management +und Job Management. Die Smart-Plug-Schnittstelle durchlief mehrere Iterationen. Die finale -Implementation kapselte die gesamte Kommunikationslogik in einer -einzigen Klasse, die eine simple API bot: turn_on(), turn_off(), -get_status(). Diese Abstraktion verbarg die Komplexität des -darunterliegenden Protokolls und ermöglichte einfache Erweiterungen. +Implementation kapselte die Kommunikationslogik in einer einzigen Klasse +mit simpler API: turn_on(), turn_off(), get_status(). -Die Datenbank-Schnittstelle nutzte SQLAlchemy's ORM-Funktionalität. Die -Definition der Models erfolgte deklarativ, Migrationen wurden über -Alembic verwaltet. Die Entscheidung für SQLite als Datenbank war -pragmatisch – keine zusätzlichen Services, keine Konfiguration, perfekt -für die Offline-Anforderung. +Die Datenbank-Schnittstelle nutzte SQLAlchemy's ORM-Funktionalität. +Models wurden deklarativ definiert, Migrationen über Alembic verwaltet. +SQLite als Datenbank war perfekt für die Offline-Anforderung. Der Scheduler wurde als eigenständiger Thread implementiert, der beim -Anwendungsstart initialisiert wurde. Die Kommunikation mit dem -Hauptthread erfolgte über thread-sichere Queues. Diese Architektur -ermöglichte asynchrone Hardware-Operationen ohne Blockierung der -Web-Requests. +Anwendungsstart initialisiert wurde. Thread-sichere Queues ermöglichten +asynchrone Hardware-Operationen ohne Blockierung der Web-Requests. ## 3.5 Konfiguration von Übertragungssystemen und Integration in die Gesamtinfrastruktur Die Integration in die Mercedes-Benz-Infrastruktur erforderte zahlreiche -Kompromisse und kreative Lösungen. Das dedizierte IoT-Subnetz wurde -speziell für das MYP-System eingerichtet, mit restriktiven -Firewall-Regeln und ohne Internet-Zugang. +Kompromisse. Das dedizierte IoT-Subnetz wurde speziell für das +MYP-System eingerichtet mit restriktiven Firewall-Regeln. Die Netzwerkkonfiguration erfolgte in enger Abstimmung mit der -IT-Abteilung. Jede Änderung erforderte ein Change-Request, jede -Port-Öffnung eine Security-Review. Der bürokratische Overhead war -erheblich, aber notwendig für die Compliance. +IT-Abteilung. Jede Änderung erforderte ein Change-Request. Der +bürokratische Overhead war erheblich aber notwendig. -Die SSL-Konfiguration mit selbstsignierten Zertifikaten war ein -notwendiges Übel. Ohne Internet-Zugang war Let's Encrypt keine Option. -Die Zertifikate wurden mit allen relevanten SANs (Subject Alternative -Names) generiert, um Kompatibilitätsprobleme zu vermeiden. Die -Browser-Warnungen wurden durch eine dokumentierte Prozedur zur -Zertifikats-Installation umgangen. +Die SSL-Konfiguration mit selbstsignierten Zertifikaten war notwendig +mangels Internet-Zugang. Die Zertifikate wurden mit allen relevanten +SANs generiert. Browser-Warnungen wurden durch eine dokumentierte +Installationsprozedur umgangen. -Die Integration der Smart-Plugs erforderte statische IP-Adressen – -DHCP-Reservierungen waren in der Netzwerk-Policy nicht vorgesehen. Die -manuelle Konfiguration jedes Geräts war mühsam, gewährleistete aber -stabile Verbindungen. +Die Smart-Plugs erhielten statische IP-Adressen, da DHCP-Reservierungen +nicht vorgesehen waren. Die manuelle Konfiguration war aufwändig, +gewährleistete aber stabile Verbindungen. ## 3.6 Erfüllen der Anforderungen an die Informationssicherheit -Die Informationssicherheit wurde von Anfang an als kritischer -Erfolgsfaktor behandelt. Jede Designentscheidung wurde durch die -Sicherheitsbrille betrachtet, jede Implementierung auf Schwachstellen -geprüft. +Die Informationssicherheit wurde als kritischer Erfolgsfaktor behandelt. +Jede Designentscheidung wurde unter Sicherheitsaspekten betrachtet. Die Authentifizierung implementierte moderne Best Practices: bcrypt-Hashing, sichere Session-Verwaltung, CSRF-Protection. Die API-Endpunkte wurden systematisch gegen die OWASP Top 10 abgesichert. -Input-Validation erfolgte auf mehreren Ebenen – Client-seitig für UX, -Server-seitig für Sicherheit. +Input-Validation erfolgte auf mehreren Ebenen. -Die Implementierung eines Rate-Limiters verhinderte -Brute-Force-Angriffe. Nach fünf fehlgeschlagenen Login-Versuchen wurde -die IP-Adresse für 30 Minuten gesperrt – lang genug, um Angriffe -unwirtschaftlich zu machen, kurz genug, um legitime Nutzer nicht -übermäßig zu frustrieren. +Ein Rate-Limiter verhinderte Brute-Force-Angriffe. Nach fünf +fehlgeschlagenen Login-Versuchen wurde die IP-Adresse für 30 Minuten +gesperrt. -DSGVO-Compliance wurde durch Privacy-by-Design erreicht. -Personenbezogene Daten wurden minimiert, Löschfristen implementiert, +DSGVO-Compliance wurde durch Privacy-by-Design erreicht. Personenbezogene +Daten wurden minimiert, Löschfristen implementiert, Datenexport-Funktionen bereitgestellt. Die Logging-Funktionalität anonymisierte IP-Adressen nach 30 Tagen automatisch. # 4. Projektabschluss -Dieses Kapitel beschreibt das Ergebnis, die Bewertung sowie das Fazit. - ## 4.1 Soll-Ist-Vergleich (Abweichung, Anpassungen) -Der Vergleich zwischen geplanten und erreichten Zielen offenbart ein -gemischtes, aber letztendlich positives Bild. Die Kernfunktionalität – -digitale Reservierungsverwaltung mit automatischer Hardware-Steuerung – -wurde vollständig implementiert und übertraf in einigen Aspekten sogar -die ursprünglichen Anforderungen. +Der Vergleich zwischen geplanten und erreichten Zielen zeigt ein +positives Gesamtbild. Die Kernfunktionalität wurde vollständig +implementiert und übertraf in einigen Aspekten die ursprünglichen +Anforderungen. #### Erfolgreich umgesetzte Anforderungen: - Vollständige Digitalisierung des Reservierungsprozesses - - Automatische Steuerung der 3D-Drucker via Smart-Plugs - - Robuste Benutzerauthentifizierung und -autorisierung - - Umfassende REST-API mit über 100 Endpunkten - - Offline-fähige Architektur ohne Cloud-Abhängigkeiten - - DSGVO-konforme Datenhaltung - - Energiemonitoring und Nutzungsstatistiken #### Abweichungen vom ursprünglichen Plan: - Konsolidierung auf einen statt zwei Raspberry Pis - - Wechsel von PyP100 zu alternativem Kommunikationsmodul - -- Verzicht auf Touch-Display zugunsten konventioneller Eingabe - -- Verschiebung der Benutzerschulungen auf Nach-Projektphase +- Kein Touch-Display implementiert +- Verschiebung der Benutzerschulungen Die größte positive Überraschung war die erfolgreiche Integration des Energiemonitorings. Diese ursprünglich nicht geplante Funktion -ermöglicht detaillierte Einblicke in Nutzungsmuster und Energieverbrauch -– wertvolle Daten für die Optimierung des Druckerbetriebs. +ermöglicht detaillierte Einblicke in Nutzungsmuster und Energieverbrauch. -Die technischen Herausforderungen – insbesondere die -Smart-Plug-Integration – erforderten mehr Zeit als geplant. Die -investierte Mühe zahlte sich jedoch aus: Die finale Lösung ist robuster -und wartungsfreundlicher als eine Quick-and-Dirty-Implementation gewesen -wäre. +Die technischen Herausforderungen, insbesondere die +Smart-Plug-Integration, erforderten mehr Zeit als geplant. Die +investierte Mühe zahlte sich jedoch in einer robusten und +wartungsfreundlichen Lösung aus. ## 4.2 Fazit -Das MYP-Projekt demonstriert eindrucksvoll, wie durch kreative Ansätze -und technisches Geschick aus scheinbar unüberwindbaren Hindernissen -elegante Lösungen entstehen können. Die Transformation eines analogen -Whiteboards in ein modernes cyber-physisches System mag auf den ersten -Blick trivial erscheinen – die Umsetzung offenbarte jedoch die volle -Komplexität vernetzter Systeme. +Das MYP-Projekt demonstriert, wie durch kreative Ansätze und technisches +Geschick aus scheinbar unüberwindbaren Hindernissen elegante Lösungen +entstehen. Die Transformation eines analogen Whiteboards in ein modernes +cyber-physisches System mag trivial erscheinen – die Umsetzung +offenbarte jedoch die volle Komplexität vernetzter Systeme. Die Entscheidung, die fehlenden Schnittstellen der 3D-Drucker durch -Smart-Plugs zu überbrücken, erwies sich als Glücksgriff. Diese -Abstraktion auf die grundlegendste Ebene – Stromversorgung – ermöglichte -eine universelle Lösung, die unabhängig von Druckermodell oder -Hersteller funktioniert. Ein klassisches Beispiel für das KISS-Prinzip -(Keep It Simple, Stupid) in Aktion. +Smart-Plugs zu überbrücken, erwies sich als optimal. Diese Abstraktion +auf die grundlegendste Ebene – Stromversorgung – ermöglichte eine +universelle, herstellerunabhängige Lösung. -Die technische Exzellenz des Systems zeigt sich in den Details: Über -9.000 Zeilen sauber strukturierter Python-Code, eine umfassende -REST-API, robuste Fehlerbehandlung, durchdachte Sicherheitsarchitektur. -Doch der wahre Erfolg liegt in der Praxistauglichkeit – das System läuft -stabil, wird aktiv genutzt und hat das manuelle Chaos endgültig beendet. +Die technische Exzellenz zeigt sich in den Details: Über 9.000 Zeilen +strukturierter Python-Code, eine umfassende REST-API, robuste +Fehlerbehandlung und durchdachte Sicherheitsarchitektur. Der wahre +Erfolg liegt jedoch in der Praxistauglichkeit – das System läuft stabil +und hat das manuelle Chaos beendet. -Persönlich war das Projekt eine Achterbahnfahrt der Emotionen. Von der -anfänglichen Euphorie über die frustrierenden Debugging-Sessions bis zum -finalen Triumph – jede Phase bot Lernerfahrungen. Die Fähigkeit, unter -Zeitdruck pragmatische Entscheidungen zu treffen, ohne dabei die -Qualität zu kompromittieren, war die wichtigste erworbene Kompetenz. +Persönlich war das Projekt eine intensive Lernerfahrung. Von der +anfänglichen Euphorie über frustrierende Debugging-Sessions bis zum +finalen Erfolg – jede Phase bot wertvolle Erkenntnisse. Die Fähigkeit, +unter Zeitdruck pragmatische Entscheidungen zu treffen ohne die Qualität +zu kompromittieren, war die wichtigste erworbene Kompetenz. ## 4.3 Optimierungsmöglichkeiten @@ -835,57 +687,54 @@ zusätzlicher Funktionalitäten ohne grundlegende Systemänderungen. Kurzfristig ist die Anbindung an das Active Directory der Mercedes-Benz AG geplant. Die vorbereiteten Schnittstellen ermöglichen eine nahtlose -Integration, sobald die erforderlichen Genehmigungen vorliegen. Diese -Erweiterung würde die Benutzerverwaltung erheblich vereinfachen. +Integration, sobald die erforderlichen Genehmigungen vorliegen. Mittelfristig könnte bei Verfügbarkeit modernerer 3D-Drucker eine direkte Geräteintegration realisiert werden. Die Einbindung von -OctoPrint oder vergleichbaren Systemen würde erweiterte Funktionen wie -Druckfortschrittsüberwachung und Remote-Dateiverwaltung ermöglichen. +OctoPrint würde erweiterte Funktionen wie Druckfortschrittsüberwachung +ermöglichen. Langfristig bietet sich die Erweiterung zu einer umfassenden -Maker-Space-Management-Lösung an. Die grundlegende Architektur -unterstützt die Integration weiterer Gerätetypen wie Lasercutter oder -CNC-Fräsen. Machine-Learning-Algorithmen könnten perspektivisch für -Auslastungsprognosen und Optimierungsvorschläge implementiert werden. +Maker-Space-Management-Lösung an. Die Architektur unterstützt die +Integration weiterer Gerätetypen. Machine-Learning-Algorithmen könnten +für Auslastungsprognosen implementiert werden. ## 4.4 Abnahme Die formale Projektabnahme erfolgte am 2. Juni 2025 durch die Ausbildungsleitung der TBA. Die Präsentation umfasste eine Live-Demonstration aller Kernfunktionen sowie eine technische -Deep-Dive-Session für interessierte Kollegen. +Deep-Dive-Session. -Die Live-Demonstration verlief – trotz Murphy's Law – reibungslos. Die -automatische Aktivierung eines 3D-Druckers zur reservierten Zeit löste -sichtbare Begeisterung aus. Die anschließende Erläuterung der -technischen Herausforderungen und deren Lösungen unterstrich die -Komplexität des scheinbar simplen Systems. +Bei der Live-Demonstration kam es zunächst zu einem Missverständnis +bezüglich der Systemkonfiguration, da das System noch nicht vollständig +produktiv aufgebaut war – letzte Hardware-Komponenten waren noch in der +Lieferung. Dank der robusten Systemarchitektur konnte ich jedoch aus dem +Stegreif improvisieren und das System sofort demonstrieren. Die +automatische Aktivierung eines 3D-Druckers zur reservierten Zeit +funktionierte einwandfrei und löste sichtbare Begeisterung aus. -Besonders positiv wurde die Wirtschaftlichkeit der Lösung bewertet. Mit -Gesamtkosten unter 600 Euro (inklusive privat finanzierter Komponenten) -liegt das System weit unter kommerziellen Alternativen. Die Einsparungen -durch automatisierte Abschaltung und optimierte Nutzung amortisieren die -Investition binnen weniger Monate. +Besonders positiv wurde die Wirtschaftlichkeit bewertet. Mit +Gesamtkosten unter 600 Euro liegt das System weit unter kommerziellen +Alternativen. Die Einsparungen durch automatisierte Abschaltung +amortisieren die Investition binnen weniger Monate. Die Rückmeldungen der ersten Nutzer bestätigten die Praxistauglichkeit. -Die intuitive Bedienung, die zuverlässige Funktion und die Eliminierung -von Reservierungskonflikten wurden besonders gelobt. Kritikpunkte – -hauptsächlich bezüglich kleiner UX-Details – wurden dokumentiert und -fließen in zukünftige Updates ein. +Die intuitive Bedienung, zuverlässige Funktion und Eliminierung von +Reservierungskonflikten wurden besonders gelobt. Kleinere UX-Details +wurden dokumentiert für zukünftige Updates. Mit der erfolgreichen Abnahme und Inbetriebnahme schließt das Projekt formal ab. Das MYP-System ist jedoch kein statisches Produkt, sondern der Beginn einer kontinuierlichen Evolution. Die geschaffene Basis -ermöglicht iterative Verbesserungen und Erweiterungen – ganz im Sinne -moderner Software-Entwicklung. +ermöglicht iterative Verbesserungen und Erweiterungen. Die Transformation der 3D-Drucker-Verwaltung von analog zu digital, von chaotisch zu strukturiert, von manuell zu automatisiert ist vollbracht. Was als technische Herausforderung begann, endete als Erfolgsgeschichte -– ein Beweis dafür, dass mit Kreativität, Durchhaltevermögen und einer -Prise technischer Finesse auch scheinbar unlösbare Probleme gemeistert -werden können. +– ein Beweis dafür, dass mit Kreativität, Durchhaltevermögen und +technischer Finesse auch scheinbar unlösbare Probleme gemeistert werden +können. # Anlagen diff --git a/backend/config_optimized.py b/backend/config_optimized.py deleted file mode 100644 index b180314de..000000000 --- a/backend/config_optimized.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Optimized configuration for Raspberry Pi deployment -""" -import os - -class OptimizedConfig: - """Configuration for performance-optimized deployment on Raspberry Pi""" - - # Performance optimization flags - OPTIMIZED_MODE = True - USE_MINIFIED_ASSETS = True - DISABLE_ANIMATIONS = True - LIMIT_GLASSMORPHISM = True - - # Flask performance settings - DEBUG = False - TESTING = False - SEND_FILE_MAX_AGE_DEFAULT = 31536000 # 1 year cache for static files - - # Template settings - TEMPLATES_AUTO_RELOAD = False - EXPLAIN_TEMPLATE_LOADING = False - - # Session configuration - SESSION_COOKIE_SECURE = True - SESSION_COOKIE_HTTPONLY = True - SESSION_COOKIE_SAMESITE = 'Lax' - - # Performance optimizations - MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max upload - JSON_SORT_KEYS = False - JSONIFY_PRETTYPRINT_REGULAR = False - - # Database optimizations - SQLALCHEMY_ECHO = False - SQLALCHEMY_TRACK_MODIFICATIONS = False - SQLALCHEMY_ENGINE_OPTIONS = { - 'pool_size': 5, - 'pool_recycle': 3600, - 'pool_pre_ping': True, - 'connect_args': { - 'check_same_thread': False - } - } - - # Cache configuration - CACHE_TYPE = 'simple' - CACHE_DEFAULT_TIMEOUT = 300 - CACHE_KEY_PREFIX = 'myp_' - - # Static file caching headers - SEND_FILE_MAX_AGE_DEFAULT = 31536000 # 1 year - - @staticmethod - def init_app(app): - """Initialize application with optimized settings""" - # Set optimized template - app.jinja_env.globals['optimized_mode'] = True - app.jinja_env.globals['base_template'] = 'base-optimized.html' - - # Add cache headers for static files - @app.after_request - def add_cache_headers(response): - if 'static' in response.headers.get('Location', ''): - response.headers['Cache-Control'] = 'public, max-age=31536000' - response.headers['Vary'] = 'Accept-Encoding' - return response - - # Disable unnecessary features - app.config['EXPLAIN_TEMPLATE_LOADING'] = False - app.config['TEMPLATES_AUTO_RELOAD'] = False - - print("🚀 Running in OPTIMIZED mode for Raspberry Pi") \ No newline at end of file diff --git a/backend/setup.bat b/backend/setup.bat new file mode 100644 index 000000000..0519ecba6 --- /dev/null +++ b/backend/setup.bat @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/setup.sh b/backend/setup.sh index 488210887..9b5ad9a14 100644 --- a/backend/setup.sh +++ b/backend/setup.sh @@ -1,39 +1,24 @@ #!/bin/bash # =================================================================== -# 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 +# MYP Druckerverwaltung - VOLLSTÄNDIGES SETUP-SKRIPT +# Optimiert für Raspberry Pi OS (Debian-basiert) +# Version: 5.0.0 - Komplett überarbeitet und getestet # =================================================================== set -euo pipefail # =========================== GLOBALE KONFIGURATION =========================== readonly APP_NAME="MYP Druckerverwaltung" -readonly APP_VERSION="4.1.0" +readonly APP_VERSION="5.0.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="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 INSTALL_LOG="${CURRENT_DIR}/logs/myp-install.log" +readonly ERROR_LOG="${CURRENT_DIR}/logs/myp-install-errors.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' @@ -44,46 +29,11 @@ readonly PURPLE='\033[0;35m' readonly CYAN='\033[0;36m' readonly NC='\033[0m' -# =========================== VERBESSERTE LOGGING-FUNKTIONEN =========================== -# Globale Variablen für Fehler-Tracking -ERROR_COUNT=0 -WARNING_COUNT=0 - -# Log-Dateien initialisieren +# =========================== LOGGING-FUNKTIONEN =========================== init_logging() { - # 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" + 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" } log() { @@ -92,64 +42,14 @@ log() { } error() { - 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" - + local message="[ERROR] $1" + echo -e "${RED}${message}${NC}" | tee -a "$INSTALL_LOG" | tee -a "$ERROR_LOG" exit 1 } warning() { - 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 + local message="[WARNING] $1" 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() { @@ -157,3288 +57,387 @@ info() { } progress() { - echo -e "${PURPLE}[FORTSCHRITT] $1${NC}" | tee -a "$INSTALL_LOG" + echo -e "${PURPLE}[PROGRESS] $1${NC}" | tee -a "$INSTALL_LOG" } success() { - echo -e "${CYAN}[ERFOLG] $1${NC}" | tee -a "$INSTALL_LOG" + echo -e "${CYAN}[SUCCESS] $1${NC}" | tee -a "$INSTALL_LOG" } -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 =========================== +# =========================== SYSTEM-CHECKS =========================== 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_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 - - # 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_debian_system() { - progress "Prüfe Debian/Raspbian-System..." - - # 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" + if [ ! -f /etc/debian_version ]; then + error "Dieses Skript ist nur für Debian/Raspberry Pi OS gedacht!" 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" + log "✅ Debian-basiertes System erkannt" } -check_internet_connection() { - progress "Prüfe Internetverbindung (erweiterte Methoden)..." +check_required_files() { + log "=== PRÜFE ERFORDERLICHE DATEIEN ===" - 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=( + local required_files=( "app.py" "models.py" "requirements.txt" ) - 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" + for file in "${required_files[@]}"; do + if [ ! -f "${CURRENT_DIR}/${file}" ]; then + error "Erforderliche Datei nicht gefunden: ${file}" fi + success "✅ ${file} gefunden" done - - # Verzeichnisse mit robuster Behandlung - local directories=( - "blueprints" - "config" - "database" - "static" - "templates" - "uploads" - "utils" - "logs" - "certs" - ) - - 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 - - # Optionale Dateien - local optional_files=( - "package.json" - "package-lock.json" - "tailwind.config.js" - "postcss.config.js" - "README.md" - ".gitignore" - ) - - 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 - - # 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" - ) - - for dir in "${required_dirs[@]}"; do - mkdir -p "$APP_DIR/$dir" 2>/dev/null || warning "⚠️ Verzeichnis $dir konnte nicht erstellt werden" - done - - # 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 - - # 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 - - # Upload-Verzeichnisse - chmod 755 "$APP_DIR/uploads" 2>/dev/null || true - chmod 755 "$APP_DIR/static" 2>/dev/null || true - - # Python-Umgebung robust konfigurieren - progress "Konfiguriere robuste Python-Umgebung..." - - # 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 - - 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 "⚠️ Python site-packages Verzeichnis nicht gefunden" - fi - - # 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" } -install_npm_dependencies() { - log "=== NPM-ABHÄNGIGKEITEN INSTALLATION ===" +# =========================== SYSTEM-UPDATE =========================== +update_system() { + log "=== SYSTEM UPDATE ===" - 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 + progress "Aktualisiere Paketlisten..." + apt-get update || error "Fehler beim Update der Paketlisten" - log "✅ NPM-Abhängigkeiten verarbeitet" + progress "Aktualisiere System-Pakete..." + DEBIAN_FRONTEND=noninteractive apt-get upgrade -y || warning "Einige Pakete konnten nicht aktualisiert werden" + + success "✅ System aktualisiert" } -# =========================== SSL-ZERTIFIKAT GENERIERUNG =========================== -generate_ssl_certificate() { - log "=== SSL-ZERTIFIKAT GENERIERUNG ===" +# =========================== ABHÄNGIGKEITEN INSTALLATION =========================== +install_base_dependencies() { + log "=== INSTALLIERE BASIS-ABHÄNGIGKEITEN ===" - progress "Generiere selbstsigniertes SSL-Zertifikat für localhost..." + 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" + ) - local cert_dir="$APP_DIR/certs/localhost" + 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}" + done + + success "✅ Basis-Abhängigkeiten installiert" +} + +# =========================== PYTHON-UMGEBUNG =========================== +setup_python_environment() { + log "=== PYTHON-UMGEBUNG EINRICHTEN ===" + + # App-Verzeichnis erstellen + progress "Erstelle App-Verzeichnis..." + mkdir -p "$APP_DIR" + + # Dateien kopieren + progress "Kopiere Anwendungsdateien..." + cp -r "${CURRENT_DIR}"/* "$APP_DIR/" 2>/dev/null || true + + # Python-Abhängigkeiten installieren + progress "Installiere Python-Abhängigkeiten..." + cd "$APP_DIR" + + 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" + fi + + cd "$CURRENT_DIR" + success "✅ Python-Umgebung eingerichtet" +} + +# =========================== NODE.JS INSTALLATION =========================== +install_nodejs() { + log "=== INSTALLIERE NODE.JS ===" + + if command -v node >/dev/null 2>&1; then + log "Node.js bereits installiert: $(node --version)" + return + fi + + 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" + + 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" + + progress "Baue Frontend-Assets..." + npm run build || warning "Fehler beim Frontend-Build" + else + warning "package.json nicht gefunden - überspringe Frontend-Build" + fi + + cd "$CURRENT_DIR" + success "✅ Frontend-Build abgeschlossen" +} + +# =========================== SSL-ZERTIFIKATE =========================== +setup_ssl_certificates() { + log "=== SSL-ZERTIFIKATE EINRICHTEN ===" + + local cert_dir="${APP_DIR}/instance/ssl" mkdir -p "$cert_dir" - # Generiere privaten Schlüssel - openssl genrsa -out "$cert_dir/localhost.key" 2048 || error "Fehler beim Generieren des privaten Schlüssels" + 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 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" + chmod 600 "${cert_dir}/key.pem" + chmod 644 "${cert_dir}/cert.pem" - # Berechtigungen setzen - chmod 600 "$cert_dir/localhost.key" - chmod 644 "$cert_dir/localhost.crt" - - log "✅ SSL-Zertifikat erfolgreich generiert" + success "✅ SSL-Zertifikate erstellt" } -# =========================== ROBUSTE SYSTEMD-SERVICES INSTALLATION =========================== -install_systemd_services() { - log "=== ROBUSTE SYSTEMD-SERVICES INSTALLATION ===" +# =========================== SYSTEMD SERVICES =========================== +setup_systemd_services() { + log "=== SYSTEMD SERVICES EINRICHTEN ===" - # Validiere systemd-Verzeichnis - if [ ! -d "$SYSTEMD_DIR" ]; then - error "systemd-Verzeichnis nicht gefunden: $SYSTEMD_DIR" - fi - - 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" + # 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" } -enable_and_start_services() { - log "=== ROBUSTE SERVICES AKTIVIERUNG UND START ===" +# =========================== KIOSK-BENUTZER =========================== +setup_kiosk_user() { + log "=== KIOSK-BENUTZER EINRICHTEN ===" - # Service-Status tracking - local successful_services=0 - local failed_services=0 - - # HTTPS-Service (kritisch) - progress "Aktiviere und starte HTTPS-Service (kritisch)..." - - 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++)) + if ! id "$KIOSK_USER" &>/dev/null; then + progress "Erstelle Kiosk-Benutzer..." + useradd -m -s /bin/bash "$KIOSK_USER" + usermod -aG video,audio "$KIOSK_USER" fi - # 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 - - # 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 - - # 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 + # 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" } -# =========================== ROBUSTE SYSTEM-TESTS =========================== -test_application() { - log "=== UMFASSENDE SYSTEM-TESTS ===" +# =========================== FIREWALL =========================== +configure_firewall() { + log "=== FIREWALL KONFIGURIEREN ===" - local test_errors=0 - local test_warnings=0 + systemctl start firewalld + systemctl enable firewalld - # 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 + # HTTPS Port öffnen + firewall-cmd --permanent --add-port=${HTTPS_PORT}/tcp - # 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 + # SSH erlauben + firewall-cmd --permanent --add-service=ssh - # Test 3: HTTPS-Verbindung (robust mit mehreren Methoden) - progress "Teste HTTPS-Verbindung (robust)..." + # RDP für Remote-Desktop + firewall-cmd --permanent --add-port=3389/tcp - local max_attempts=20 - local attempt=1 - local connection_successful=false + firewall-cmd --reload - 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 + success "✅ Firewall konfiguriert" +} + +# =========================== DATENBANK INITIALISIERUNG =========================== +initialize_database() { + log "=== DATENBANK INITIALISIEREN ===" - 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 + cd "$APP_DIR" - # 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 " + progress "Initialisiere Datenbank..." + python3 << EOF import sys -import os -sys.path.insert(0, '$APP_DIR') -os.chdir('$APP_DIR') - +sys.path.append('${APP_DIR}') +from models import init_database, create_initial_admin try: - 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') - + init_database() + create_initial_admin() + print("✅ Datenbank erfolgreich initialisiert") except Exception as e: - print(f'⚠️ App-Import-Problem: {e}') - exit(1) -" 2>&1; then - success "✅ Python-Anwendung kann erfolgreich importiert werden" - else - warning "⚠️ Python-Anwendung Import-Probleme (möglicherweise nicht kritisch)" - ((test_warnings++)) - fi + print(f"❌ Fehler bei Datenbank-Initialisierung: {e}") + sys.exit(1) +EOF - 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 - - # 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 + cd "$CURRENT_DIR" } -# =========================== AUFRÄUMEN =========================== -cleanup_old_files() { - log "=== AUFRÄUMEN ALTE DATEIEN ===" +# =========================== SERVICES STARTEN =========================== +start_services() { + log "=== SERVICES STARTEN ===" - progress "Entferne alte Shell-Skripte..." + # SSH aktivieren + systemctl enable ssh + systemctl start ssh - # Entferne alte Skripte (falls vorhanden) - local old_scripts=("combined.sh" "installer.sh") + # HTTPS Service + systemctl enable "${HTTPS_SERVICE_NAME}" + systemctl start "${HTTPS_SERVICE_NAME}" - 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 + # Warte bis Service läuft + sleep 5 - log "✅ Aufräumen abgeschlossen" + # 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" + else + warning "⚠️ HTTPS Service läuft nicht" + 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" + 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 "" } # =========================== HAUPTMENÜ =========================== show_menu() { clear - echo -e "${CYAN}=================================================================${NC}" - echo -e "${CYAN} $APP_NAME - Setup-Skript v$APP_VERSION${NC}" - echo -e "${CYAN}=================================================================${NC}" + echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" + echo -e "${CYAN}║ MYP DRUCKERVERWALTUNG - SETUP v${APP_VERSION} ║${NC}" + echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" - 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 -e "${BLUE}Wählen Sie eine Option:${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 -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" + echo -n "Ihre Auswahl [1-3]: " } # =========================== HAUPTPROGRAMM =========================== main() { - # Erstelle logs-Verzeichnis im aktuellen Projektverzeichnis - mkdir -p "logs" 2>/dev/null || true + init_logging while true; do show_menu @@ -3446,46 +445,47 @@ main() { case $choice in 1) - 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" + 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!" echo "" echo -n "Drücken Sie Enter um fortzufahren..." read -r ;; 2) - 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" + log "=== NUR ABHÄNGIGKEITEN INSTALLATION ===" + check_root + check_debian_system + update_system + install_base_dependencies + setup_python_environment + install_nodejs + log "✅ Abhängigkeiten installiert!" echo "" echo -n "Drücken Sie Enter um fortzufahren..." read -r ;; 3) - 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 + echo -e "${CYAN}Setup beendet.${NC}" exit 0 ;; *) - echo -e "${RED}Ungültige Auswahl. Bitte wählen Sie 1-3.${NC}" + echo -e "${RED}Ungültige Auswahl!${NC}" + sleep 2 ;; esac done