instance launching (fake)
This commit is contained in:
Binary file not shown.
@@ -1672,3 +1672,13 @@ def init_routes(main_bp):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({'error': f'Connection failed: {str(e)}'}), 400
|
return jsonify({'error': f'Connection failed: {str(e)}'}), 400
|
||||||
|
|
||||||
|
@main_bp.route('/instances/launch-progress')
|
||||||
|
@login_required
|
||||||
|
@require_password_change
|
||||||
|
def launch_progress():
|
||||||
|
if not os.environ.get('MASTER', 'false').lower() == 'true':
|
||||||
|
flash('This page is only available in master instances.', 'error')
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
|
|
||||||
|
return render_template('main/launch_progress.html')
|
||||||
@@ -315,9 +315,25 @@
|
|||||||
<div class="step-pane" id="step4">
|
<div class="step-pane" id="step4">
|
||||||
<h5 class="mb-4">Port Configuration</h5>
|
<h5 class="mb-4">Port Configuration</h5>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="port" class="form-label">Port Number <span class="text-danger">*</span></label>
|
<label for="port" class="form-label">Internal Port Number <span class="text-danger">*</span></label>
|
||||||
<input type="number" class="form-control" id="port" required min="1024" max="65535">
|
<input type="number" class="form-control" id="port" required min="1024" max="65535">
|
||||||
<div class="form-text">Ports below 1024 are reserved for system use. Suggested port: <span id="suggestedPort">3000</span></div>
|
<div class="form-text">Ports below 1024 are reserved for system use. Ports are automatically assigned by the system but you can change it if you want.</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Web Addresses <span class="text-danger">*</span></label>
|
||||||
|
<div id="webAddressesContainer">
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<span class="input-group-text">https://</span>
|
||||||
|
<input type="text" class="form-control web-address" required placeholder="your-domain.com">
|
||||||
|
<button type="button" class="btn btn-outline-danger remove-address" onclick="removeWebAddress(this)" style="display: none;">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addWebAddress()">
|
||||||
|
<i class="fas fa-plus"></i> Add Another Address
|
||||||
|
</button>
|
||||||
|
<div class="form-text">Enter the domain names where your instance will be accessible</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -367,12 +383,36 @@
|
|||||||
|
|
||||||
<!-- Step 6 -->
|
<!-- Step 6 -->
|
||||||
<div class="step-pane" id="step6">
|
<div class="step-pane" id="step6">
|
||||||
<h4>Launch</h4>
|
<div class="text-center">
|
||||||
<p class="text-muted">Ready to launch your instance!</p>
|
<i class="fas fa-rocket fa-4x mb-4" style="color: var(--primary-color);"></i>
|
||||||
|
<h4>Ready to Launch!</h4>
|
||||||
|
<p class="text-muted mb-4">Review your instance configuration before launching</p>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3">Instance Configuration</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p><strong>Repository:</strong> <span id="reviewRepo"></span></p>
|
||||||
|
<p><strong>Branch:</strong> <span id="reviewBranch"></span></p>
|
||||||
|
<p><strong>Company:</strong> <span id="reviewCompany"></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p><strong>Port:</strong> <span id="reviewPort"></span></p>
|
||||||
|
<p><strong>Web Addresses:</strong> <span id="reviewWebAddresses"></span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary btn-lg" onclick="launchInstance()">
|
||||||
|
<i class="fas fa-rocket me-2"></i> Launch Instance
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer d-flex justify-content-end" id="launchStepsFooter">
|
||||||
<button type="button" class="btn btn-secondary" onclick="previousStep()">
|
<button type="button" class="btn btn-secondary" onclick="previousStep()">
|
||||||
<i class="fas fa-arrow-left me-1"></i> Previous
|
<i class="fas fa-arrow-left me-1"></i> Previous
|
||||||
</button>
|
</button>
|
||||||
@@ -470,6 +510,16 @@
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end !important;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer button {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Edit Instance Modal -->
|
<!-- Edit Instance Modal -->
|
||||||
@@ -1290,12 +1340,24 @@ function updateStepDisplay() {
|
|||||||
item.classList.toggle('completed', step < currentStep);
|
item.classList.toggle('completed', step < currentStep);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update buttons
|
// Get the modal footer
|
||||||
const prevButton = document.querySelector('.modal-footer .btn-secondary');
|
const modalFooter = document.getElementById('launchStepsFooter');
|
||||||
const nextButton = document.querySelector('.modal-footer .btn-primary');
|
const prevButton = modalFooter.querySelector('.btn-secondary');
|
||||||
|
const nextButton = modalFooter.querySelector('.btn-primary');
|
||||||
|
|
||||||
|
// Always show the footer
|
||||||
|
modalFooter.style.display = 'block';
|
||||||
|
|
||||||
|
// Show/hide Previous button based on step
|
||||||
prevButton.style.display = currentStep === 1 ? 'none' : 'inline-block';
|
prevButton.style.display = currentStep === 1 ? 'none' : 'inline-block';
|
||||||
nextButton.innerHTML = currentStep === totalSteps ? 'Launch Instance' : 'Next <i class="fas fa-arrow-right ms-1"></i>';
|
|
||||||
|
// Show/hide Next button based on step
|
||||||
|
if (currentStep === totalSteps) {
|
||||||
|
nextButton.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
nextButton.style.display = 'inline-block';
|
||||||
|
nextButton.innerHTML = 'Next <i class="fas fa-arrow-right ms-1"></i>';
|
||||||
|
}
|
||||||
|
|
||||||
// If we're on step 4, get the next available port
|
// If we're on step 4, get the next available port
|
||||||
if (currentStep === 4) {
|
if (currentStep === 4) {
|
||||||
@@ -1310,13 +1372,18 @@ function nextStep() {
|
|||||||
if (currentStep === 3 && !validateStep3()) {
|
if (currentStep === 3 && !validateStep3()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (currentStep === 4 && !validateStep4()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentStep < totalSteps) {
|
if (currentStep < totalSteps) {
|
||||||
currentStep++;
|
currentStep++;
|
||||||
updateStepDisplay();
|
updateStepDisplay();
|
||||||
} else {
|
|
||||||
// Handle final step (launch instance)
|
// Update review section if we're moving to the final step
|
||||||
launchInstance();
|
if (currentStep === totalSteps) {
|
||||||
|
updateReviewSection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1620,5 +1687,142 @@ function validateStep3() {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to validate step 4
|
||||||
|
function validateStep4() {
|
||||||
|
const port = document.getElementById('port').value;
|
||||||
|
const webAddresses = document.querySelectorAll('.web-address');
|
||||||
|
|
||||||
|
// Validate port
|
||||||
|
if (!port || port < 1024 || port > 65535) {
|
||||||
|
alert('Please enter a valid port number between 1024 and 65535');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate web addresses
|
||||||
|
if (webAddresses.length === 0) {
|
||||||
|
alert('Please add at least one web address');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic domain validation for each address
|
||||||
|
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])*\.[a-zA-Z]{2,}$/;
|
||||||
|
for (const address of webAddresses) {
|
||||||
|
if (!address.value) {
|
||||||
|
alert('Please fill in all web addresses');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!domainRegex.test(address.value)) {
|
||||||
|
alert(`Please enter a valid domain name for: ${address.value}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for duplicate addresses
|
||||||
|
const addresses = Array.from(webAddresses).map(addr => addr.value.toLowerCase());
|
||||||
|
const uniqueAddresses = new Set(addresses);
|
||||||
|
if (addresses.length !== uniqueAddresses.size) {
|
||||||
|
alert('Please remove duplicate web addresses');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to add a new web address input
|
||||||
|
function addWebAddress() {
|
||||||
|
const container = document.getElementById('webAddressesContainer');
|
||||||
|
const newAddress = document.createElement('div');
|
||||||
|
newAddress.className = 'input-group mb-2';
|
||||||
|
newAddress.innerHTML = `
|
||||||
|
<span class="input-group-text">https://</span>
|
||||||
|
<input type="text" class="form-control web-address" required placeholder="your-domain.com">
|
||||||
|
<button type="button" class="btn btn-outline-danger remove-address" onclick="removeWebAddress(this)">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
container.appendChild(newAddress);
|
||||||
|
|
||||||
|
// Show remove buttons if there's more than one address
|
||||||
|
updateRemoveButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to remove a web address input
|
||||||
|
function removeWebAddress(button) {
|
||||||
|
button.closest('.input-group').remove();
|
||||||
|
updateRemoveButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update remove buttons visibility
|
||||||
|
function updateRemoveButtons() {
|
||||||
|
const addresses = document.querySelectorAll('.web-address');
|
||||||
|
const removeButtons = document.querySelectorAll('.remove-address');
|
||||||
|
|
||||||
|
// Show remove buttons only if there's more than one address
|
||||||
|
removeButtons.forEach(button => {
|
||||||
|
button.style.display = addresses.length > 1 ? 'block' : 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get all web addresses
|
||||||
|
function getAllWebAddresses() {
|
||||||
|
return Array.from(document.querySelectorAll('.web-address')).map(input => input.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update the review section
|
||||||
|
function updateReviewSection() {
|
||||||
|
// Get all the values
|
||||||
|
const repo = document.getElementById('giteaRepoSelect').value;
|
||||||
|
const branch = document.getElementById('giteaBranchSelect').value;
|
||||||
|
const company = document.getElementById('companyName').value;
|
||||||
|
const port = document.getElementById('port').value;
|
||||||
|
const webAddresses = Array.from(document.querySelectorAll('.web-address'))
|
||||||
|
.map(input => input.value)
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
// Update the review section
|
||||||
|
document.getElementById('reviewRepo').textContent = repo;
|
||||||
|
document.getElementById('reviewBranch').textContent = branch;
|
||||||
|
document.getElementById('reviewCompany').textContent = company;
|
||||||
|
document.getElementById('reviewPort').textContent = port;
|
||||||
|
document.getElementById('reviewWebAddresses').textContent = webAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to launch the instance
|
||||||
|
function launchInstance() {
|
||||||
|
// Collect all the data
|
||||||
|
const instanceData = {
|
||||||
|
repository: document.getElementById('giteaRepoSelect').value,
|
||||||
|
branch: document.getElementById('giteaBranchSelect').value,
|
||||||
|
company: {
|
||||||
|
name: document.getElementById('companyName').value,
|
||||||
|
industry: document.getElementById('industry').value,
|
||||||
|
streetAddress: document.getElementById('streetAddress').value,
|
||||||
|
city: document.getElementById('city').value,
|
||||||
|
state: document.getElementById('state').value,
|
||||||
|
zipCode: document.getElementById('zipCode').value,
|
||||||
|
country: document.getElementById('country').value,
|
||||||
|
website: document.getElementById('website').value,
|
||||||
|
email: document.getElementById('email').value,
|
||||||
|
phone: document.getElementById('phone').value,
|
||||||
|
description: document.getElementById('companyDescription').value
|
||||||
|
},
|
||||||
|
port: document.getElementById('port').value,
|
||||||
|
webAddresses: Array.from(document.querySelectorAll('.web-address')).map(input => input.value),
|
||||||
|
colors: {
|
||||||
|
primary: document.getElementById('primaryColor').value,
|
||||||
|
secondary: document.getElementById('secondaryColor').value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store the data in sessionStorage
|
||||||
|
sessionStorage.setItem('instanceLaunchData', JSON.stringify(instanceData));
|
||||||
|
|
||||||
|
// Close the modal
|
||||||
|
launchStepsModal.hide();
|
||||||
|
|
||||||
|
// Redirect to the launch progress page
|
||||||
|
window.location.href = '/instances/launch-progress';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
344
templates/main/launch_progress.html
Normal file
344
templates/main/launch_progress.html
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
{% extends "common/base.html" %}
|
||||||
|
{% from "components/header.html" import header %}
|
||||||
|
|
||||||
|
{% block title %}Launching Instance - DocuPulse{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{{ header(
|
||||||
|
title="Launching Instance",
|
||||||
|
description="Setting up your new DocuPulse instance",
|
||||||
|
icon="fa-rocket"
|
||||||
|
) }}
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<div class="spinner-border text-primary mb-3" role="status" style="width: 3rem; height: 3rem;">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
<h4 id="currentStep">Initializing...</h4>
|
||||||
|
<p class="text-muted" id="stepDescription">Preparing to launch your instance</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="progress mb-4" style="height: 25px;">
|
||||||
|
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
||||||
|
role="progressbar"
|
||||||
|
id="launchProgress"
|
||||||
|
style="width: 0%">0%</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="launch-steps">
|
||||||
|
<div class="step-item" data-step="1">
|
||||||
|
<div class="step-icon"><i class="fas fa-code-branch"></i></div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Cloning Repository</h5>
|
||||||
|
<p class="step-status">Waiting...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-item" data-step="2">
|
||||||
|
<div class="step-icon"><i class="fas fa-server"></i></div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Creating Container</h5>
|
||||||
|
<p class="step-status">Waiting...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-item" data-step="3">
|
||||||
|
<div class="step-icon"><i class="fas fa-network-wired"></i></div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Configuring Network</h5>
|
||||||
|
<p class="step-status">Waiting...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-item" data-step="4">
|
||||||
|
<div class="step-icon"><i class="fas fa-database"></i></div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Setting Up Database</h5>
|
||||||
|
<p class="step-status">Waiting...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-item" data-step="5">
|
||||||
|
<div class="step-icon"><i class="fas fa-paint-brush"></i></div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Applying Customization</h5>
|
||||||
|
<p class="step-status">Waiting...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-item" data-step="6">
|
||||||
|
<div class="step-icon"><i class="fas fa-check-circle"></i></div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h5>Finalizing Setup</h5>
|
||||||
|
<p class="step-status">Waiting...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-4">
|
||||||
|
<div id="errorContainer" class="alert alert-danger" style="display: none;">
|
||||||
|
<h5><i class="fas fa-exclamation-triangle"></i> Error</h5>
|
||||||
|
<p id="errorMessage"></p>
|
||||||
|
<button class="btn btn-outline-danger" onclick="retryLaunch()">
|
||||||
|
<i class="fas fa-redo"></i> Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.launch-steps {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.active {
|
||||||
|
background-color: #e3f2fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.completed {
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.failed {
|
||||||
|
background-color: #ffebee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #e9ecef;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 1rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.active .step-icon {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.completed .step-icon {
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.failed .step-icon {
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content h5 {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-status {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.completed .step-status {
|
||||||
|
color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item.failed .step-status {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Get the launch data from sessionStorage
|
||||||
|
const launchData = JSON.parse(sessionStorage.getItem('instanceLaunchData'));
|
||||||
|
if (!launchData) {
|
||||||
|
showError('No launch data found. Please start over.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the launch process
|
||||||
|
startLaunch(launchData);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function startLaunch(data) {
|
||||||
|
try {
|
||||||
|
// Step 1: Clone Repository
|
||||||
|
await updateStep(1, 'Cloning Repository', 'Fetching code from repository...');
|
||||||
|
const cloneResult = await cloneRepository(data.repository, data.branch);
|
||||||
|
if (!cloneResult.success) throw new Error(cloneResult.error);
|
||||||
|
|
||||||
|
// Step 2: Create Container
|
||||||
|
await updateStep(2, 'Creating Container', 'Setting up Docker container...');
|
||||||
|
const containerResult = await createContainer(data);
|
||||||
|
if (!containerResult.success) throw new Error(containerResult.error);
|
||||||
|
|
||||||
|
// Step 3: Configure Network
|
||||||
|
await updateStep(3, 'Configuring Network', 'Setting up network and ports...');
|
||||||
|
const networkResult = await configureNetwork(data);
|
||||||
|
if (!networkResult.success) throw new Error(networkResult.error);
|
||||||
|
|
||||||
|
// Step 4: Setup Database
|
||||||
|
await updateStep(4, 'Setting Up Database', 'Initializing database...');
|
||||||
|
const dbResult = await setupDatabase(data);
|
||||||
|
if (!dbResult.success) throw new Error(dbResult.error);
|
||||||
|
|
||||||
|
// Step 5: Apply Customization
|
||||||
|
await updateStep(5, 'Applying Customization', 'Applying your custom settings...');
|
||||||
|
const customResult = await applyCustomization(data);
|
||||||
|
if (!customResult.success) throw new Error(customResult.error);
|
||||||
|
|
||||||
|
// Step 6: Finalize
|
||||||
|
await updateStep(6, 'Finalizing Setup', 'Completing the setup...');
|
||||||
|
const finalResult = await finalizeSetup(data);
|
||||||
|
if (!finalResult.success) throw new Error(finalResult.error);
|
||||||
|
|
||||||
|
// Launch complete
|
||||||
|
completeLaunch();
|
||||||
|
} catch (error) {
|
||||||
|
showError(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStep(stepNumber, title, description) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// Update the current step in the header
|
||||||
|
document.getElementById('currentStep').textContent = title;
|
||||||
|
document.getElementById('stepDescription').textContent = description;
|
||||||
|
|
||||||
|
// Update progress bar
|
||||||
|
const progress = (stepNumber - 1) * 20;
|
||||||
|
const progressBar = document.getElementById('launchProgress');
|
||||||
|
progressBar.style.width = `${progress}%`;
|
||||||
|
progressBar.textContent = `${progress}%`;
|
||||||
|
|
||||||
|
// Update step items
|
||||||
|
document.querySelectorAll('.step-item').forEach((item, index) => {
|
||||||
|
const step = index + 1;
|
||||||
|
item.classList.remove('active', 'completed', 'failed');
|
||||||
|
|
||||||
|
if (step < stepNumber) {
|
||||||
|
item.classList.add('completed');
|
||||||
|
item.querySelector('.step-status').textContent = 'Completed';
|
||||||
|
} else if (step === stepNumber) {
|
||||||
|
item.classList.add('active');
|
||||||
|
item.querySelector('.step-status').textContent = description;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simulate some work being done
|
||||||
|
setTimeout(resolve, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(message) {
|
||||||
|
const errorContainer = document.getElementById('errorContainer');
|
||||||
|
const errorMessage = document.getElementById('errorMessage');
|
||||||
|
|
||||||
|
errorMessage.textContent = message;
|
||||||
|
errorContainer.style.display = 'block';
|
||||||
|
|
||||||
|
// Update the current step to show error
|
||||||
|
const currentStep = document.querySelector('.step-item.active');
|
||||||
|
if (currentStep) {
|
||||||
|
currentStep.classList.add('failed');
|
||||||
|
currentStep.querySelector('.step-status').textContent = 'Failed: ' + message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeLaunch() {
|
||||||
|
// Update progress to 100%
|
||||||
|
const progressBar = document.getElementById('launchProgress');
|
||||||
|
progressBar.style.width = '100%';
|
||||||
|
progressBar.textContent = '100%';
|
||||||
|
|
||||||
|
// Mark all steps as completed
|
||||||
|
document.querySelectorAll('.step-item').forEach(item => {
|
||||||
|
item.classList.add('completed');
|
||||||
|
item.querySelector('.step-status').textContent = 'Completed';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update header
|
||||||
|
document.getElementById('currentStep').textContent = 'Launch Complete!';
|
||||||
|
document.getElementById('stepDescription').textContent = 'Your instance is ready to use';
|
||||||
|
|
||||||
|
// Show success message and redirect button
|
||||||
|
const successMessage = document.createElement('div');
|
||||||
|
successMessage.className = 'alert alert-success text-center mt-4';
|
||||||
|
successMessage.innerHTML = `
|
||||||
|
<h5><i class="fas fa-check-circle"></i> Success!</h5>
|
||||||
|
<p>Your instance has been successfully launched.</p>
|
||||||
|
<a href="/instances" class="btn btn-success">
|
||||||
|
<i class="fas fa-arrow-right"></i> Go to Instances
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
document.querySelector('.card-body').appendChild(successMessage);
|
||||||
|
|
||||||
|
// Clear the launch data from sessionStorage
|
||||||
|
sessionStorage.removeItem('instanceLaunchData');
|
||||||
|
}
|
||||||
|
|
||||||
|
function retryLaunch() {
|
||||||
|
// Reload the page to start over
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock API functions (replace these with actual API calls)
|
||||||
|
async function cloneRepository(repo, branch) {
|
||||||
|
// Simulate API call
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createContainer(data) {
|
||||||
|
// Simulate API call
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function configureNetwork(data) {
|
||||||
|
// Simulate API call
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupDatabase(data) {
|
||||||
|
// Simulate API call
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyCustomization(data) {
|
||||||
|
// Simulate API call
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function finalizeSetup(data) {
|
||||||
|
// Simulate API call
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user