diff --git a/backend/index.js b/backend/index.js index 398d5a3..81c1fa0 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) => { @@ -35,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}`); }); @@ -49,6 +50,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 +91,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 +145,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..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 }) => { @@ -68,45 +71,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 new file mode 100755 index 0000000..e4645d1 --- /dev/null +++ b/scripts/stash.sh @@ -0,0 +1,138 @@ +#!/bin/bash + +# =============================== +# Git Stash Manager mit Branch-Filter und Überschreiben +# =============================== + +# Aktuellen Branch ermitteln +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 (apply)" +echo "3) Vorhandenen Stash löschen" +echo "4) Stash anwenden und löschen (pop)" +read -p "Auswahl: " 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" + + # 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 + + # Neuen Stash anlegen (inkl. untracked Dateien) + git stash push -u -m "$STASH_NAME" + echo "Stash '$STASH_NAME' wurde angelegt." + ;; + + 2) + # ========== Stash anwenden ========== + 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 (Nummer): " choice + if [[ -z "${STASH_MAP[$choice]}" ]]; then + echo "Ungültige Auswahl!" + exit 1 + fi + + SELECTED_STASH=${STASH_MAP[$choice]} + echo "Wende Stash an: $SELECTED_STASH" + git stash apply "$SELECTED_STASH" + ;; + + 3) + # ========== Stash löschen ========== + 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 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" + ;; + + 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 + ;; +esac