better 504 handling

This commit is contained in:
2025-06-23 14:24:13 +02:00
parent 1370bef1f1
commit 033f82eb2b
2 changed files with 177 additions and 24 deletions

View File

@@ -481,6 +481,24 @@ async function startLaunch(data) {
// Step 9: Deploy Stack
await updateStep(9, 'Deploying Stack', 'Launching your application stack...');
// Add progress indicator for stack deployment
const stackDeployStepElement = document.querySelectorAll('.step-item')[8];
const stackProgressDiv = document.createElement('div');
stackProgressDiv.className = 'mt-2';
stackProgressDiv.innerHTML = `
<div class="progress" style="height: 20px;">
<div class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
style="width: 0%"
id="stackProgress">
0%
</div>
</div>
<small class="text-muted" id="stackProgressText">Initiating stack deployment...</small>
`;
stackDeployStepElement.querySelector('.step-content').appendChild(stackProgressDiv);
const stackResult = await deployStack(dockerComposeResult.content, data.instanceName, data.port);
launchReport.steps.push({
step: 'Stack Deployment',
@@ -498,6 +516,8 @@ async function startLaunch(data) {
stackDeployStep.querySelector('.step-status').textContent =
stackResult.data.status === 'existing' ?
'Using existing stack' :
stackResult.data.status === 'active' ?
'Successfully deployed and activated stack' :
'Successfully deployed stack';
// Add stack details
@@ -522,13 +542,13 @@ async function startLaunch(data) {
</tr>
<tr>
<td>Stack ID</td>
<td>${stackResult.data.id}</td>
<td>${stackResult.data.id || 'Will be determined during deployment'}</td>
</tr>
<tr>
<td>Status</td>
<td>
<span class="badge bg-${stackResult.data.status === 'existing' ? 'info' : 'success'}">
${stackResult.data.status === 'existing' ? 'Existing' : 'Deployed'}
<span class="badge bg-${stackResult.data.status === 'existing' ? 'info' : stackResult.data.status === 'active' ? 'success' : 'warning'}">
${stackResult.data.status === 'existing' ? 'Existing' : stackResult.data.status === 'active' ? 'Active' : 'Deployed'}
</span>
</td>
</tr>
@@ -547,7 +567,7 @@ async function startLaunch(data) {
name: data.instanceName,
port: data.port,
domains: data.webAddresses,
stack_id: stackResult.data.id,
stack_id: stackResult.data.id || null, // May be null if we got a 504
stack_name: stackResult.data.name,
status: stackResult.data.status,
repository: data.repository,
@@ -2006,7 +2026,7 @@ async function saveInstanceData(instanceData) {
main_url: `https://${instanceData.domains[0]}`,
status: 'inactive',
port: instanceData.port,
stack_id: instanceData.stack_id,
stack_id: instanceData.stack_id || '', // Use empty string if null
stack_name: instanceData.stack_name,
repository: instanceData.repository,
branch: instanceData.branch
@@ -2031,7 +2051,7 @@ async function saveInstanceData(instanceData) {
main_url: `https://${instanceData.domains[0]}`,
status: 'inactive',
port: instanceData.port,
stack_id: instanceData.stack_id,
stack_id: instanceData.stack_id || '', // Use empty string if null
stack_name: instanceData.stack_name,
repository: instanceData.repository,
branch: instanceData.branch
@@ -2536,9 +2556,6 @@ async function checkStackExists(stackName) {
async function deployStack(dockerComposeContent, stackName, port) {
try {
// First, attempt to deploy the stack
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10 * 60 * 1000); // 10 minutes timeout
const response = await fetch('/api/admin/deploy-stack', {
method: 'POST',
headers: {
@@ -2574,22 +2591,41 @@ async function deployStack(dockerComposeContent, stackName, port) {
value: new Date().toISOString()
}
]
}),
signal: controller.signal
})
});
clearTimeout(timeoutId); // Clear the timeout if the request completes
// Handle 504 Gateway Timeout as successful initiation
if (response.status === 504) {
console.log('Received 504 Gateway Timeout - stack creation may still be in progress');
return {
success: true,
data: {
name: stackName,
id: null, // Will be determined during polling
status: 'creating'
}
};
}
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to deploy stack');
}
// Return success result with response data
const result = await response.json();
console.log('Stack deployment initiated:', result);
// If stack is being created, poll for status
if (result.data.status === 'creating') {
console.log('Stack is being created, polling for status...');
const pollResult = await pollStackStatus(stackName, 10 * 60 * 1000); // 10 minutes max
return pollResult;
}
// Return success result with response data
return {
success: true,
data: result
data: result.data
};
} catch (error) {
@@ -2599,4 +2635,113 @@ async function deployStack(dockerComposeContent, stackName, port) {
error: error.message
};
}
}
// Function to poll stack status
async function pollStackStatus(stackName, maxWaitTime = 10 * 60 * 1000) {
const startTime = Date.now();
const pollInterval = 5000; // 5 seconds
let attempts = 0;
console.log(`Starting to poll stack status for: ${stackName}`);
// Update progress indicator
const progressBar = document.getElementById('stackProgress');
const progressText = document.getElementById('stackProgressText');
while (Date.now() - startTime < maxWaitTime) {
attempts++;
console.log(`Polling attempt ${attempts} for stack: ${stackName}`);
// Update progress
const elapsed = Date.now() - startTime;
const progress = Math.min((elapsed / maxWaitTime) * 100, 95); // Cap at 95% until complete
if (progressBar && progressText) {
progressBar.style.width = `${progress}%`;
progressBar.textContent = `${Math.round(progress)}%`;
progressText.textContent = `Checking stack status (attempt ${attempts})...`;
}
try {
const response = await fetch('/api/admin/check-stack-status', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
stack_name: stackName
})
});
if (response.ok) {
const result = await response.json();
console.log(`Stack status check result:`, result);
if (result.data && result.data.status === 'active') {
console.log(`Stack ${stackName} is now active!`);
// Update progress to 100%
if (progressBar && progressText) {
progressBar.style.width = '100%';
progressBar.textContent = '100%';
progressText.textContent = 'Stack is now active!';
}
return {
success: true,
data: {
name: stackName,
id: result.data.stack_id,
status: 'active'
}
};
} else if (result.data && result.data.status === 'partial') {
console.log(`Stack ${stackName} is partially running, continuing to poll...`);
if (progressText) {
progressText.textContent = `Stack is partially running (attempt ${attempts})...`;
}
} else if (result.data && result.data.status === 'inactive') {
console.log(`Stack ${stackName} is inactive, continuing to poll...`);
if (progressText) {
progressText.textContent = `Stack is starting up (attempt ${attempts})...`;
}
} else {
console.log(`Stack ${stackName} status unknown, continuing to poll...`);
if (progressText) {
progressText.textContent = `Checking stack status (attempt ${attempts})...`;
}
}
} else if (response.status === 404) {
console.log(`Stack ${stackName} not found yet, continuing to poll...`);
if (progressText) {
progressText.textContent = `Stack not found yet (attempt ${attempts})...`;
}
} else {
console.log(`Stack status check failed with status ${response.status}, continuing to poll...`);
if (progressText) {
progressText.textContent = `Status check failed (attempt ${attempts})...`;
}
}
} catch (error) {
console.error(`Error polling stack status (attempt ${attempts}):`, error);
if (progressText) {
progressText.textContent = `Error checking status (attempt ${attempts})...`;
}
}
// Wait before next poll
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
// If we get here, we've timed out
console.error(`Stack status polling timed out after ${maxWaitTime / 1000} seconds`);
if (progressBar && progressText) {
progressBar.style.width = '100%';
progressBar.classList.remove('progress-bar-animated');
progressBar.classList.add('bg-warning');
progressText.textContent = 'Stack deployment timed out';
}
return {
success: false,
error: `Stack deployment timed out after ${maxWaitTime / 1000} seconds. The stack may still be deploying.`
};
}