From 716cd13c53d3a5e268fd260633a1fb338066ab02 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Sep 2025 06:37:44 +0000 Subject: [PATCH] Update --- frontend/src/Stacks.jsx | 79 ++++++++++++++++++-------- frontend/src/Stacks.jsx.bak | 109 ++++++++++++++++++++++++++++++++++++ scripts/docker-release.sh | 30 ++-------- 3 files changed, 169 insertions(+), 49 deletions(-) create mode 100644 frontend/src/Stacks.jsx.bak mode change 100644 => 100755 scripts/docker-release.sh diff --git a/frontend/src/Stacks.jsx b/frontend/src/Stacks.jsx index 9560ebd..8b7bf27 100644 --- a/frontend/src/Stacks.jsx +++ b/frontend/src/Stacks.jsx @@ -6,37 +6,59 @@ export default function Stacks() { const [stacks, setStacks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); + // Map: { [stackId]: boolean } const [redeploying, setRedeploying] = useState({}); - // WebSocket initialisieren + // ------------------------- + // WebSocket: connect + events + // ------------------------- useEffect(() => { - const socket = io("/"); - console.log("Socket connected"); + // wenn Frontend und Backend auf verschiedenen Hosts/Ports laufen: + // const socket = io("http://localhost:4001"); + const socket = io(); // gleiche Origin (Express static) -> passt normalerweise + socket.on("connect", () => console.log("Socket connected:", socket.id)); socket.on("redeployStatus", async ({ stackId, status }) => { + // sofort den lokalen map-state setzen (optimistisch) setRedeploying(prev => ({ ...prev, [stackId]: status })); - // Wenn Redeploy beendet, Stack-Status neu laden + // Wenn der Redeploy zu false geht, holen wir die aktuellen Stacks (aktualisieren UI) if (!status) { try { const res = await axios.get("/api/stacks"); const sortedStacks = res.data.sort((a, b) => a.Name.localeCompare(b.Name)); setStacks(sortedStacks); + + // Map aus API-Daten aufbauen (wichtig für F5 / Konsistenz) + const map = {}; + sortedStacks.forEach(s => { map[s.Id] = !!s.redeploying; }); + setRedeploying(map); } catch (err) { console.error("Fehler beim Aktualisieren des Status nach Redeploy:", err); } } }); - return () => socket.disconnect(); + return () => { + socket.off("redeployStatus"); + socket.disconnect(); + }; }, []); - // Stacks initial laden + // ------------------------- + // Initiale Stacks laden (auch setzt redeploying map) + // ------------------------- const fetchStacks = async () => { + setLoading(true); try { const res = await axios.get("/api/stacks"); const sortedStacks = res.data.sort((a, b) => a.Name.localeCompare(b.Name)); setStacks(sortedStacks); + + // Wichtig: redeploying-Map aus API-Daten setzen, damit F5 den echten Zustand zeigt + const map = {}; + sortedStacks.forEach(s => { map[s.Id] = !!s.redeploying; }); + setRedeploying(map); } catch (err) { console.error("❌ Fehler beim Abrufen der Stacks:", err); setError("Fehler beim Laden der Stacks"); @@ -49,60 +71,71 @@ export default function Stacks() { fetchStacks(); }, []); - // Redeploy eines Stacks + // ------------------------- + // Redeploy Trigger + // ------------------------- const handleRedeploy = async (stackId) => { + // sofort UI-Feedback setRedeploying(prev => ({ ...prev, [stackId]: true })); try { await axios.put(`/api/stacks/${stackId}/redeploy`); + // kein sofortiges setRedeploying(false) – Backend sendet das finale Event } catch (err) { console.error("❌ Fehler beim Redeploy:", err); + // Fehlerfall: wieder auf false setzen damit UI nicht hängen bleibt setRedeploying(prev => ({ ...prev, [stackId]: false })); } }; + // ------------------------- + // Render + // ------------------------- if (loading) return

Lade Stacks...

; if (error) return

{error}

; return (
{stacks.map(stack => { - const isRedeploying = redeploying[stack.Id] || false; - const isUpToDate = stack.updateStatus === "✅"; + const isRedeploying = Boolean(redeploying[stack.Id]); return (
"gedämpfter" Container (sichtbar deaktiviert) className={`flex justify-between items-center p-5 rounded-xl shadow-lg transition - ${isRedeploying ? "bg-gray-700 cursor-not-allowed" : "bg-gray-800 hover:bg-gray-700"}`} + ${isRedeploying ? "bg-gray-700 opacity-60" : "bg-gray-800 hover:bg-gray-700"}`} + aria-disabled={isRedeploying} >
- {/* Status Indicator */}
-
+ />

{stack.Name}

ID: {stack.Id}

- {!isUpToDate && ( - - )} + {/* Button ist jetzt immer sichtbar */} +
); })} + {stacks.length === 0 &&

Keine Stacks gefunden.

}
); diff --git a/frontend/src/Stacks.jsx.bak b/frontend/src/Stacks.jsx.bak new file mode 100644 index 0000000..9560ebd --- /dev/null +++ b/frontend/src/Stacks.jsx.bak @@ -0,0 +1,109 @@ +import React, { useEffect, useState } from "react"; +import axios from "axios"; +import { io } from "socket.io-client"; + +export default function Stacks() { + const [stacks, setStacks] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + const [redeploying, setRedeploying] = useState({}); + + // WebSocket initialisieren + useEffect(() => { + const socket = io("/"); + console.log("Socket connected"); + + socket.on("redeployStatus", async ({ stackId, status }) => { + setRedeploying(prev => ({ ...prev, [stackId]: status })); + + // Wenn Redeploy beendet, Stack-Status neu laden + if (!status) { + try { + const res = await axios.get("/api/stacks"); + const sortedStacks = res.data.sort((a, b) => a.Name.localeCompare(b.Name)); + setStacks(sortedStacks); + } catch (err) { + console.error("Fehler beim Aktualisieren des Status nach Redeploy:", err); + } + } + }); + + return () => socket.disconnect(); + }, []); + + // Stacks initial laden + const fetchStacks = async () => { + try { + const res = await axios.get("/api/stacks"); + const sortedStacks = res.data.sort((a, b) => a.Name.localeCompare(b.Name)); + setStacks(sortedStacks); + } catch (err) { + console.error("❌ Fehler beim Abrufen der Stacks:", err); + setError("Fehler beim Laden der Stacks"); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchStacks(); + }, []); + + // Redeploy eines Stacks + const handleRedeploy = async (stackId) => { + setRedeploying(prev => ({ ...prev, [stackId]: true })); + + try { + await axios.put(`/api/stacks/${stackId}/redeploy`); + } catch (err) { + console.error("❌ Fehler beim Redeploy:", err); + setRedeploying(prev => ({ ...prev, [stackId]: false })); + } + }; + + if (loading) return

Lade Stacks...

; + if (error) return

{error}

; + + return ( +
+ {stacks.map(stack => { + const isRedeploying = redeploying[stack.Id] || false; + const isUpToDate = stack.updateStatus === "✅"; + + return ( +
+
+ {/* Status Indicator */} +
+
+
+

{stack.Name}

+

ID: {stack.Id}

+
+
+ + {!isUpToDate && ( + + )} +
+ ); + })} + {stacks.length === 0 &&

Keine Stacks gefunden.

} +
+ ); +} diff --git a/scripts/docker-release.sh b/scripts/docker-release.sh old mode 100644 new mode 100755 index c079f8e..d584e68 --- a/scripts/docker-release.sh +++ b/scripts/docker-release.sh @@ -2,8 +2,8 @@ set -e # --- Konfiguration --- -GHCR_USERNAME="" -REPO_NAME="" +GHCR_USERNAME="mboehmlaender" +REPO_NAME="stackpulse" # --- Branch prüfen --- BRANCH=$(git rev-parse --abbrev-ref HEAD) @@ -14,32 +14,10 @@ fi # --- Versionsnummer abfragen --- while true; do - read -p "Bitte Versionsnummer für das Release eingeben (z.B. v0.2.0): " VERSION_TAG + read -p "Bitte Versionsnummer für das Docker-Image eingeben (z.B. v0.1): " VERSION_TAG if [[ -n "$VERSION_TAG" ]]; then break; else echo "Versionsnummer darf nicht leer sein."; fi done -# --- Git: Pull und Merge sicherstellen --- -git pull origin master - -# --- Git: Commit ausstehender Änderungen --- -git add . -read -p "Commit-Nachricht eingeben (default: 'Release $VERSION_TAG'): " COMMIT_MSG -COMMIT_MSG=${COMMIT_MSG:-"Release $VERSION_TAG"} -git commit -m "$COMMIT_MSG" || echo "Keine Änderungen zum Committen." - -# --- Git: Tag setzen --- -if git rev-parse "$VERSION_TAG" >/dev/null 2>&1; then - echo "Tag $VERSION_TAG existiert bereits. Bitte zuerst löschen oder neuen Tag wählen." - exit 1 -fi - -git tag -a "$VERSION_TAG" -m "Release $VERSION_TAG" - -# --- Git: Push master + Tag --- -git push origin master -git push origin "$VERSION_TAG" -echo "Tag $VERSION_TAG gesetzt und auf master gepusht." - # --- Docker: Login --- if [ -z "$CR_PAT" ]; then echo "CR_PAT (GitHub Token) nicht gesetzt! Bitte export CR_PAT=" @@ -55,4 +33,4 @@ docker tag ghcr.io/$GHCR_USERNAME/$REPO_NAME:$VERSION_TAG ghcr.io/$GHCR_USERNAME docker push ghcr.io/$GHCR_USERNAME/$REPO_NAME:$VERSION_TAG docker push ghcr.io/$GHCR_USERNAME/$REPO_NAME:latest -echo "Release $VERSION_TAG erfolgreich auf master + GHCR gepusht!" +echo "Docker-Release $VERSION_TAG erfolgreich gebaut und zu GHCR gepusht!"