feat(deploy): async compose-up for instant UI feedback (v0.28.2)

Deploy API now returns immediately after validation + config save.
docker compose up -d runs in a background goroutine so the UI shows
progress during image pulls instead of blocking for 30-60s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 12:08:08 +01:00
parent 4a6ab4d61c
commit 563cf07ec8
6 changed files with 70 additions and 16 deletions
+4 -2
View File
@@ -38,7 +38,7 @@ func (s *Server) templateFuncMap() template.FuncMap {
switch state {
case stacks.StateRunning:
return "green"
case stacks.StateStarting:
case stacks.StateStarting, stacks.StateDeploying:
return "orange"
case stacks.StateUnhealthy:
return "yellow"
@@ -56,6 +56,8 @@ func (s *Server) templateFuncMap() template.FuncMap {
return "Fut"
case stacks.StateStarting:
return "Indulás..."
case stacks.StateDeploying:
return "Telepítés..."
case stacks.StateUnhealthy:
return "Nem egészséges"
case stacks.StateStopped, stacks.StateExited:
@@ -74,7 +76,7 @@ func (s *Server) templateFuncMap() template.FuncMap {
switch state {
case stacks.StateRunning:
return "●"
case stacks.StateStarting:
case stacks.StateStarting, stacks.StateDeploying:
return "◐"
case stacks.StateUnhealthy:
return "◑"
+16 -1
View File
@@ -632,8 +632,23 @@ document.getElementById('deploy-form').addEventListener('submit', async function
var sd = await sr.json();
if (!sd.ok || !sd.data) return;
var state = sd.data.state;
var deployError = sd.data.deploy_error;
if (state === 'running') {
if (deployError) {
// Async compose-up failed
clearInterval(pollTimer);
setStep(stepContainers, 'error', 'Telepítés sikertelen');
setStep(stepHealth, 'error');
progressEl.querySelector('h3').textContent = 'Telepítés sikertelen';
resultEl.innerHTML = '<div class="alert alert-error" style="margin-top:1rem">' +
'A telepítés nem sikerült: ' + deployError +
'</div><a href="/stacks/' + stackName + '/logs" class="btn btn-outline" style="margin-top:.75rem">Naplók megtekintése</a>' +
' <a href="/stacks" class="btn btn-primary" style="margin-top:.75rem">Alkalmazások</a>';
resultEl.style.display = 'block';
} else if (state === 'deploying') {
// Compose up in progress (pulling images, creating containers)
setStep(stepContainers, 'active', 'Képek letöltése, konténerek indítása...');
} else if (state === 'running') {
clearInterval(pollTimer);
setStep(stepContainers, 'done', 'Konténerek elindultak');
setStep(stepHealth, 'done', 'Alkalmazás kész!');