fix launch issue

This commit is contained in:
2025-06-16 10:55:22 +02:00
parent e469db9ba6
commit 4e9a3fe139
4 changed files with 211 additions and 4 deletions

View File

@@ -7,6 +7,7 @@ Create Date: 2024-03-19 13:00:00.000000
""" """
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@@ -17,7 +18,13 @@ depends_on = None
def upgrade(): def upgrade():
op.add_column('instances', sa.Column('connection_token', sa.String(64), nullable=True, unique=True)) # Get the inspector
inspector = inspect(op.get_bind())
# Check if the column exists
columns = [col['name'] for col in inspector.get_columns('instances')]
if 'connection_token' not in columns:
op.add_column('instances', sa.Column('connection_token', sa.String(64), nullable=True, unique=True))
def downgrade(): def downgrade():

View File

@@ -841,4 +841,43 @@ def deploy_stack():
return jsonify({'error': 'Request timed out while deploying stack. The operation may still be in progress.'}), 504 return jsonify({'error': 'Request timed out while deploying stack. The operation may still be in progress.'}), 504
except Exception as e: except Exception as e:
current_app.logger.error(f"Error deploying stack: {str(e)}") current_app.logger.error(f"Error deploying stack: {str(e)}")
return jsonify({'error': str(e)}), 500
@launch_api.route('/save-instance', methods=['POST'])
@csrf.exempt
def save_instance():
try:
if not request.is_json:
return jsonify({'error': 'Request must be JSON'}), 400
data = request.get_json()
required_fields = ['name', 'port', 'domains', 'stack_id', 'stack_name', 'status', 'repository', 'branch']
if not all(field in data for field in required_fields):
missing_fields = [field for field in required_fields if field not in data]
return jsonify({'error': f'Missing required fields: {", ".join(missing_fields)}'}), 400
# Save instance data
instance_data = {
'name': data['name'],
'port': data['port'],
'domains': data['domains'],
'stack_id': data['stack_id'],
'stack_name': data['stack_name'],
'status': data['status'],
'repository': data['repository'],
'branch': data['branch'],
'created_at': datetime.utcnow().isoformat()
}
# Save to database using KeyValueSettings
KeyValueSettings.set_value(f'instance_{data["name"]}', instance_data)
return jsonify({
'message': 'Instance data saved successfully',
'data': instance_data
})
except Exception as e:
current_app.logger.error(f"Error saving instance data: {str(e)}")
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500

View File

@@ -99,6 +99,18 @@ function initializeSteps() {
</div> </div>
`; `;
stepsContainer.appendChild(stackDeployStep); stepsContainer.appendChild(stackDeployStep);
// Add Save Instance Data step
const saveDataStep = document.createElement('div');
saveDataStep.className = 'step-item';
saveDataStep.innerHTML = `
<div class="step-icon"><i class="fas fa-save"></i></div>
<div class="step-content">
<h5>Saving Instance Data</h5>
<p class="step-status">Storing instance information...</p>
</div>
`;
stepsContainer.appendChild(saveDataStep);
} }
async function startLaunch(data) { async function startLaunch(data) {
@@ -250,7 +262,10 @@ async function startLaunch(data) {
const stackDeployStep = document.querySelectorAll('.step-item')[6]; const stackDeployStep = document.querySelectorAll('.step-item')[6];
stackDeployStep.classList.remove('active'); stackDeployStep.classList.remove('active');
stackDeployStep.classList.add('completed'); stackDeployStep.classList.add('completed');
stackDeployStep.querySelector('.step-status').textContent = 'Successfully deployed stack'; stackDeployStep.querySelector('.step-status').textContent =
stackResult.data.status === 'existing' ?
'Using existing stack' :
'Successfully deployed stack';
// Add stack details // Add stack details
const stackDetails = document.createElement('div'); const stackDetails = document.createElement('div');
@@ -279,7 +294,9 @@ async function startLaunch(data) {
<tr> <tr>
<td>Status</td> <td>Status</td>
<td> <td>
<span class="badge bg-success">Deployed</span> <span class="badge bg-${stackResult.data.status === 'existing' ? 'info' : 'success'}">
${stackResult.data.status === 'existing' ? 'Existing' : 'Deployed'}
</span>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -290,7 +307,70 @@ async function startLaunch(data) {
`; `;
stackDeployStep.querySelector('.step-content').appendChild(stackDetails); stackDeployStep.querySelector('.step-content').appendChild(stackDetails);
// Save instance data
await updateStep(8, 'Saving Instance Data', 'Storing instance information...');
try {
const instanceData = {
name: data.instanceName,
port: data.port,
domains: data.webAddresses,
stack_id: stackResult.data.id,
stack_name: stackResult.data.name,
status: stackResult.data.status,
repository: data.repository,
branch: data.branch
};
console.log('Saving instance data:', instanceData);
const saveResult = await saveInstanceData(instanceData);
console.log('Save result:', saveResult);
await updateStep(8, 'Saving Instance Data', 'Instance data saved successfully');
} catch (error) {
console.error('Error saving instance data:', error);
await updateStep(8, 'Saving Instance Data', `Error: ${error.message}`);
throw error;
}
// Update the step to show success
const saveDataStep = document.querySelectorAll('.step-item')[7];
saveDataStep.classList.remove('active');
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 = `
<div class="card">
<div class="card-body">
<h6 class="card-title mb-3">Instance Information</h6>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Property</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Internal Port</td>
<td>${data.port}</td>
</tr>
<tr>
<td>Domains</td>
<td><a href="https://${data.webAddresses.join(', ')}" target="_blank">${data.webAddresses.join(', ')}</a></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
`;
saveDataStep.querySelector('.step-content').appendChild(instanceDetails);
} catch (error) { } catch (error) {
console.error('Launch failed:', error);
await updateStep(8, 'Saving Instance Data', `Error: ${error.message}`);
showError(error.message); showError(error.message);
} }
} }
@@ -1020,4 +1100,82 @@ async function deployStack(dockerComposeContent, stackName, port) {
error: error.message error: error.message
}; };
} }
}
// Add new function to save instance data
async function saveInstanceData(instanceData) {
try {
console.log('Saving instance data:', instanceData);
// First check if instance already exists
const checkResponse = await fetch('/instances');
const text = await checkResponse.text();
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
// Look for an existing instance with the same name
const existingInstance = Array.from(doc.querySelectorAll('table tbody tr')).find(row => {
const nameCell = row.querySelector('td:first-child');
return nameCell && nameCell.textContent.trim() === instanceData.port;
});
if (existingInstance) {
console.log('Instance already exists:', instanceData.port);
return {
success: true,
data: {
name: instanceData.port,
company: 'loading...',
rooms_count: 0,
conversations_count: 0,
data_size: 0.0,
payment_plan: 'Basic',
main_url: `https://${instanceData.domains[0]}`,
status: 'inactive',
port: instanceData.port,
stack_id: instanceData.stack_id,
stack_name: instanceData.stack_name,
repository: instanceData.repository,
branch: instanceData.branch
}
};
}
// If instance doesn't exist, create it
const response = await fetch('/instances/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
name: instanceData.port,
company: 'loading...',
rooms_count: 0,
conversations_count: 0,
data_size: 0.0,
payment_plan: 'Basic',
main_url: `https://${instanceData.domains[0]}`,
status: 'inactive',
port: instanceData.port,
stack_id: instanceData.stack_id,
stack_name: instanceData.stack_name,
repository: instanceData.repository,
branch: instanceData.branch
})
});
if (!response.ok) {
const errorText = await response.text();
console.error('Error response:', errorText);
throw new Error(`Failed to save instance data: ${response.status} ${response.statusText}`);
}
const result = await response.json();
console.log('Instance data saved:', result);
return result;
} catch (error) {
console.error('Error saving instance data:', error);
throw error;
}
} }

View File

@@ -35,7 +35,7 @@
</div> </div>
<div id="stepsContainer" class="launch-steps-container"> <div id="stepsContainer" class="launch-steps-container">
<!-- Your custom steps will be added here --> <!-- Steps will be added here by JavaScript -->
</div> </div>
<div class="text-center mt-4"> <div class="text-center mt-4">
@@ -68,6 +68,9 @@
url: '{{ portainer_settings.url if portainer_settings else "" }}', url: '{{ portainer_settings.url if portainer_settings else "" }}',
api_key: '{{ portainer_settings.api_key if portainer_settings else "" }}' api_key: '{{ portainer_settings.api_key if portainer_settings else "" }}'
}; };
// Pass CSRF token to JavaScript
window.csrfToken = '{{ csrf_token }}';
</script> </script>
<script src="{{ url_for('static', filename='js/launch_progress.js') }}"></script> <script src="{{ url_for('static', filename='js/launch_progress.js') }}"></script>
{% endblock %} {% endblock %}