diff --git a/backend/index.js b/backend/index.js index c22e158..ac1612b 100644 --- a/backend/index.js +++ b/backend/index.js @@ -39,6 +39,12 @@ const axiosInstance = axios.create({ baseURL: process.env.PORTAINER_URL, }); +const REDEPLOY_STATUS = { + IDLE: 'idle', + QUEUED: 'queued', + RUNNING: 'running' +}; + const redeployingStacks = {}; const server = http.createServer(app); @@ -51,9 +57,22 @@ io.on("connection", (socket) => { }); const broadcastRedeployStatus = (stackId, status) => { - redeployingStacks[stackId] = status; - io.emit("redeployStatus", { stackId, status }); - console.log(`πŸ”„ [RedeployStatus] Stack ${stackId} ist jetzt ${status ? "im Redeploy" : "fertig"}`); + if (!status || status === REDEPLOY_STATUS.IDLE) { + delete redeployingStacks[stackId]; + } else { + redeployingStacks[stackId] = status; + } + + const payloadStatus = status || REDEPLOY_STATUS.IDLE; + io.emit("redeployStatus", { stackId, status: payloadStatus }); + + let statusText = 'fertig'; + if (payloadStatus === REDEPLOY_STATUS.RUNNING) { + statusText = 'im Redeploy'; + } else if (payloadStatus === REDEPLOY_STATUS.QUEUED) { + statusText = 'in der Warteschlange'; + } + console.log(`πŸ”„ [RedeployStatus] Stack ${stackId} ist jetzt ${statusText}`); }; const REDEPLOY_TYPES = { @@ -127,29 +146,36 @@ app.get('/api/stacks', async (req, res) => { const stacksWithStatus = await Promise.all( uniqueStacks.map(async (stack) => { + const redeployState = redeployingStacks[stack.Id] || REDEPLOY_STATUS.IDLE; + const redeployCommon = { + redeployState, + redeploying: redeployState === REDEPLOY_STATUS.RUNNING, + redeployQueued: redeployState === REDEPLOY_STATUS.QUEUED, + redeployDisabled: SELF_STACK_ID ? String(stack.Id) === SELF_STACK_ID : false + }; + try { const statusRes = await axiosInstance.get( `/api/stacks/${stack.Id}/images_status?refresh=true` ); const statusEmoji = statusRes.data.Status === 'outdated' ? '⚠️' : 'βœ…'; - return { - ...stack, - updateStatus: statusEmoji, - redeploying: redeployingStacks[stack.Id] || false, - redeployDisabled: SELF_STACK_ID ? String(stack.Id) === SELF_STACK_ID : false + return { + ...stack, + updateStatus: statusEmoji, + ...redeployCommon }; } catch (err) { console.error(`❌ Fehler beim Abrufen des Status fΓΌr Stack ${stack.Id}:`, err.message); return { ...stack, updateStatus: '❌', - redeploying: redeployingStacks[stack.Id] || false, - redeployDisabled: SELF_STACK_ID ? String(stack.Id) === SELF_STACK_ID : false + ...redeployCommon }; } }) ); + stacksWithStatus.sort((a, b) => a.Name.localeCompare(b.Name)); console.log(`βœ… GET /api/stacks: Abruf erfolgreich, ${stacksWithStatus.length} Stacks geladen`); res.json(stacksWithStatus); @@ -271,7 +297,7 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { let stack; try { - broadcastRedeployStatus(id, true); + broadcastRedeployStatus(id, REDEPLOY_STATUS.RUNNING); const stackRes = await axiosInstance.get(`/api/stacks/${id}`); stack = stackRes.data; @@ -318,7 +344,7 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { ); } - broadcastRedeployStatus(id, false); + broadcastRedeployStatus(id, REDEPLOY_STATUS.IDLE); logRedeployEvent({ stackId: stack.Id || id, stackName: stack.Name, @@ -330,7 +356,7 @@ app.put('/api/stacks/:id/redeploy', async (req, res) => { console.log(`βœ… PUT /api/stacks/${id}/redeploy: Redeploy erfolgreich abgeschlossen`); res.json({ success: true, message: 'Stack redeployed' }); } catch (err) { - broadcastRedeployStatus(id, false); + broadcastRedeployStatus(id, REDEPLOY_STATUS.IDLE); const errorMessage = err.response?.data?.message || err.message; logRedeployEvent({ stackId: stack?.Id || id, @@ -387,9 +413,13 @@ app.put('/api/stacks/redeploy-all', async (req, res) => { return res.json({ success: true, message: 'Keine veralteten Stacks fΓΌr Redeploy ALL' }); } + eligibleStacks.forEach((stack) => { + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.QUEUED); + }); + for (const stack of eligibleStacks) { try { - broadcastRedeployStatus(stack.Id, true); + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.RUNNING); logRedeployEvent({ stackId: stack.Id, stackName: stack.Name, @@ -414,7 +444,7 @@ app.put('/api/stacks/redeploy-all', async (req, res) => { } } - broadcastRedeployStatus(stack.Id, false); + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.IDLE); logRedeployEvent({ stackId: stack.Id, stackName: stack.Name, @@ -425,7 +455,7 @@ app.put('/api/stacks/redeploy-all', async (req, res) => { }); console.log(`βœ… Redeploy abgeschlossen: ${stack.Name}`); } catch (err) { - broadcastRedeployStatus(stack.Id, false); + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.IDLE); const errorMessage = err.response?.data?.message || err.message; logRedeployEvent({ stackId: stack.Id, @@ -509,9 +539,13 @@ app.put('/api/stacks/redeploy-selection', async (req, res) => { return res.json({ success: true, message: 'Keine veralteten Stacks in der Auswahl' }); } + eligibleStacks.forEach((stack) => { + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.QUEUED); + }); + for (const stack of eligibleStacks) { try { - broadcastRedeployStatus(stack.Id, true); + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.RUNNING); logRedeployEvent({ stackId: stack.Id, stackName: stack.Name, @@ -536,7 +570,7 @@ app.put('/api/stacks/redeploy-selection', async (req, res) => { } } - broadcastRedeployStatus(stack.Id, false); + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.IDLE); logRedeployEvent({ stackId: stack.Id, stackName: stack.Name, @@ -547,7 +581,7 @@ app.put('/api/stacks/redeploy-selection', async (req, res) => { }); console.log(`βœ… Redeploy Auswahl abgeschlossen: ${stack.Name}`); } catch (err) { - broadcastRedeployStatus(stack.Id, false); + broadcastRedeployStatus(stack.Id, REDEPLOY_STATUS.IDLE); const errorMessage = err.response?.data?.message || err.message; logRedeployEvent({ stackId: stack.Id, diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5b757af..c099a88 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -17,7 +17,7 @@ export default function App() {
- v0.2 + v0.2.1 StackPulse