From 279ad3ffd13bb4f1c7bfae164ec7a8bfeda4783f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Sep 2025 11:34:16 +0000 Subject: [PATCH 1/6] Update --- scripts/stash.sh | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100755 scripts/stash.sh diff --git a/scripts/stash.sh b/scripts/stash.sh new file mode 100755 index 0000000..3d2c929 --- /dev/null +++ b/scripts/stash.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# =============================== +# Git Stash Manager Script +# =============================== + +# Aktuellen Branch ermitteln +CURRENT_BRANCH=$(git branch --show-current) + +# =============================== +# Auswahl: Stash anlegen, laden oder löschen +# =============================== +echo "Was möchtest du tun?" +echo "1) Neuen Stash anlegen (aktueller Branch: $CURRENT_BRANCH)" +echo "2) Vorhandenen Stash anwenden (nur aktueller Branch)" +echo "3) Vorhandenen Stash löschen (nur aktueller Branch)" + +read -p "Wähle eine Option (1-3): " ACTION + +case "$ACTION" in + 1) + # ------------------------------- + # Neuer Stash (inkl. untracked Dateien) + # ------------------------------- + read -p "Gib einen eigenen Namen für den Stash ein: " CUSTOM_NAME + if [[ -z "$CUSTOM_NAME" ]]; then + CUSTOM_NAME="WIP-$(date +%Y%m%d%H%M%S)" + fi + + STASH_NAME="${CURRENT_BRANCH} - ${CUSTOM_NAME}" + git stash push -u -m "$STASH_NAME" + echo "Änderungen (inkl. untracked files) erfolgreich gestasht unter dem Namen: '$STASH_NAME'" + git stash list | grep "On $CURRENT_BRANCH" + ;; + 2) + # ------------------------------- + # Vorhandenen Stash anwenden + # ------------------------------- + echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" + BRANCH_STASHES=($(git stash list | grep "On $CURRENT_BRANCH" | cut -d: -f1)) + + if [[ ${#BRANCH_STASHES[@]} -eq 0 ]]; then + echo "Keine Stashes für diesen Branch vorhanden." + exit 0 + fi + + i=1 + declare -A STASH_MAP + for s in "${BRANCH_STASHES[@]}"; do + echo "$i) $s" + STASH_MAP[$i]=$s + ((i++)) + done + + read -p "Gib die Nummer des Stashs ein, den du anwenden möchtest: " STASH_CHOICE + if [[ -z "${STASH_MAP[$STASH_CHOICE]}" ]]; then + echo "Ungültige Auswahl!" + exit 1 + fi + + git stash apply ${STASH_MAP[$STASH_CHOICE]} + echo "Stash ${STASH_MAP[$STASH_CHOICE]} angewendet." + ;; + 3) + # ------------------------------- + # Vorhandenen Stash löschen + # ------------------------------- + echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" + BRANCH_STASHES=($(git stash list | grep "On $CURRENT_BRANCH" | cut -d: -f1)) + + if [[ ${#BRANCH_STASHES[@]} -eq 0 ]]; then + echo "Keine Stashes für diesen Branch vorhanden." + exit 0 + fi + + i=1 + declare -A STASH_MAP + for s in "${BRANCH_STASHES[@]}"; do + echo "$i) $s" + STASH_MAP[$i]=$s + ((i++)) + done + + read -p "Gib die Nummer des Stashs ein, den du löschen möchtest: " STASH_CHOICE + if [[ -z "${STASH_MAP[$STASH_CHOICE]}" ]]; then + echo "Ungültige Auswahl!" + exit 1 + fi + + git stash drop ${STASH_MAP[$STASH_CHOICE]} + echo "Stash ${STASH_MAP[$STASH_CHOICE]} gelöscht." + ;; + *) + echo "Ungültige Option!" + exit 1 + ;; +esac From ef8920cb26a49996ae22638c2b162d8f3b3f4c98 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Sep 2025 11:38:11 +0000 Subject: [PATCH 2/6] Update --- scripts/stash.sh | 51 +++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/scripts/stash.sh b/scripts/stash.sh index 3d2c929..1b36010 100755 --- a/scripts/stash.sh +++ b/scripts/stash.sh @@ -7,9 +7,6 @@ # Aktuellen Branch ermitteln CURRENT_BRANCH=$(git branch --show-current) -# =============================== -# Auswahl: Stash anlegen, laden oder löschen -# =============================== echo "Was möchtest du tun?" echo "1) Neuen Stash anlegen (aktueller Branch: $CURRENT_BRANCH)" echo "2) Vorhandenen Stash anwenden (nur aktueller Branch)" @@ -19,9 +16,7 @@ read -p "Wähle eine Option (1-3): " ACTION case "$ACTION" in 1) - # ------------------------------- # Neuer Stash (inkl. untracked Dateien) - # ------------------------------- read -p "Gib einen eigenen Namen für den Stash ein: " CUSTOM_NAME if [[ -z "$CUSTOM_NAME" ]]; then CUSTOM_NAME="WIP-$(date +%Y%m%d%H%M%S)" @@ -29,16 +24,14 @@ case "$ACTION" in STASH_NAME="${CURRENT_BRANCH} - ${CUSTOM_NAME}" git stash push -u -m "$STASH_NAME" - echo "Änderungen (inkl. untracked files) erfolgreich gestasht unter dem Namen: '$STASH_NAME'" + echo "Änderungen erfolgreich gestasht unter dem Namen: '$STASH_NAME'" git stash list | grep "On $CURRENT_BRANCH" ;; 2) - # ------------------------------- - # Vorhandenen Stash anwenden - # ------------------------------- + # Stash anwenden echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" - BRANCH_STASHES=($(git stash list | grep "On $CURRENT_BRANCH" | cut -d: -f1)) - + BRANCH_STASHES=($(git stash list | grep "On $CURRENT_BRANCH")) + if [[ ${#BRANCH_STASHES[@]} -eq 0 ]]; then echo "Keine Stashes für diesen Branch vorhanden." exit 0 @@ -46,11 +39,13 @@ case "$ACTION" in i=1 declare -A STASH_MAP - for s in "${BRANCH_STASHES[@]}"; do - echo "$i) $s" - STASH_MAP[$i]=$s + while read -r line; do + STASH_ID=$(echo "$line" | cut -d: -f1) + STASH_MSG=$(echo "$line" | cut -d: -f3- | sed 's/^ //') + echo "$i) $STASH_ID -> $STASH_MSG" + STASH_MAP[$i]=$STASH_ID ((i++)) - done + done < <(git stash list | grep "On $CURRENT_BRANCH") read -p "Gib die Nummer des Stashs ein, den du anwenden möchtest: " STASH_CHOICE if [[ -z "${STASH_MAP[$STASH_CHOICE]}" ]]; then @@ -62,25 +57,23 @@ case "$ACTION" in echo "Stash ${STASH_MAP[$STASH_CHOICE]} angewendet." ;; 3) - # ------------------------------- - # Vorhandenen Stash löschen - # ------------------------------- + # Stash löschen echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" - BRANCH_STASHES=($(git stash list | grep "On $CURRENT_BRANCH" | cut -d: -f1)) - - if [[ ${#BRANCH_STASHES[@]} -eq 0 ]]; then + i=1 + declare -A STASH_MAP + while read -r line; do + STASH_ID=$(echo "$line" | cut -d: -f1) + STASH_MSG=$(echo "$line" | cut -d: -f3- | sed 's/^ //') + echo "$i) $STASH_ID -> $STASH_MSG" + STASH_MAP[$i]=$STASH_ID + ((i++)) + done < <(git stash list | grep "On $CURRENT_BRANCH") + + if [[ $i -eq 1 ]]; then echo "Keine Stashes für diesen Branch vorhanden." exit 0 fi - i=1 - declare -A STASH_MAP - for s in "${BRANCH_STASHES[@]}"; do - echo "$i) $s" - STASH_MAP[$i]=$s - ((i++)) - done - read -p "Gib die Nummer des Stashs ein, den du löschen möchtest: " STASH_CHOICE if [[ -z "${STASH_MAP[$STASH_CHOICE]}" ]]; then echo "Ungültige Auswahl!" From bff652165865a4caefb33ad6508ab61b873cc47b Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Sep 2025 11:47:18 +0000 Subject: [PATCH 3/6] Update --- ca | 0 scripts/stash.sh | 158 ++++++++++++++++++++++++++--------------------- 2 files changed, 87 insertions(+), 71 deletions(-) create mode 100644 ca diff --git a/ca b/ca new file mode 100644 index 0000000..e69de29 diff --git a/scripts/stash.sh b/scripts/stash.sh index 1b36010..0235617 100755 --- a/scripts/stash.sh +++ b/scripts/stash.sh @@ -1,90 +1,106 @@ #!/bin/bash # =============================== -# Git Stash Manager Script +# Git Stash Manager mit Branch-Filter und Überschreiben # =============================== # Aktuellen Branch ermitteln -CURRENT_BRANCH=$(git branch --show-current) +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +echo "Aktueller Branch: $CURRENT_BRANCH" echo "Was möchtest du tun?" -echo "1) Neuen Stash anlegen (aktueller Branch: $CURRENT_BRANCH)" -echo "2) Vorhandenen Stash anwenden (nur aktueller Branch)" -echo "3) Vorhandenen Stash löschen (nur aktueller Branch)" +echo "1) Neuen Stash anlegen" +echo "2) Vorhandenen Stash laden" +echo "3) Vorhandenen Stash löschen" +read -p "Auswahl: " action -read -p "Wähle eine Option (1-3): " ACTION +case $action in + 1) + # ========== Neuen Stash anlegen ========== + read -p "Gib einen Namen für den Stash ein: " USER_INPUT + STASH_NAME="$CURRENT_BRANCH - $USER_INPUT" -case "$ACTION" in - 1) - # Neuer Stash (inkl. untracked Dateien) - read -p "Gib einen eigenen Namen für den Stash ein: " CUSTOM_NAME - if [[ -z "$CUSTOM_NAME" ]]; then - CUSTOM_NAME="WIP-$(date +%Y%m%d%H%M%S)" - fi + # Prüfen, ob Stash-Name schon existiert + EXISTING=$(git stash list | grep "$STASH_NAME") + if [[ -n "$EXISTING" ]]; then + echo "Ein Stash mit dem Namen '$STASH_NAME' existiert bereits." + # Alle passenden Stashes löschen + while IFS= read -r line; do + STASH_REF=$(echo "$line" | awk -F: '{print $1}') + echo "Lösche vorhandenen Stash: $STASH_REF" + git stash drop "$STASH_REF" + done <<< "$EXISTING" + fi - STASH_NAME="${CURRENT_BRANCH} - ${CUSTOM_NAME}" - git stash push -u -m "$STASH_NAME" - echo "Änderungen erfolgreich gestasht unter dem Namen: '$STASH_NAME'" - git stash list | grep "On $CURRENT_BRANCH" - ;; - 2) - # Stash anwenden - echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" - BRANCH_STASHES=($(git stash list | grep "On $CURRENT_BRANCH")) + # Neuen Stash anlegen (inkl. untracked Dateien) + git stash push -u -m "$STASH_NAME" + echo "Stash '$STASH_NAME' wurde angelegt." + ;; - if [[ ${#BRANCH_STASHES[@]} -eq 0 ]]; then - echo "Keine Stashes für diesen Branch vorhanden." - exit 0 - fi + 2) + # ========== Stash anwenden ========== + echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" + STASHES=$(git stash list | grep "$CURRENT_BRANCH") - i=1 - declare -A STASH_MAP - while read -r line; do - STASH_ID=$(echo "$line" | cut -d: -f1) - STASH_MSG=$(echo "$line" | cut -d: -f3- | sed 's/^ //') - echo "$i) $STASH_ID -> $STASH_MSG" - STASH_MAP[$i]=$STASH_ID - ((i++)) - done < <(git stash list | grep "On $CURRENT_BRANCH") + if [[ -z "$STASHES" ]]; then + echo "Keine Stashes für diesen Branch vorhanden." + exit 0 + fi - read -p "Gib die Nummer des Stashs ein, den du anwenden möchtest: " STASH_CHOICE - if [[ -z "${STASH_MAP[$STASH_CHOICE]}" ]]; then - echo "Ungültige Auswahl!" - exit 1 - fi + i=1 + declare -A STASH_MAP + while IFS= read -r line; do + STASH_REF=$(echo "$line" | awk -F: '{print $1}') + STASH_MSG=$(echo "$line" | cut -d':' -f3- | sed 's/^ //') + echo "$i) $STASH_REF -> $STASH_MSG" + STASH_MAP[$i]=$STASH_REF + ((i++)) + done <<< "$STASHES" - git stash apply ${STASH_MAP[$STASH_CHOICE]} - echo "Stash ${STASH_MAP[$STASH_CHOICE]} angewendet." - ;; - 3) - # Stash löschen - echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" - i=1 - declare -A STASH_MAP - while read -r line; do - STASH_ID=$(echo "$line" | cut -d: -f1) - STASH_MSG=$(echo "$line" | cut -d: -f3- | sed 's/^ //') - echo "$i) $STASH_ID -> $STASH_MSG" - STASH_MAP[$i]=$STASH_ID - ((i++)) - done < <(git stash list | grep "On $CURRENT_BRANCH") + read -p "Wähle einen Stash (Nummer): " choice + if [[ -z "${STASH_MAP[$choice]}" ]]; then + echo "Ungültige Auswahl!" + exit 1 + fi - if [[ $i -eq 1 ]]; then - echo "Keine Stashes für diesen Branch vorhanden." - exit 0 - fi + SELECTED_STASH=${STASH_MAP[$choice]} + echo "Wende Stash an: $SELECTED_STASH" + git stash apply "$SELECTED_STASH" + ;; - read -p "Gib die Nummer des Stashs ein, den du löschen möchtest: " STASH_CHOICE - if [[ -z "${STASH_MAP[$STASH_CHOICE]}" ]]; then - echo "Ungültige Auswahl!" - exit 1 - fi + 3) + # ========== Stash löschen ========== + echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" + STASHES=$(git stash list | grep "$CURRENT_BRANCH") - git stash drop ${STASH_MAP[$STASH_CHOICE]} - echo "Stash ${STASH_MAP[$STASH_CHOICE]} gelöscht." - ;; - *) - echo "Ungültige Option!" - exit 1 - ;; + if [[ -z "$STASHES" ]]; then + echo "Keine Stashes für diesen Branch vorhanden." + exit 0 + fi + + i=1 + declare -A STASH_MAP + while IFS= read -r line; do + STASH_REF=$(echo "$line" | awk -F: '{print $1}') + STASH_MSG=$(echo "$line" | cut -d':' -f3- | sed 's/^ //') + echo "$i) $STASH_REF -> $STASH_MSG" + STASH_MAP[$i]=$STASH_REF + ((i++)) + done <<< "$STASHES" + + read -p "Wähle einen Stash zum Löschen (Nummer): " choice + if [[ -z "${STASH_MAP[$choice]}" ]]; then + echo "Ungültige Auswahl!" + exit 1 + fi + + SELECTED_STASH=${STASH_MAP[$choice]} + echo "Lösche Stash: $SELECTED_STASH" + git stash drop "$SELECTED_STASH" + ;; + + *) + echo "Ungültige Auswahl!" + exit 1 + ;; esac From ca148247cbf8e6ca6cbb8be973ee439877026755 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Sep 2025 11:48:52 +0000 Subject: [PATCH 4/6] Update --- ca | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ca diff --git a/ca b/ca deleted file mode 100644 index e69de29..0000000 From 3489ea55a21063af42d0f917538e9a2da9ff3077 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 26 Sep 2025 09:28:34 +0000 Subject: [PATCH 5/6] Update --- backend/index.js | 48 ++++++++++++++++++++++- frontend/src/Stacks.jsx | 87 ++++++++++++++++++++++++++--------------- scripts/stash.sh | 34 +++++++++++++++- 3 files changed, 135 insertions(+), 34 deletions(-) diff --git a/backend/index.js b/backend/index.js index 398d5a3..2843036 100644 --- a/backend/index.js +++ b/backend/index.js @@ -14,7 +14,6 @@ const __dirname = path.dirname(__filename); const app = express(); app.use(express.json()); - app.use(express.static(path.join(__dirname, 'public'))); app.get('*', (req, res, next) => { @@ -49,6 +48,7 @@ const broadcastRedeployStatus = (stackId, status) => { // --- API Endpoints --- +// Stacks abrufen app.get('/api/stacks', async (req, res) => { console.log("ℹ️ [API] GET /api/stacks: Abruf gestartet"); try { @@ -89,6 +89,7 @@ app.get('/api/stacks', async (req, res) => { } }); +// Einzel-Redeploy app.put('/api/stacks/:id/redeploy', async (req, res) => { const { id } = req.params; console.log(`🔄 PUT /api/stacks/${id}/redeploy: Redeploy gestartet`); @@ -142,6 +143,51 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { } }); +// Redeploy ALL +app.put('/api/stacks/redeploy-all', async (req, res) => { + console.log(`🚀 PUT /api/stacks/redeploy-all: Redeploy ALL gestartet`); + + try { + const stacksRes = await axiosInstance.get('/api/stacks'); + const filteredStacks = stacksRes.data.filter(stack => stack.EndpointId === ENDPOINT_ID); + + console.log("📦 Redeploy ALL für folgende Stacks:"); + filteredStacks.forEach(s => console.log(` - ${s.Name}`)); + + filteredStacks.forEach(async (stack) => { + try { + broadcastRedeployStatus(stack.Id, true); + + if (stack.Type === 1) { + console.log(`🔄 [Redeploy] Git Stack "${stack.Name}" (${stack.Id})`); + await axiosInstance.put(`/api/stacks/${stack.Id}/git/redeploy?endpointId=${stack.EndpointId}`); + } else if (stack.Type === 2) { + console.log(`🔄 [Redeploy] Compose Stack "${stack.Name}" (${stack.Id})`); + const fileRes = await axiosInstance.get(`/api/stacks/${stack.Id}/file`); + const stackFileContent = fileRes.data?.StackFileContent; + if (stackFileContent) { + await axiosInstance.put(`/api/stacks/${stack.Id}`, + { StackFileContent: stackFileContent, Prune: false, PullImage: true }, + { params: { endpointId: stack.EndpointId } } + ); + } + } + + broadcastRedeployStatus(stack.Id, false); + console.log(`✅ Redeploy abgeschlossen: ${stack.Name}`); + } catch (err) { + broadcastRedeployStatus(stack.Id, false); + console.error(`❌ Fehler beim Redeploy von Stack ${stack.Name}:`, err.message); + } + }); + + res.json({ success: true, message: 'Redeploy ALL gestartet' }); + } catch (err) { + console.error(`❌ Fehler beim Redeploy ALL:`, err.message); + res.status(500).json({ error: err.message }); + } +}); + server.listen(PORT, '0.0.0.0', () => { console.log(`🚀 Backend läuft auf Port ${PORT}`); }); diff --git a/frontend/src/Stacks.jsx b/frontend/src/Stacks.jsx index cc6c353..00b9ac5 100644 --- a/frontend/src/Stacks.jsx +++ b/frontend/src/Stacks.jsx @@ -68,45 +68,68 @@ export default function Stacks() { } }; + const handleRedeployAll = async () => { + setStacks(prev => prev.map(stack => ({ ...stack, redeploying: true }))); + + try { + await axios.put("/api/stacks/redeploy-all"); + // Statusupdates kommen über Socket.IO + } catch (err) { + console.error("❌ Fehler beim Redeploy ALL:", err); + setStacks(prev => prev.map(stack => ({ ...stack, redeploying: false }))); + } + }; + if (loading) return

Lade Stacks...

; if (error) return

{error}

; return ( -
- {stacks.map(stack => { - const isRedeploying = stack.redeploying; +
+
+ +
- return ( -
-
-
-
-

{stack.Name}

-

ID: {stack.Id}

-
-
+
+ {stacks.map(stack => { + const isRedeploying = stack.redeploying; - -
- ); - })} - {stacks.length === 0 &&

Keine Stacks gefunden.

} +
+
+
+

{stack.Name}

+

ID: {stack.Id}

+
+
+ + +
+ ); + })} + {stacks.length === 0 &&

Keine Stacks gefunden.

} +
); } diff --git a/scripts/stash.sh b/scripts/stash.sh index 0235617..e4645d1 100755 --- a/scripts/stash.sh +++ b/scripts/stash.sh @@ -10,8 +10,9 @@ CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) echo "Aktueller Branch: $CURRENT_BRANCH" echo "Was möchtest du tun?" echo "1) Neuen Stash anlegen" -echo "2) Vorhandenen Stash laden" +echo "2) Vorhandenen Stash laden (apply)" echo "3) Vorhandenen Stash löschen" +echo "4) Stash anwenden und löschen (pop)" read -p "Auswahl: " action case $action in @@ -99,6 +100,37 @@ case $action in git stash drop "$SELECTED_STASH" ;; + 4) + # ========== Stash anwenden und löschen (pop) ========== + echo "Liste aller Stashes für Branch '$CURRENT_BRANCH':" + STASHES=$(git stash list | grep "$CURRENT_BRANCH") + + if [[ -z "$STASHES" ]]; then + echo "Keine Stashes für diesen Branch vorhanden." + exit 0 + fi + + i=1 + declare -A STASH_MAP + while IFS= read -r line; do + STASH_REF=$(echo "$line" | awk -F: '{print $1}') + STASH_MSG=$(echo "$line" | cut -d':' -f3- | sed 's/^ //') + echo "$i) $STASH_REF -> $STASH_MSG" + STASH_MAP[$i]=$STASH_REF + ((i++)) + done <<< "$STASHES" + + read -p "Wähle einen Stash zum Anwenden & Löschen (Nummer): " choice + if [[ -z "${STASH_MAP[$choice]}" ]]; then + echo "Ungültige Auswahl!" + exit 1 + fi + + SELECTED_STASH=${STASH_MAP[$choice]} + echo "Wende Stash an und lösche ihn: $SELECTED_STASH" + git stash pop "$SELECTED_STASH" + ;; + *) echo "Ungültige Auswahl!" exit 1 From 960832af6b6260615d6ab91b14ad57bd36abe287 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 26 Sep 2025 10:06:59 +0000 Subject: [PATCH 6/6] Update --- backend/index.js | 6 ++++-- frontend/src/Stacks.jsx | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/index.js b/backend/index.js index 2843036..81c1fa0 100644 --- a/backend/index.js +++ b/backend/index.js @@ -34,8 +34,10 @@ const axiosInstance = axios.create({ const redeployingStacks = {}; const server = http.createServer(app); -const io = new Server(server, { cors: { origin: "*" } }); - +const io = new Server(server, { + path: "/socket.io", + cors: { origin: "*" } +}); io.on("connection", (socket) => { console.log(`🔌 [Socket] Client verbunden: ${socket.id}`); }); diff --git a/frontend/src/Stacks.jsx b/frontend/src/Stacks.jsx index 00b9ac5..a7a43b8 100644 --- a/frontend/src/Stacks.jsx +++ b/frontend/src/Stacks.jsx @@ -8,7 +8,10 @@ export default function Stacks() { const [error, setError] = useState(""); useEffect(() => { - const socket = io("/", { transports: ["websocket"] }); +const socket = io("/", { + path: "/socket.io", + transports: ["websocket"] +}); console.log("🔌 Socket connected"); socket.on("redeployStatus", async ({ stackId, status }) => {