diff --git a/Website/templates/schnell_buchung.html b/Website/templates/schnell_buchung.html index 267dc44..9ebdec3 100644 --- a/Website/templates/schnell_buchung.html +++ b/Website/templates/schnell_buchung.html @@ -507,9 +507,8 @@ formState.style.display = 'none'; loadingState.style.display = 'block'; - // Fixiert auf genau 90 Sekunden (1:30 Min.) - const estimatedSec = result.estimated_seconds || 90; - startPolling(tenantSlug, estimatedSec); + // Startet den fixierten 90 Sekunden Prozess + startProvisioningProcess(tenantSlug); } catch (error) { errorAlert.textContent = error.message; @@ -519,94 +518,114 @@ } }); - function startPolling(slug, estimatedSeconds) { - // Exakt auf die Prozessschritte aus dem Log abgestimmte Statusmeldungen für den 90s Ablauf + function startProvisioningProcess(slug) { + const totalDurationMs = 90 * 1000; // Exakt 1:30 Minuten (90 Sekunden) + let startTime = Date.now(); + let backendData = null; // Speichert das finale Backend-Resultat + let isFailed = false; + + // Echte Log-Meldungen, die exakt alle 10 Sekunden durchgeschaltet werden const statusSteps = [ - { time: 0, msg: "Trigger verarbeitet: Starte Provisionierung für " + slug + "..." }, - { time: 8, msg: "Freier Netzwerk-Port wird zugewiesen (Port 10004)..." }, - { time: 18, msg: "Certbot & Let's Encrypt: SSL-Zertifikat wird angefordert..." }, - { time: 32, msg: "SSL-Zertifikat ausgestellt & Nginx VirtualHost konfiguriert..." }, - { time: 42, msg: "Starte Anwendungs-Container (Docker Stack / MongoDB / Redis)..." }, - { time: 54, msg: "Datenbank initialisiert. Standard-Admin wird angelegt..." }, - { time: 64, msg: "Konfiguriere gebuchte Module (Bibliothek = ON)..." }, - { time: 74, msg: "Führe automatischen Systemneustart (restart.sh) durch..." }, - { time: 84, msg: "Bereinige verwaiste App-Container & schließe Setup ab..." } + { atMs: 0, msg: "[INFO] Verarbeite Trigger: Starte Provisionierung für " + slug + "..." }, + { atMs: 10000, msg: "[INFO] Prüfe freie Ports... Verwende Port 10004 für Tenant " + slug }, + { atMs: 20000, msg: "Requesting a certificate for " + slug + ".invario-software.de..." }, + { atMs: 30000, msg: "Successfully deployed certificate to Nginx. Restarting app container..." }, + { atMs: 40000, msg: "Container inventarsystem-app-1 Recreated. Waiting for MongoDB/Redis..." }, + { atMs: 50000, msg: "Initializing database for " + slug + "... Default admin created." }, + { atMs: 60000, msg: "Module configurations updated successfully (library=on)..." }, + { atMs: 70000, msg: "Rebuilding and/or restarting app container using docker-compose..." }, + { atMs: 80000, msg: "[INFO] Bereinige verwaiste App-Container. Cleaning up old temporary files..." } ]; - let msgIdx = 0; - statusText.textContent = statusSteps[0].msg; - - // Zeit-getriebenes Update der Status-Texte - const msgInterval = setInterval(() => { - msgIdx++; - if (msgIdx < statusSteps.length) { - statusText.textContent = statusSteps[msgIdx].msg; - } else { - clearInterval(msgInterval); + // 1. Hintergrund-Polling: Fragt den echten Status ab, OHNE die UI abzubrechen + const checkBackend = async () => { + if (isFailed) return; + try { + const res = await fetch(`/instance_request/status?slug=${slug}`); + const data = await res.json(); + + // Wenn Backend fertig ist, Daten merken, aber Timer weiterlaufen lassen! + if (data.status === 'ready' || data.status === 'active') { + backendData = data; + } else if (data.status === 'failed' || data.status === 'error') { + isFailed = true; + handleError("Fehler bei der Bereitstellung auf dem Server gemeldet."); + } else { + // Wenn noch nicht fertig, in 4 Sekunden nochmal fragen + if (!backendData) setTimeout(checkBackend, 4000); + } + } catch (err) { + if (!backendData) setTimeout(checkBackend, 4000); } - }, (estimatedSeconds * 1000) / statusSteps.length); - - // Fortschrittsbalken-Animation (0% -> 99% gleichmäßig verteilt über 90 Sekunden) - let elapsedMs = 0; - const totalMs = estimatedSeconds * 1000; + }; - const progressInterval = setInterval(() => { - elapsedMs += 200; - let currentPercent = Math.min(99, Math.floor((elapsedMs / totalMs) * 100)); - progressBar.style.width = currentPercent + '%'; - progressPercent.textContent = currentPercent + '%'; - }, 200); + // Starte das Polling nach 5 Sekunden + setTimeout(checkBackend, 5000); - // Polling-Logik zur Abfrage beim Backend - let retryCount = 0; - const maxRetries = 60; // 60 * 3s = 180s maximale Pufferzeit - - const checkStatus = async () => { - retryCount++; - if (retryCount > maxRetries) { - handleError("Die Bereitstellung dauert länger als erwartet. Sie erhalten eine E-Mail, sobald die Instanz bereit ist."); + // 2. Der strikte visuelle 90-Sekunden Timer (läuft in einer Endlosschleife bis die 90s voll sind) + const uiInterval = setInterval(() => { + if (isFailed) { + clearInterval(uiInterval); return; } - try { - const res = await fetch(`/instance_request/status?slug=${slug}`); - if (!res.ok) throw new Error("Netzwerkfehler"); - - const data = await res.json(); + let elapsedMs = Date.now() - startTime; + + // A. Fortschrittsbalken berechnen (geht maximal bis 99% während der Wartezeit) + let currentPercent = Math.min(99, Math.floor((elapsedMs / totalDurationMs) * 100)); + progressBar.style.width = currentPercent + '%'; + progressPercent.textContent = currentPercent + '%'; - if (data.status === 'ready' || data.status === 'active') { - clearInterval(msgInterval); - clearInterval(progressInterval); - - // Abschlussanimation auf 100% - progressBar.style.width = '100%'; - progressBar.style.background = '#1b8a3e'; - progressPercent.textContent = '100%'; - statusText.textContent = "[SUCCESS] Tenant bereitgestellt: " + slug + " (Port: 10004)"; - statusText.style.color = '#1b8a3e'; - - setTimeout(() => { - showSuccessState( - data.url || `https://${slug}.invario-software.de`, - data.username || "admin", - data.password || "admin123", - data.invoice_url - ); - }, 1000); - - } else if (data.status === 'failed' || data.status === 'error') { - handleError("Fehler bei der Bereitstellung. Bitte kontaktieren Sie den Support."); - } else { - setTimeout(checkStatus, 3000); - } - } catch (err) { - setTimeout(checkStatus, 3000); + // B. Den passenden Text basierend auf der vergangenen Zeit heraussuchen + let currentStep = statusSteps.slice().reverse().find(step => elapsedMs >= step.atMs); + if (currentStep && statusText.textContent !== currentStep.msg) { + statusText.textContent = currentStep.msg; } - }; + // C. Die vollen 90 Sekunden sind abgelaufen + if (elapsedMs >= totalDurationMs) { + clearInterval(uiInterval); // Timer stoppen + + if (backendData) { + // Backend ist fertig -> Abschluss initiieren + finishProcess(backendData); + } else { + // Fallback: Falls das Backend nach 90s ausnahmsweise noch lädt + statusText.textContent = "Warte auf finale Bestätigung des Servers (dies kann noch einen kurzen Moment dauern)..."; + const waitInterval = setInterval(() => { + if (backendData) { + clearInterval(waitInterval); + finishProcess(backendData); + } else if (isFailed) { + clearInterval(waitInterval); + } + }, 2000); + } + } + }, 100); // 100ms Interval für sehr weiche Animation des Balkens + + // 3. Abschluss-Funktion (wird nur aufgerufen, wenn 90s vorbei SIND und Backend ready IST) + function finishProcess(data) { + progressBar.style.width = '100%'; + progressBar.style.background = '#1b8a3e'; + progressPercent.textContent = '100%'; + statusText.textContent = "[SUCCESS] System erfolgreich neu gestartet für " + slug + "."; + statusText.style.color = '#1b8a3e'; + + // Nach 1,5 Sekunden auf den Success-Screen wechseln + setTimeout(() => { + showSuccessState( + data.url || `https://${slug}.invario-software.de`, + data.username || "admin", + data.password || "admin123", + data.invoice_url + ); + }, 1500); + } + + // 4. Fehler-Behandlung (bricht sofort ab) function handleError(message) { - clearInterval(msgInterval); - clearInterval(progressInterval); + clearInterval(uiInterval); statusText.textContent = message; statusText.style.color = '#b02a37'; progressBar.style.background = '#b02a37'; @@ -615,9 +634,6 @@ statusText.insertAdjacentHTML('afterend', '
'); } } - - // Erstes Polling nach 5 Sekunden starten - setTimeout(checkStatus, 5000); } function showSuccessState(url, user, pass, invoiceUrl) {