changes to the implementation bar
This commit is contained in:
@@ -507,9 +507,8 @@
|
|||||||
formState.style.display = 'none';
|
formState.style.display = 'none';
|
||||||
loadingState.style.display = 'block';
|
loadingState.style.display = 'block';
|
||||||
|
|
||||||
// Fixiert auf genau 90 Sekunden (1:30 Min.)
|
// Startet den fixierten 90 Sekunden Prozess
|
||||||
const estimatedSec = result.estimated_seconds || 90;
|
startProvisioningProcess(tenantSlug);
|
||||||
startPolling(tenantSlug, estimatedSec);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorAlert.textContent = error.message;
|
errorAlert.textContent = error.message;
|
||||||
@@ -519,72 +518,101 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function startPolling(slug, estimatedSeconds) {
|
function startProvisioningProcess(slug) {
|
||||||
// Exakt auf die Prozessschritte aus dem Log abgestimmte Statusmeldungen für den 90s Ablauf
|
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 = [
|
const statusSteps = [
|
||||||
{ time: 0, msg: "Trigger verarbeitet: Starte Provisionierung für " + slug + "..." },
|
{ atMs: 0, msg: "[INFO] Verarbeite Trigger: Starte Provisionierung für " + slug + "..." },
|
||||||
{ time: 8, msg: "Freier Netzwerk-Port wird zugewiesen (Port 10004)..." },
|
{ atMs: 10000, msg: "[INFO] Prüfe freie Ports... Verwende Port 10004 für Tenant " + slug },
|
||||||
{ time: 18, msg: "Certbot & Let's Encrypt: SSL-Zertifikat wird angefordert..." },
|
{ atMs: 20000, msg: "Requesting a certificate for " + slug + ".invario-software.de..." },
|
||||||
{ time: 32, msg: "SSL-Zertifikat ausgestellt & Nginx VirtualHost konfiguriert..." },
|
{ atMs: 30000, msg: "Successfully deployed certificate to Nginx. Restarting app container..." },
|
||||||
{ time: 42, msg: "Starte Anwendungs-Container (Docker Stack / MongoDB / Redis)..." },
|
{ atMs: 40000, msg: "Container inventarsystem-app-1 Recreated. Waiting for MongoDB/Redis..." },
|
||||||
{ time: 54, msg: "Datenbank initialisiert. Standard-Admin wird angelegt..." },
|
{ atMs: 50000, msg: "Initializing database for " + slug + "... Default admin created." },
|
||||||
{ time: 64, msg: "Konfiguriere gebuchte Module (Bibliothek = ON)..." },
|
{ atMs: 60000, msg: "Module configurations updated successfully (library=on)..." },
|
||||||
{ time: 74, msg: "Führe automatischen Systemneustart (restart.sh) durch..." },
|
{ atMs: 70000, msg: "Rebuilding and/or restarting app container using docker-compose..." },
|
||||||
{ time: 84, msg: "Bereinige verwaiste App-Container & schließe Setup ab..." }
|
{ atMs: 80000, msg: "[INFO] Bereinige verwaiste App-Container. Cleaning up old temporary files..." }
|
||||||
];
|
];
|
||||||
|
|
||||||
let msgIdx = 0;
|
// 1. Hintergrund-Polling: Fragt den echten Status ab, OHNE die UI abzubrechen
|
||||||
statusText.textContent = statusSteps[0].msg;
|
const checkBackend = async () => {
|
||||||
|
if (isFailed) return;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/instance_request/status?slug=${slug}`);
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
// Zeit-getriebenes Update der Status-Texte
|
// Wenn Backend fertig ist, Daten merken, aber Timer weiterlaufen lassen!
|
||||||
const msgInterval = setInterval(() => {
|
if (data.status === 'ready' || data.status === 'active') {
|
||||||
msgIdx++;
|
backendData = data;
|
||||||
if (msgIdx < statusSteps.length) {
|
} else if (data.status === 'failed' || data.status === 'error') {
|
||||||
statusText.textContent = statusSteps[msgIdx].msg;
|
isFailed = true;
|
||||||
|
handleError("Fehler bei der Bereitstellung auf dem Server gemeldet.");
|
||||||
} else {
|
} else {
|
||||||
clearInterval(msgInterval);
|
// Wenn noch nicht fertig, in 4 Sekunden nochmal fragen
|
||||||
|
if (!backendData) setTimeout(checkBackend, 4000);
|
||||||
}
|
}
|
||||||
}, (estimatedSeconds * 1000) / statusSteps.length);
|
} catch (err) {
|
||||||
|
if (!backendData) setTimeout(checkBackend, 4000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Fortschrittsbalken-Animation (0% -> 99% gleichmäßig verteilt über 90 Sekunden)
|
// Starte das Polling nach 5 Sekunden
|
||||||
let elapsedMs = 0;
|
setTimeout(checkBackend, 5000);
|
||||||
const totalMs = estimatedSeconds * 1000;
|
|
||||||
|
|
||||||
const progressInterval = setInterval(() => {
|
// 2. Der strikte visuelle 90-Sekunden Timer (läuft in einer Endlosschleife bis die 90s voll sind)
|
||||||
elapsedMs += 200;
|
const uiInterval = setInterval(() => {
|
||||||
let currentPercent = Math.min(99, Math.floor((elapsedMs / totalMs) * 100));
|
if (isFailed) {
|
||||||
progressBar.style.width = currentPercent + '%';
|
clearInterval(uiInterval);
|
||||||
progressPercent.textContent = currentPercent + '%';
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
// 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.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
let elapsedMs = Date.now() - startTime;
|
||||||
const res = await fetch(`/instance_request/status?slug=${slug}`);
|
|
||||||
if (!res.ok) throw new Error("Netzwerkfehler");
|
|
||||||
|
|
||||||
const data = await res.json();
|
// 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') {
|
// B. Den passenden Text basierend auf der vergangenen Zeit heraussuchen
|
||||||
clearInterval(msgInterval);
|
let currentStep = statusSteps.slice().reverse().find(step => elapsedMs >= step.atMs);
|
||||||
clearInterval(progressInterval);
|
if (currentStep && statusText.textContent !== currentStep.msg) {
|
||||||
|
statusText.textContent = currentStep.msg;
|
||||||
|
}
|
||||||
|
|
||||||
// Abschlussanimation auf 100%
|
// 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.width = '100%';
|
||||||
progressBar.style.background = '#1b8a3e';
|
progressBar.style.background = '#1b8a3e';
|
||||||
progressPercent.textContent = '100%';
|
progressPercent.textContent = '100%';
|
||||||
statusText.textContent = "[SUCCESS] Tenant bereitgestellt: " + slug + " (Port: 10004)";
|
statusText.textContent = "[SUCCESS] System erfolgreich neu gestartet für " + slug + ".";
|
||||||
statusText.style.color = '#1b8a3e';
|
statusText.style.color = '#1b8a3e';
|
||||||
|
|
||||||
|
// Nach 1,5 Sekunden auf den Success-Screen wechseln
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showSuccessState(
|
showSuccessState(
|
||||||
data.url || `https://${slug}.invario-software.de`,
|
data.url || `https://${slug}.invario-software.de`,
|
||||||
@@ -592,21 +620,12 @@
|
|||||||
data.password || "admin123",
|
data.password || "admin123",
|
||||||
data.invoice_url
|
data.invoice_url
|
||||||
);
|
);
|
||||||
}, 1000);
|
}, 1500);
|
||||||
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// 4. Fehler-Behandlung (bricht sofort ab)
|
||||||
function handleError(message) {
|
function handleError(message) {
|
||||||
clearInterval(msgInterval);
|
clearInterval(uiInterval);
|
||||||
clearInterval(progressInterval);
|
|
||||||
statusText.textContent = message;
|
statusText.textContent = message;
|
||||||
statusText.style.color = '#b02a37';
|
statusText.style.color = '#b02a37';
|
||||||
progressBar.style.background = '#b02a37';
|
progressBar.style.background = '#b02a37';
|
||||||
@@ -615,9 +634,6 @@
|
|||||||
statusText.insertAdjacentHTML('afterend', '<br><button id="retry-btn" onclick="window.location.reload();" class="btn secondary" style="margin-top:1.5rem;">Seite neu laden</button>');
|
statusText.insertAdjacentHTML('afterend', '<br><button id="retry-btn" onclick="window.location.reload();" class="btn secondary" style="margin-top:1.5rem;">Seite neu laden</button>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erstes Polling nach 5 Sekunden starten
|
|
||||||
setTimeout(checkStatus, 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSuccessState(url, user, pass, invoiceUrl) {
|
function showSuccessState(url, user, pass, invoiceUrl) {
|
||||||
|
|||||||
Reference in New Issue
Block a user