From ece3f8bf781ee78ddcc239380eb1d4bc6b5eb80e Mon Sep 17 00:00:00 2001 From: Stefan Mogeritsch Date: Sat, 9 May 2026 17:09:47 +0200 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20Grundlegendes=20HTML-Template?= =?UTF-8?q?=20f=C3=BCr=20Website=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stefan Mogeritsch --- .gitignore | 4 + conveyor.conf | 38 +- docs/02_Guides/Desktop-Packaging-Guide.md | 98 ++ docs/90_Reports/Network-POC-Testplan.md | 47 + .../meldestelle-desktop/build.gradle.kts | 58 +- .../src/jvmMain/resources/icon.png | Bin 21694 -> 13582 bytes index.html | 850 ++++++++++++++++++ setup-firewall-linux.sh | 29 + 8 files changed, 1040 insertions(+), 84 deletions(-) create mode 100644 docs/02_Guides/Desktop-Packaging-Guide.md create mode 100644 docs/90_Reports/Network-POC-Testplan.md create mode 100644 index.html create mode 100755 setup-firewall-linux.sh diff --git a/.gitignore b/.gitignore index c381d9af..005d81b1 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,7 @@ desktop.ini docs/temp/ docs/Bin/ docs/_archive/ + +# Conveyor +conveyor.rootkey +output/ diff --git a/conveyor.conf b/conveyor.conf index 97b9232b..f4ae9879 100644 --- a/conveyor.conf +++ b/conveyor.conf @@ -1,42 +1,31 @@ # ============================================================================= # Conveyor Configuration for Meldestelle Desktop App # ============================================================================= -# Dieser Build-Weg ermöglicht das Cross-Packaging für Windows (MSI) auf Linux. -# Dokumentation: https://conveyor.hydraulic.dev/ -# ============================================================================= +include required("/stdlib/jdk/21/openjdk.conf") include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf") -# Basis-Import der Gradle-Konfiguration (sofern das Plugin genutzt wird, -# aber wir definieren es hier explizit für maximale Kontrolle im CI/CD). app { - # Anzeige-Name und Vendor display-name = "Meldestelle" rdns-name = "at.mocode.meldestelle" vendor = "mo-code.at" contact-email = "support@mo-code.at" - - # Version aus version.properties (Conveyor kann HOCON-Variablen nutzen) - # Für diesen Task hart codiert oder via CLI-Flag --variable übergeben. version = "1.0.0" - - # Beschreibung description = "ÖTO-konforme Turnier-Meldestelle – Desktop App" - # Ziel-Plattformen - # Wir konzentrieren uns auf Windows, können aber Linux/Mac später ergänzen. - site.base-url = "localhost" # Später echte Update-URL + # Ziel-Plattformen: Windows und Linux (AMD/Intel 64-bit) + machines = [ windows.amd64, linux.amd64.glibc ] - # Icons - icons = "frontend/shells/meldestelle-desktop/src/jvmMain/resources/icon.png" + site.base-url = "localhost" + + # Wir geben nur den Ordner an, Conveyor findet die icon.png darin automatisch + icons = "frontend/shells/meldestelle-desktop/src/jvmMain/resources" - # Einbetten der JRE (Temurin 21 wie in CI genutzt) jvm { gui { main-class = "at.mocode.frontend.shell.desktop.MainKt" } - # JVM-Argumente (analog build.gradle.kts) jvm-options = [ "-Xms128m", "-Xmx512m", @@ -45,19 +34,14 @@ app { ] } - # Input-Dateien: Hier ziehen wir die Uber-JAR oder die Gradle-Outputs. - # Da wir plattformunabhängig bleiben wollen, nutzen wir das Gradle-Output-Dir. - inputs += "frontend/shells/meldestelle-desktop/build/libs/meldestelle-desktop-jvm-*.jar" + # JARs aus dem Gradle-Build + inputs += "frontend/shells/meldestelle-desktop/build/libs/*.jar" - # Windows-spezifische Einstellungen windows { - # Icon als .ico - icons = "frontend/shells/meldestelle-desktop/src/jvmMain/resources/icon.ico" - # GUID für Upgrades (muss stabil bleiben) upgrade-uuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" - # Menü-Eintrag menu-group = "Meldestelle" - # Verknüpfung desktop-shortcut = true } } + +conveyor.compatibility-level = 22 diff --git a/docs/02_Guides/Desktop-Packaging-Guide.md b/docs/02_Guides/Desktop-Packaging-Guide.md new file mode 100644 index 00000000..05df4bc9 --- /dev/null +++ b/docs/02_Guides/Desktop-Packaging-Guide.md @@ -0,0 +1,98 @@ +# 📦 Guide: Desktop App Packaging (Linux, Windows, macOS) + +Dieses Dokument beschreibt, wie die Meldestelle Desktop App für verschiedene Betriebssysteme paketiert wird. Wir nutzen einen hybriden Ansatz aus **Gradle (Compose-Desktop)** für lokale Linux-Builds und **Conveyor** für das Cross-Packaging (Windows/macOS) von Linux aus. + +--- + +## 1. Voraussetzungen + +### Linux (Entwicklungsrechner / Fedora) +Um native Pakete bauen zu können, müssen folgende Werkzeuge auf dem System vorhanden sein: + +```bash +# Für RPM-Pakete (Fedora) +sudo dnf install rpm-build + +# Für DEB-Pakete (Ubuntu/Debian) +sudo apt install dpkg-dev fakeroot +``` + +### Conveyor (Cross-Packaging Tool) +Conveyor wird benötigt, um von Linux aus Windows-Installer (.msi) oder macOS-Pakete zu erzeugen. + +**Installation auf Fedora/Linux:** +Da automatisierte Skripte manchmal unzuverlässig sind, hier der direkte Weg über das Binär-Paket: + +1. **Tarball herunterladen:** + Besuchen Sie [https://downloads.hydraulic.dev/conveyor/download.html](https://downloads.hydraulic.dev/conveyor/download.html) und laden Sie die neueste `linux-amd64.tar.gz` Datei herunter. + +2. **Manuelle Installation:** + ```bash + # Entpacken + tar -xvf hydraulic-conveyor-*-linux-amd64.tar.gz + # In den Pfad verschieben + sudo mv conveyor /usr/local/bin/ + ``` + +3. **Verifizieren:** + ```bash + conveyor --version + ``` + +--- + +## 2. Lokale Linux-Builds (Gradle) + +Die schnellste Methode, um während der Entwicklung ein installierbares Paket für das eigene System zu erstellen. + +### RPM-Paket (Fedora) +```bash +./gradlew :frontend:shells:meldestelle-desktop:packageRpm +``` +*Ausgabe: `frontend/shells:meldestelle-desktop/build/compose/binaries/main/rpm/`* + +### DEB-Paket (Ubuntu/Debian) +```bash +./gradlew :frontend:shells:meldestelle-desktop:packageDeb +``` +*Ausgabe: `frontend/shells:meldestelle-desktop/build/compose/binaries/main/deb/`* + +### Portable Version (Ohne Installation) +```bash +./gradlew :frontend:shells:meldestelle-desktop:createDistributable +``` +*Ausgabe: `frontend/shells:meldestelle-desktop/build/compose/binaries/main/app/`* + +--- + +## 3. Cross-Packaging mit Conveyor + +Conveyor nutzt die kompilierte JAR-Datei und schnürt daraus Pakete für alle Zielplattformen. + +### Schritt 1: JAR erstellen +```bash +./gradlew :frontend:shells:meldestelle-desktop:jvmJar +``` + +### Schritt 2: Pakete bauen +```bash +# Erstellt den Windows-Installer und die HTML-Downloadseite +conveyor make site +``` + +### Schritt 3: Ergebnisse +Die fertigen Installer (z.B. `.msi` für Windows) befinden sich im neu erstellten Ordner `output/`. + +--- + +## 4. Problembehandlung & Optimierung + +### Native Access Warnungen +Die App benötigt Zugriff auf native Bibliotheken (Netty/SQLite). Der notwendige Parameter `--enable-native-access=ALL-UNNAMED` ist bereits fest hinterlegt. + +### Firewall-Konfiguration +Für Netzwerk-Tests (Discovery/Chat) müssen die Ports 8090, 8080 und 5353 (UDP) geöffnet sein. +Nutzen Sie dafür das bereitgestellte Skript: +```bash +sudo ./setup-firewall-linux.sh +``` diff --git a/docs/90_Reports/Network-POC-Testplan.md b/docs/90_Reports/Network-POC-Testplan.md new file mode 100644 index 00000000..4dad2248 --- /dev/null +++ b/docs/90_Reports/Network-POC-Testplan.md @@ -0,0 +1,47 @@ +# 🧪 Testplan: Real-World Netzwerk-POC (Chat) + +Ziel dieses Tests ist die Verifizierung der stabilen Kommunikation zwischen verschiedenen Geräten (Master & Client) im lokalen Netzwerk (LAN/WLAN) inklusive automatischer Dienst-Erkennung (mDNS). + +--- + +## Vorbereitung (USB-Stick) + +Folgende Dateien sollten auf dem Test-USB-Stick vorhanden sein: +1. **Installer:** Das .rpm oder .deb Paket der App (oder der distributable Ordner). +2. **Windows-Installer:** Die .msi Datei (via Conveyor). +3. **Setup-Skript:** setup-firewall-linux.sh. + +--- + +## Durchführung + +### 1. Master-Gerät einrichten (Zentrale) +1. App auf dem Haupt-PC installieren und starten. +2. In der **Geräte-Initialisierung**: + * Rolle: **MASTER** wählen. + * Gerätename vergeben (z.B. "Meldestelle-Master"). + * Sicherheitsschlüssel (Sync-Key) festlegen (z.B. "geheim123"). +3. Auf **Finalisieren** klicken. +4. Der Master zeigt nun seine IP-Adresse an und wartet auf Clients. + +### 2. Client-Geräte einrichten (Richter/PC) +1. App auf weiteren Geräten (Linux/Windows) starten. +2. In der **Geräte-Initialisierung**: + * Rolle: **CLIENT** wählen. + * **Shared Key** eingeben (muss exakt wie beim Master sein). +3. Warten, bis der Master in der Liste erscheint (mDNS Discovery). +4. Master auswählen und auf **Jetzt verbinden** klicken. + +### 3. Verbindungs-Check & Chat +1. Sobald der Status auf "Verbunden" steht, den Button **"Verbindung testen (Chat & Self-Test)"** klicken. +2. Im Chat-Modal eine Nachricht schreiben. +3. Prüfen, ob die Nachricht auf allen verbundenen Geräten erscheint. +4. Den automatischen "Ping-Pong" Self-Test beobachten. + +--- + +## Erfolgskriterien +* [ ] Master wird innerhalb von 10 Sekunden automatisch in der Client-Liste gefunden. +* [ ] Nachrichten werden nahezu verzögerungsfrei (< 500ms) übertragen. +* [ ] Der Status wechselt zuverlässig auf "CONNECTED". +* [ ] Keine FocusRelatedWarning mehr in der Konsole/Log. diff --git a/frontend/shells/meldestelle-desktop/build.gradle.kts b/frontend/shells/meldestelle-desktop/build.gradle.kts index f103a495..44e38057 100644 --- a/frontend/shells/meldestelle-desktop/build.gradle.kts +++ b/frontend/shells/meldestelle-desktop/build.gradle.kts @@ -1,21 +1,6 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat import java.util.* -/** - * Shell-Modul: Meldestelle Desktop App - * Reines JVM/Compose-Desktop-Modul – Desktop-First gemäß MASTER_ROADMAP. - * Setzt alle Core- und Feature-Module zu einer lauffähigen Desktop-Anwendung zusammen. - * - * Packaging: - * ./gradlew :frontend:shells:meldestelle-desktop:packageDeb → Linux .deb - * ./gradlew :frontend:shells:meldestelle-desktop:packageMsi → Windows .msi - * ./gradlew :frontend:shells:meldestelle-desktop:packageDmg → macOS .dmg - * ./gradlew :frontend:shells:meldestelle-desktop:packageReleaseDistributables → alle Plattformen - * - * Version: Wird automatisch aus version.properties im Root-Projekt gelesen (SemVer). - * Icons: src/jvmMain/resources/icon.png / icon.ico / icon.icns - * → siehe ICONS_PLACEHOLDER.md für Anforderungen - */ plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.composeCompiler) @@ -26,16 +11,12 @@ plugins { group = "at.mocode.frontend.shell" version = "1.0.0" -// --------------------------------------------------------------- -// Version aus root version.properties lesen (SemVer) -// --------------------------------------------------------------- val versionProps = Properties().also { props -> rootProject.file("version.properties").inputStream().use { props.load(it) } } val vMajor: String? = versionProps.getProperty("VERSION_MAJOR", "1") val vMinor: String? = versionProps.getProperty("VERSION_MINOR", "0") val vPatch: String? = versionProps.getProperty("VERSION_PATCH", "0") -// nativeDistributions erwartet reines "MAJOR.MINOR.PATCH" (kein Qualifier) val packageVer = "$vMajor.$vMinor.$vPatch" kotlin { @@ -43,7 +24,6 @@ kotlin { sourceSets { jvmMain.dependencies { - // Core-Module implementation(projects.frontend.core.domain) implementation(projects.core.coreDomain) implementation(projects.frontend.core.designSystem) @@ -54,10 +34,8 @@ kotlin { implementation(projects.frontend.core.auth) implementation(projects.core.znsParser) - // Feature-Module implementation(projects.frontend.features.pingFeature) implementation(projects.frontend.features.nennungFeature) - implementation(projects.frontend.features.znsImportFeature) implementation(projects.frontend.features.veranstalterFeature) implementation(projects.frontend.features.veranstaltungFeature) @@ -70,7 +48,6 @@ kotlin { implementation(projects.frontend.features.billingFeature) implementation(projects.frontend.features.deviceInitialization) - // Compose Desktop implementation(compose.desktop.currentOs) implementation(compose.runtime) implementation(compose.foundation) @@ -80,15 +57,10 @@ kotlin { implementation(compose.uiTooling) implementation(libs.composeHotReloadApi) - // DI (Koin) implementation(libs.koin.core) implementation(libs.koin.compose) implementation(libs.koin.compose.viewmodel) - - // Coroutines implementation(libs.kotlinx.coroutines.swing) - - // Bundles implementation(libs.bundles.kmp.common) implementation(libs.bundles.compose.common) implementation(libs.logback.classic) @@ -105,12 +77,8 @@ compose.desktop { mainClass = "at.mocode.frontend.shell.desktop.MainKt" nativeDistributions { - // Ziel-Formate: Linux .deb, Windows .msi, macOS .dmg - targetFormats(TargetFormat.Deb, TargetFormat.Msi, TargetFormat.Dmg) + targetFormats(TargetFormat.Deb, TargetFormat.Rpm, TargetFormat.Msi, TargetFormat.Dmg) - // ------------------------------------------------------- - // Gemeinsame App-Metadaten - // ------------------------------------------------------- packageName = "meldestelle" packageVersion = packageVer description = "ÖTO-konforme Turnier-Meldestelle – Desktop App" @@ -118,53 +86,30 @@ compose.desktop { copyright = "© 2024–2026 mo-code.at. Alle Rechte vorbehalten." licenseFile.set(rootProject.file("LICENSE")) - // ------------------------------------------------------- - // Linux (.deb) - // ------------------------------------------------------- linux { - // PNG 512×512 px — siehe src/jvmMain/resources/ICONS_PLACEHOLDER.md iconFile.set(project.file("src/jvmMain/resources/icon.png")) packageName = "meldestelle" - // Debian-Kategorie appCategory = "misc" - // Menü-Eintrag menuGroup = "Meldestelle" shortcut = true debMaintainer = "support@mo-code.at" } - // ------------------------------------------------------- - // Windows (.msi) - // ------------------------------------------------------- windows { - // ICO Multi-Size — siehe src/jvmMain/resources/ICONS_PLACEHOLDER.md iconFile.set(project.file("src/jvmMain/resources/icon.ico")) - // Eindeutige GUID für Windows Installer Upgrade-Erkennung - // WICHTIG: Diese UUID darf sich NIE ändern! upgradeUuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" menuGroup = "Meldestelle" - // Startmenü-Verknüpfung shortcut = true - // Desktop-Verknüpfung dirChooser = true perUserInstall = false } - // ------------------------------------------------------- - // macOS (.dmg) - // ------------------------------------------------------- macOS { - // ICNS 1024×1024 px — siehe src/jvmMain/resources/ICONS_PLACEHOLDER.md iconFile.set(project.file("src/jvmMain/resources/icon.icns")) bundleID = "at.mocode.meldestelle" appCategory = "public.app-category.productivity" - // Für notarisierten Release: signing-Konfiguration hier ergänzen - // signing { sign.set(true); identity.set("Developer ID Application: ...") } } - // ------------------------------------------------------- - // JVM-Laufzeit-Konfiguration (eingebettetes JRE) - // ------------------------------------------------------- modules( "java.base", "java.desktop", @@ -177,7 +122,6 @@ compose.desktop { ) } - // JVM-Argumente für die gepackte Anwendung jvmArgs( "--enable-native-access=ALL-UNNAMED", "-Xms128m", diff --git a/frontend/shells/meldestelle-desktop/src/jvmMain/resources/icon.png b/frontend/shells/meldestelle-desktop/src/jvmMain/resources/icon.png index fc232d66ce4f179547a19998a53b5601689a7bc5..d2a261e56381abf74f87dc60da9664a4c2ae15bd 100644 GIT binary patch literal 13582 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

S8YqhF}phQ#9F2WZOf4U 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + +CHANGELOG.md - Realease notes & Feedback Management Tool + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +
+ +
+ +
+
+
+
+
+ + + Effortlessly collect feedback + +
+
+

Create better products driven by customer feedback

+

Simplify feedback collection, lighten support tasks, and share product updates—all in one powerful tool.

+
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + + +
+
+
+
+
+
+
+
+ clients-logo-1.svg +
+
+
+
+ clients-logo-2.svg +
+
+
+
+ clients-logo-3.svg +
+
+
+
+ clients-logo-4.svg +
+
+
+
+ clients-logo-5.svg +
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+ + + Features + +
+
+

All-in-One Platform for Customer Feedback

+

Centralize your feedback, prioritize your next steps, and keep everyone informed.

+
+
+
+
+
+ +
+
+ +
+
+ + +
+ +
+
+ +
+
+
+ Powerful SaaS solutions. +
+

Feedback Management

+

Don’t let valuable ideas fall through the cracks. Use a single tool to collect, analyze, and organize feedback and feature requests efficiently.

+
    +
  • - Capture customer input seamlessly from conversations with Autopilot.
  • +
  • - Detect and merge duplicate requests to better quantify user needs.
  • +
+ + +
+ + +
+
+ feature-img +
+
+
+
+ + +
+
+ +
+
+
+ Define Your Product Vision. +
+

Build your roadmap

+

Keep users and stakeholders informed about current projects and upcoming plans.

+
+ + +
+
+ feature-img +
+
+
+
+ + +
+
+ +
+
+
+ Management & prioritization +
+

Prioritize feature requests

+

Create a prioritization formula to score feedback and feature requests, ensuring you focus on the most impactful features.

+
    +
  • - Adjust impact and effort factors to fit your needs.
  • +
  • - Include business-specific post fields for greater flexibility.
  • +
  • - Prioritize features based on user demand.
  • +
+
+ + +
+
+ feature-img +
+
+
+
+ + +
+
+ +
+
+
+ CHANGELOG.md +
+

Share updates

+

Create a changelog that keeps everyone informed and engaged.

+
    +
  • - Publish Detailed Release Notes.
  • +
  • - Notify users who voted on specific feature requests.
  • +
  • - Drive customer retention, engagement and feature adoption.
  • +
+
+ + +
+
+ feature-img +
+
+
+
+
+
+
+ + + + +
+
+
+
+
+ + + Capture feedback + +
+
+

Easy to set up and use

+

Simplify feedback collection, lighten support workloads, and announce product updates—all with a single tool.

+
+
+
+
+
+
+
+
+

Feedback Board

+

Gather, analyze, and organize feedback in a centralized location

+
+
+
+ +
+
+
+
+
+
+
+

Product Roadmap

+

Create public/private roadmaps to keep everyone updated on your progress

+
+
+
+ +
+
+
+
+
+
+
+

Changelog

+

Increase transparency with detailed change logs

+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+

Feature Request

+

Organize feature requests to identify the most in-demand improvements.

+
+
+
+
+
+
+
+
+
+
+ +
+
+

Customer Satisfaction

+

Collect ongoing feedback to track and improve customer satisfaction over time.

+
+
+
+
+
+
+
+
+
+
+ +
+
+

Analyze feedback

+

+ Uncover valuable customer insights to make better product decisions. +

+
+
+
+
+
+
+
+
+
+
+ +
+
+

Bug Reporting

+

Receive instant notifications when users report bugs, keeping you ahead of critical issues.

+
+
+
+
+
+
+
+
+
+
+ +
+
+

Uptime monitoring service

+

Create beautiful status pages & incident management reports and keep your visitors updated.(Soon)

+
+
+
+
+
+
+
+ + + + + +
+
+
+
+
+
+ Join Our Newsletter +

Subscribe Now

+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+ + + Help Center + +
+
+

Frequently Asked Questions

+
+
+
+
+
+
+
+
+
+
+

+ +

+
+

CHANGELOG.md is a SaaS platform designed to help you collect, analyze, and act on customer feedback to uncover valuable insights and make informed product decisions.

+
+
+
+

+ +

+
+

Absolutely! With our prioritization tools, you can score feedback and feature requests based on factors like impact and effort, helping you focus on what matters most.

+
+
+
+

+ +

+
+

CHANGELOG.md allows you to build a clear and actionable roadmap by organizing feedback and aligning it with your product vision.

+
+
+
+

+ +

+
+

Yes! CHANGELOG.md includes a changelog feature where you can publish detailed release notes, link them to specific feature requests, and notify users who requested those features automatically.

+
+
+
+

+ +

+
+

Yes, CHANGELOG.md integrates with popular customer support and project management tools, allowing your team to seamlessly capture and manage feedback within their existing workflows.

+
+
+
+

+ +

+
+

Product managers, customer success teams, and anyone involved in building and improving products can benefit from CHANGELOG.md. It’s perfect for startups, SaaS companies, and organizations looking to make data-driven product decisions.

+
+
+
+

+ +

+
+

Getting started is simple! Sign up for a free trial, set up your feedback portal, and start collecting insights to drive your product decisions.

+
+
+
+
+
+
+
+
+ + + + + +
+
+
+
+
+ + + More features. More power. + +
+
+

We bring companies and customers even closer

+

Ready to start building the right things?

+
+
+ +
+
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/setup-firewall-linux.sh b/setup-firewall-linux.sh new file mode 100755 index 00000000..8026f155 --- /dev/null +++ b/setup-firewall-linux.sh @@ -0,0 +1,29 @@ +#!/bin/bash +echo "===========================================" +echo "Meldestelle - Netzwerk-Setup für POC" +echo "===========================================" + +if [ "$EUID" -ne 0 ]; then + echo "Bitte mit sudo ausführen: sudo ./setup-firewall-linux.sh" + exit +fi + +# Erkennung der Firewall (firewalld für Fedora/KDE, ufw für Ubuntu) +if command -v firewall-cmd &> /dev/null; then + echo "[Fedora/firewalld] Öffne Ports 8090 (TCP), 8080 (TCP) und 5353 (UDP)..." + firewall-cmd --permanent --add-port=8090/tcp + firewall-cmd --permanent --add-port=8080/tcp + firewall-cmd --permanent --add-service=mdns + firewall-cmd --reload + echo "Fertig!" +elif command -v ufw &> /dev/null; then + echo "[Ubuntu/ufw] Öffne Ports 8090 (TCP), 8080 (TCP) und 5353 (UDP)..." + ufw allow 8090/tcp + ufw allow 8080/tcp + ufw allow 5353/udp + echo "Fertig!" +else + echo "Keine bekannte Firewall (ufw/firewalld) gefunden. Bitte Ports manuell prüfen." +fi + +echo "Das System ist nun bereit für den Meldestelle-POC."