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.
}
+
);
}
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