diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index 77feddd..4ededb4 100644 Binary files a/routes/__pycache__/main.cpython-313.pyc and b/routes/__pycache__/main.cpython-313.pyc differ diff --git a/routes/main.py b/routes/main.py index ba0562f..00b7168 100644 --- a/routes/main.py +++ b/routes/main.py @@ -350,7 +350,7 @@ def init_routes(main_bp): try: # Construct the health check URL health_url = f"{instance.main_url.rstrip('/')}/health" - response = requests.get(health_url, timeout=5) + response = requests.get(health_url, timeout=30) # Increased timeout to 30 seconds if response.status_code == 200: data = response.json() @@ -397,7 +397,7 @@ def init_routes(main_bp): deployed_commit = None try: version_url = f"{instance.main_url.rstrip('/')}/api/version" - resp = requests.get(version_url, timeout=5) + resp = requests.get(version_url, timeout=30) # Increased timeout to 30 seconds if resp.status_code == 200: version_data = resp.json() deployed_version = version_data.get('version', 'unknown') @@ -419,7 +419,7 @@ def init_routes(main_bp): headers = {'Accept': 'application/json', 'Authorization': f'token {gitea_token}'} # Gitea API: /api/v1/repos/{owner}/{repo}/commits/{branch} commit_url = f"{gitea_url}/api/v1/repos/{gitea_repo}/commits/{deployed_branch}" - commit_resp = requests.get(commit_url, headers=headers, timeout=5) + commit_resp = requests.get(commit_url, headers=headers, timeout=30) # Increased timeout to 30 seconds if commit_resp.status_code == 200: latest_version = commit_resp.json().get('sha') except Exception as e: diff --git a/static/js/launch_progress.js b/static/js/launch_progress.js index fa2892a..6cbc7ba 100644 --- a/static/js/launch_progress.js +++ b/static/js/launch_progress.js @@ -571,40 +571,26 @@ async function startLaunch(data) { saveDataStep.classList.add('completed'); saveDataStep.querySelector('.step-status').textContent = 'Successfully saved instance data'; - // Add instance details - const instanceDetails = document.createElement('div'); - instanceDetails.className = 'mt-3'; - instanceDetails.innerHTML = ` -
-
-
Instance Information
-
- - - - - - - - - - - - - - - - - -
PropertyValue
Internal Port${data.port}
Domains${data.webAddresses.join(', ')}
-
-
-
- `; - saveDataStep.querySelector('.step-content').appendChild(instanceDetails); - // After saving instance data, add the health check step await updateStep(11, 'Health Check', 'Verifying instance health...'); + + // Add a progress indicator + const healthStepElement = document.querySelectorAll('.step-item')[10]; + const progressDiv = document.createElement('div'); + progressDiv.className = 'mt-2'; + progressDiv.innerHTML = ` +
+
+ 0% +
+
+ Starting health check... + `; + healthStepElement.querySelector('.step-content').appendChild(progressDiv); + const healthResult = await checkInstanceHealth(`https://${data.webAddresses[0]}`); if (!healthResult.success) { @@ -612,7 +598,7 @@ async function startLaunch(data) { } // Add a retry button if health check fails - const healthStep = document.querySelectorAll('.step-item')[10]; + const healthStepElement2 = document.querySelectorAll('.step-item')[10]; if (!healthResult.success) { const retryButton = document.createElement('button'); retryButton.className = 'btn btn-sm btn-warning mt-2'; @@ -622,15 +608,15 @@ async function startLaunch(data) { retryButton.innerHTML = ' Checking...'; const retryResult = await checkInstanceHealth(`https://${data.webAddresses[0]}`); if (retryResult.success) { - healthStep.classList.remove('failed'); - healthStep.classList.add('completed'); + healthStepElement2.classList.remove('failed'); + healthStepElement2.classList.add('completed'); retryButton.remove(); } else { retryButton.disabled = false; retryButton.innerHTML = ' Retry Health Check'; } }; - healthStep.querySelector('.step-content').appendChild(retryButton); + healthStepElement2.querySelector('.step-content').appendChild(retryButton); } // After health check, add authentication step @@ -2242,11 +2228,19 @@ async function saveInstanceData(instanceData) { } async function checkInstanceHealth(instanceUrl) { - const maxRetries = 5; + const maxRetries = 120; // 120 retries * 5 seconds = 10 minutes total + const baseDelay = 5000; // 5 seconds base delay let currentAttempt = 1; + const startTime = Date.now(); + const maxTotalTime = 10 * 60 * 1000; // 10 minutes in milliseconds while (currentAttempt <= maxRetries) { try { + // Check if we've exceeded the total timeout + if (Date.now() - startTime > maxTotalTime) { + throw new Error('Health check timeout: 10 minutes exceeded'); + } + // First get the instance ID from the database const response = await fetch('/instances'); const text = await response.text(); @@ -2280,16 +2274,31 @@ async function checkInstanceHealth(instanceUrl) { const data = await statusResponse.json(); // Update the health check step - const healthStep = document.querySelectorAll('.step-item')[8]; // Adjust index based on your steps - healthStep.classList.remove('active'); - healthStep.classList.add('completed'); - const statusText = healthStep.querySelector('.step-status'); + const healthStepElement = document.querySelectorAll('.step-item')[10]; // Adjust index based on your steps + healthStepElement.classList.remove('active'); + healthStepElement.classList.add('completed'); + const statusText = healthStepElement.querySelector('.step-status'); if (data.status === 'active') { - statusText.textContent = `Instance is healthy (Attempt ${currentAttempt}/${maxRetries})`; + const elapsedTime = Math.round((Date.now() - startTime) / 1000); + statusText.textContent = `Instance is healthy (Attempt ${currentAttempt}/${maxRetries}, ${elapsedTime}s elapsed)`; + + // Update progress bar to 100% + const progressBar = document.getElementById('healthProgress'); + const progressText = document.getElementById('healthProgressText'); + if (progressBar && progressText) { + progressBar.style.width = '100%'; + progressBar.textContent = '100%'; + progressBar.classList.remove('progress-bar-animated'); + progressBar.classList.add('bg-success'); + progressText.textContent = `Health check completed successfully in ${elapsedTime}s`; + } + return { success: true, - data: data + data: data, + attempts: currentAttempt, + elapsedTime: elapsedTime }; } else { throw new Error('Instance is not healthy'); @@ -2297,25 +2306,58 @@ async function checkInstanceHealth(instanceUrl) { } catch (error) { console.error(`Health check attempt ${currentAttempt} failed:`, error); - // Update status to show current attempt - const healthStep = document.querySelectorAll('.step-item')[8]; - const statusText = healthStep.querySelector('.step-status'); - statusText.textContent = `Health check failed (Attempt ${currentAttempt}/${maxRetries}): ${error.message}`; + // Update status to show current attempt and elapsed time + const healthStepElement = document.querySelectorAll('.step-item')[10]; + const statusText = healthStepElement.querySelector('.step-status'); + const elapsedTime = Math.round((Date.now() - startTime) / 1000); + statusText.textContent = `Health check failed (Attempt ${currentAttempt}/${maxRetries}, ${elapsedTime}s elapsed): ${error.message}`; - if (currentAttempt === maxRetries) { + // Update progress bar + const progressBar = document.getElementById('healthProgress'); + const progressText = document.getElementById('healthProgressText'); + if (progressBar && progressText) { + const progressPercent = Math.min((currentAttempt / maxRetries) * 100, 100); + progressBar.style.width = `${progressPercent}%`; + progressBar.textContent = `${Math.round(progressPercent)}%`; + progressText.textContent = `Attempt ${currentAttempt}/${maxRetries} (${elapsedTime}s elapsed)`; + } + + if (currentAttempt === maxRetries || (Date.now() - startTime > maxTotalTime)) { + // Update progress bar to show failure + if (progressBar && progressText) { + progressBar.classList.remove('progress-bar-animated'); + progressBar.classList.add('bg-danger'); + progressText.textContent = `Health check failed after ${currentAttempt} attempts (${elapsedTime}s)`; + } + return { success: false, - error: `Health check failed after ${maxRetries} attempts: ${error.message}` + error: `Health check failed after ${currentAttempt} attempts (${elapsedTime}s): ${error.message}` }; } - // Wait 5 seconds before next attempt - await new Promise(resolve => setTimeout(resolve, 5000)); + // Wait before next attempt (5 seconds base delay) + await new Promise(resolve => setTimeout(resolve, baseDelay)); currentAttempt++; + + // Update progress bar in real-time + updateHealthProgress(currentAttempt, maxRetries, elapsedTime); } } } +function updateHealthProgress(currentAttempt, maxRetries, elapsedTime) { + const progressBar = document.getElementById('healthProgress'); + const progressText = document.getElementById('healthProgressText'); + + if (progressBar && progressText) { + const progressPercent = Math.min((currentAttempt / maxRetries) * 100, 100); + progressBar.style.width = `${progressPercent}%`; + progressBar.textContent = `${Math.round(progressPercent)}%`; + progressText.textContent = `Attempt ${currentAttempt}/${maxRetries} (${elapsedTime}s elapsed)`; + } +} + async function authenticateInstance(instanceUrl, instanceId) { try { // First check if instance is already authenticated