diff --git a/backend/index.js b/backend/index.js index c9f6f97..1a26dfc 100644 --- a/backend/index.js +++ b/backend/index.js @@ -48,7 +48,7 @@ const server = http.createServer(app); const io = new Server(server, { cors: { origin: "*" } }); io.on("connection", (socket) => { - console.log("🔌 Client verbunden:", socket.id); + console.log("Client verbunden:", socket.id); }); const broadcastRedeployStatus = (stackId, status) => { @@ -59,13 +59,12 @@ const broadcastRedeployStatus = (stackId, status) => { // --- API Endpoints --- app.get('/api/stacks', async (req, res) => { try { - console.log("📦 Stacks werden von Portainer abgefragt …"); const stacksRes = await axiosInstance.get('/api/stacks'); - + // Filter nach Endpoint-ID const filteredStacks = stacksRes.data.filter(stack => stack.EndpointId === ENDPOINT_ID); - // Deduplication nach Name + // Deduplication nach Name: nur einmal pro Name const uniqueStacksMap = {}; filteredStacks.forEach(stack => { if (!uniqueStacksMap[stack.Name]) { @@ -91,17 +90,13 @@ app.get('/api/stacks', async (req, res) => { stacksWithStatus.sort((a, b) => a.Name.localeCompare(b.Name)); res.json(stacksWithStatus); } catch (err) { - console.error("❌ Fehler beim Laden der Stacks:", err.message); res.status(500).json({ error: err.message }); } }); app.put('/api/stacks/:id/redeploy', async (req, res) => { const { id } = req.params; - const startTime = Date.now(); - try { - console.log(`🟢 Redeploy gestartet für Stack ${id} …`); broadcastRedeployStatus(id, true); const stackRes = await axiosInstance.get(`/api/stacks/${id}`); @@ -113,11 +108,8 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { } if (stack.Type === 1) { - console.log(`📂 Git-Stack erkannt (${stack.Name}) → Git-Redeploy`); await axiosInstance.put(`/api/stacks/${id}/git/redeploy?endpointId=${stack.EndpointId}`); } else if (stack.Type === 2) { - console.log(`🐳 Docker-Compose-Stack erkannt (${stack.Name}) → Images pullen & neu deployen`); - const fileRes = await axiosInstance.get(`/api/stacks/${id}/file`); const stackFileContent = fileRes.data?.StackFileContent; if (!stackFileContent) throw new Error("Stack file konnte nicht geladen werden"); @@ -127,29 +119,21 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { const imageName = services[serviceName].image; if (!imageName) continue; try { - console.log(`⬇️ Pulling image: ${imageName}`); await axiosInstance.post( `/api/endpoints/${stack.EndpointId}/docker/images/create?fromImage=${encodeURIComponent(imageName)}` ); - } catch (err) { - console.warn(`⚠️ Konnte Image ${imageName} nicht pullen:`, err.message); - } + } catch {} } - await axiosInstance.put( - `/api/stacks/${id}`, + await axiosInstance.put(`/api/stacks/${id}`, { StackFileContent: stackFileContent, Prune: false, PullImage: true }, { params: { endpointId: stack.EndpointId } } ); } - const duration = ((Date.now() - startTime) / 1000).toFixed(1); - console.log(`✅ Redeploy erfolgreich abgeschlossen für Stack ${id} (${stack.Name}) in ${duration}s`); - broadcastRedeployStatus(id, false); res.json({ success: true, message: 'Stack redeployed' }); } catch (err) { - console.error(`❌ Redeploy-Fehler für Stack ${id}:`, err.message); broadcastRedeployStatus(id, false); res.status(500).json({ error: err.message }); } @@ -157,5 +141,5 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { // Server starten server.listen(PORT, '0.0.0.0', () => { - console.log(`🚀 Backend läuft auf Port ${PORT}`); + console.log(`Backend läuft auf Port ${PORT}`); }); diff --git a/backend/index.js.bak b/backend/index.js.bak index 1a26dfc..c9f6f97 100644 --- a/backend/index.js.bak +++ b/backend/index.js.bak @@ -48,7 +48,7 @@ const server = http.createServer(app); const io = new Server(server, { cors: { origin: "*" } }); io.on("connection", (socket) => { - console.log("Client verbunden:", socket.id); + console.log("🔌 Client verbunden:", socket.id); }); const broadcastRedeployStatus = (stackId, status) => { @@ -59,12 +59,13 @@ const broadcastRedeployStatus = (stackId, status) => { // --- API Endpoints --- app.get('/api/stacks', async (req, res) => { try { + console.log("📦 Stacks werden von Portainer abgefragt …"); const stacksRes = await axiosInstance.get('/api/stacks'); - + // Filter nach Endpoint-ID const filteredStacks = stacksRes.data.filter(stack => stack.EndpointId === ENDPOINT_ID); - // Deduplication nach Name: nur einmal pro Name + // Deduplication nach Name const uniqueStacksMap = {}; filteredStacks.forEach(stack => { if (!uniqueStacksMap[stack.Name]) { @@ -90,13 +91,17 @@ app.get('/api/stacks', async (req, res) => { stacksWithStatus.sort((a, b) => a.Name.localeCompare(b.Name)); res.json(stacksWithStatus); } catch (err) { + console.error("❌ Fehler beim Laden der Stacks:", err.message); res.status(500).json({ error: err.message }); } }); app.put('/api/stacks/:id/redeploy', async (req, res) => { const { id } = req.params; + const startTime = Date.now(); + try { + console.log(`🟢 Redeploy gestartet für Stack ${id} …`); broadcastRedeployStatus(id, true); const stackRes = await axiosInstance.get(`/api/stacks/${id}`); @@ -108,8 +113,11 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { } if (stack.Type === 1) { + console.log(`📂 Git-Stack erkannt (${stack.Name}) → Git-Redeploy`); await axiosInstance.put(`/api/stacks/${id}/git/redeploy?endpointId=${stack.EndpointId}`); } else if (stack.Type === 2) { + console.log(`🐳 Docker-Compose-Stack erkannt (${stack.Name}) → Images pullen & neu deployen`); + const fileRes = await axiosInstance.get(`/api/stacks/${id}/file`); const stackFileContent = fileRes.data?.StackFileContent; if (!stackFileContent) throw new Error("Stack file konnte nicht geladen werden"); @@ -119,21 +127,29 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { const imageName = services[serviceName].image; if (!imageName) continue; try { + console.log(`⬇️ Pulling image: ${imageName}`); await axiosInstance.post( `/api/endpoints/${stack.EndpointId}/docker/images/create?fromImage=${encodeURIComponent(imageName)}` ); - } catch {} + } catch (err) { + console.warn(`⚠️ Konnte Image ${imageName} nicht pullen:`, err.message); + } } - await axiosInstance.put(`/api/stacks/${id}`, + await axiosInstance.put( + `/api/stacks/${id}`, { StackFileContent: stackFileContent, Prune: false, PullImage: true }, { params: { endpointId: stack.EndpointId } } ); } + const duration = ((Date.now() - startTime) / 1000).toFixed(1); + console.log(`✅ Redeploy erfolgreich abgeschlossen für Stack ${id} (${stack.Name}) in ${duration}s`); + broadcastRedeployStatus(id, false); res.json({ success: true, message: 'Stack redeployed' }); } catch (err) { + console.error(`❌ Redeploy-Fehler für Stack ${id}:`, err.message); broadcastRedeployStatus(id, false); res.status(500).json({ error: err.message }); } @@ -141,5 +157,5 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { // Server starten server.listen(PORT, '0.0.0.0', () => { - console.log(`Backend läuft auf Port ${PORT}`); + console.log(`🚀 Backend läuft auf Port ${PORT}`); }); diff --git a/frontend/src/Stacks.jsx b/frontend/src/Stacks.jsx index 6c930a4..9560ebd 100644 --- a/frontend/src/Stacks.jsx +++ b/frontend/src/Stacks.jsx @@ -6,54 +6,37 @@ export default function Stacks() { const [stacks, setStacks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); - const [redeploying, setRedeploying] = useState({}); // { [stackId]: boolean } + const [redeploying, setRedeploying] = useState({}); - // ------------------------- - // WebSocket: connect + events - // ------------------------- + // WebSocket initialisieren useEffect(() => { - const socket = io(); // gleiche Origin - socket.on("connect", () => console.log("🔌 Socket connected:", socket.id)); + 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); - - // Map aus API-Daten aufbauen - const map = {}; - sortedStacks.forEach(s => { map[s.Id] = !!s.redeploying; }); - setRedeploying(map); } catch (err) { - console.error("Fehler beim Aktualisieren der Stacks:", err); + console.error("Fehler beim Aktualisieren des Status nach Redeploy:", err); } } }); - return () => { - socket.off("redeployStatus"); - socket.disconnect(); - }; + return () => socket.disconnect(); }, []); - // ------------------------- - // Initiale Stacks laden - // ------------------------- + // Stacks initial laden 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); - - // Map aus API-Daten setzen - 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"); @@ -66,66 +49,60 @@ export default function Stacks() { fetchStacks(); }, []); - // ------------------------- - // Redeploy Trigger - // ------------------------- + // Redeploy eines Stacks const handleRedeploy = async (stackId) => { setRedeploying(prev => ({ ...prev, [stackId]: true })); try { await axios.put(`/api/stacks/${stackId}/redeploy`); - // Backend sendet Event → UI wird dann automatisch zurückgesetzt } catch (err) { console.error("❌ Fehler beim Redeploy:", err); setRedeploying(prev => ({ ...prev, [stackId]: false })); } }; - // ------------------------- - // Render - // ------------------------- if (loading) return
Lade Stacks...
; if (error) return{error}
; return ({stack.Name}
ID: {stack.Id}
Keine Stacks gefunden.
} );