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) {