diff --git a/app/routes/admin_api.py b/app/routes/admin_api.py new file mode 100644 index 0000000..6473c9a --- /dev/null +++ b/app/routes/admin_api.py @@ -0,0 +1,107 @@ +from flask import request, jsonify, current_app +from flask_login import login_required +import os +from datetime import datetime +from app.utils.gitea_api import GiteaAPI +from app.models.gitea_settings import GiteaSettings + +@admin_api.route('/test-git-connection', methods=['POST']) +@login_required +def test_git_connection(): + try: + data = request.get_json() + if not data: + return jsonify({'success': False, 'error': 'No data provided'}), 400 + + required_fields = ['gitea_url', 'username', 'api_token', 'repo_name'] + for field in required_fields: + if field not in data: + return jsonify({'success': False, 'error': f'Missing required field: {field}'}), 400 + + # Test the connection using the provided credentials + gitea = GiteaAPI( + url=data['gitea_url'], + token=data['api_token'] + ) + + # Try to get repository information + repo = gitea.get_repo(data['username'], data['repo_name']) + if not repo: + return jsonify({'success': False, 'error': 'Repository not found'}), 404 + + return jsonify({ + 'success': True, + 'message': 'Successfully connected to Gitea', + 'data': { + 'repo_name': repo.name, + 'description': repo.description, + 'default_branch': repo.default_branch, + 'created_at': repo.created_at, + 'updated_at': repo.updated_at + } + }) + + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + +@admin_api.route('/check-docker-compose', methods=['POST']) +@login_required +def check_docker_compose(): + try: + data = request.get_json() + if not data: + return jsonify({'success': False, 'error': 'No data provided'}), 400 + + required_fields = ['repository', 'branch'] + for field in required_fields: + if field not in data: + return jsonify({'success': False, 'error': f'Missing required field: {field}'}), 400 + + # Get Gitea settings from database + gitea_settings = GiteaSettings.query.first() + if not gitea_settings: + return jsonify({'success': False, 'error': 'Gitea settings not configured'}), 400 + + # Initialize Gitea API + gitea = GiteaAPI( + url=gitea_settings.server_url, + token=gitea_settings.api_token + ) + + # Split repository into owner and repo name + owner, repo_name = data['repository'].split('/') + + # Get the docker-compose.yml file content + try: + file_content = gitea.get_file_content( + owner=owner, + repo=repo_name, + filepath='docker-compose.yml', + ref=data['branch'] + ) + except Exception as e: + return jsonify({'success': False, 'error': f'Failed to get docker-compose.yml: {str(e)}'}), 404 + + # Save the file content to a temporary location + temp_dir = os.path.join(current_app.config['UPLOAD_FOLDER'], 'temp') + os.makedirs(temp_dir, exist_ok=True) + + file_path = os.path.join(temp_dir, f'docker-compose-{owner}-{repo_name}.yml') + with open(file_path, 'w') as f: + f.write(file_content) + + # Get file stats + file_stats = os.stat(file_path) + + return jsonify({ + 'success': True, + 'message': 'Successfully found and downloaded docker-compose.yml', + 'data': { + 'file_path': file_path, + 'size': file_stats.st_size, + 'last_modified': datetime.fromtimestamp(file_stats.st_mtime).isoformat() + } + }) + + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 \ No newline at end of file diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index caa229e..094ed88 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 5ba8578..89f7068 100644 --- a/routes/main.py +++ b/routes/main.py @@ -1684,8 +1684,12 @@ def init_routes(main_bp): # Get NGINX settings nginx_settings = KeyValueSettings.get_value('nginx_settings') + # Get Portainer settings + portainer_settings = KeyValueSettings.get_value('portainer_settings') - return render_template('main/launch_progress.html', nginx_settings=nginx_settings) + return render_template('main/launch_progress.html', + nginx_settings=nginx_settings, + portainer_settings=portainer_settings) @main_bp.route('/api/check-dns', methods=['POST']) @login_required diff --git a/templates/main/launch_progress.html b/templates/main/launch_progress.html index bf1105c..e421aca 100644 --- a/templates/main/launch_progress.html +++ b/templates/main/launch_progress.html @@ -193,7 +193,7 @@ function initializeSteps() { `; stepsContainer.appendChild(nginxStep); - // Add SSL Certificate generation step (now step 3) + // Add SSL Certificate generation step const sslStep = document.createElement('div'); sslStep.className = 'step-item'; sslStep.innerHTML = ` @@ -205,7 +205,7 @@ function initializeSteps() { `; stepsContainer.appendChild(sslStep); - // Add Proxy Host creation step (now step 4) + // Add Proxy Host creation step const proxyStep = document.createElement('div'); proxyStep.className = 'step-item'; proxyStep.innerHTML = ` @@ -216,6 +216,18 @@ function initializeSteps() { `; stepsContainer.appendChild(proxyStep); + + // Add Portainer connection check step (now last) + const portainerStep = document.createElement('div'); + portainerStep.className = 'step-item'; + portainerStep.innerHTML = ` +
+
+
Checking Portainer Connection
+

Verifying connection to Portainer...

+
+ `; + stepsContainer.appendChild(portainerStep); } async function startLaunch(data) { @@ -234,7 +246,7 @@ async function startLaunch(data) { } // Update the step to show success - const dnsStep = document.querySelector('.step-item'); + const dnsStep = document.querySelectorAll('.step-item')[0]; dnsStep.classList.remove('active'); dnsStep.classList.add('completed'); @@ -294,7 +306,7 @@ async function startLaunch(data) { nginxStep.classList.add('completed'); nginxStep.querySelector('.step-status').textContent = 'Successfully connected to NGINX Proxy Manager'; - // Step 3: Generate SSL Certificate (moved up) + // Step 3: Generate SSL Certificate await updateStep(3, 'Generating SSL Certificate', 'Setting up secure HTTPS connection...'); const sslResult = await generateSSLCertificate(data.webAddresses); @@ -302,7 +314,7 @@ async function startLaunch(data) { throw new Error(sslResult.error || 'Failed to generate SSL certificate'); } - // Step 4: Create Proxy Host (moved down) + // Step 4: Create Proxy Host await updateStep(4, 'Creating Proxy Host', 'Setting up NGINX proxy host configuration...'); const proxyResult = await createProxyHost(data.webAddresses, data.port, sslResult.data.certificate.id); @@ -310,6 +322,20 @@ async function startLaunch(data) { throw new Error(proxyResult.error || 'Failed to create proxy host'); } + // Step 5: Check Portainer connection + await updateStep(5, 'Checking Portainer Connection', 'Verifying connection to Portainer...'); + const portainerResult = await checkPortainerConnection(); + + if (!portainerResult.success) { + throw new Error(portainerResult.error || 'Failed to connect to Portainer'); + } + + // Update the step to show success + const portainerStep = document.querySelectorAll('.step-item')[4]; + portainerStep.classList.remove('active'); + portainerStep.classList.add('completed'); + portainerStep.querySelector('.step-status').textContent = 'Successfully connected to Portainer'; + } catch (error) { showError(error.message); } @@ -391,6 +417,66 @@ async function checkNginxConnection() { } } +async function checkPortainerConnection() { + try { + // Get CSRF token from meta tag + const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); + + // Get Portainer settings from the template + const portainerSettings = { + url: '{{ portainer_settings.url if portainer_settings else "" }}', + api_key: '{{ portainer_settings.api_key if portainer_settings else "" }}' + }; + + // Debug log the raw template values + console.log('Raw template values:', { + url: '{{ portainer_settings.url if portainer_settings else "" }}', + api_key: '{{ portainer_settings.api_key if portainer_settings else "" }}' + }); + + // Debug log the settings (without API key) + console.log('Portainer Settings:', { + url: portainerSettings.url, + hasApiKey: !!portainerSettings.api_key + }); + + // Check if any required field is missing + if (!portainerSettings.url || !portainerSettings.api_key) { + console.error('Missing Portainer settings:', portainerSettings); + return { + success: false, + error: 'Portainer settings are not configured. Please configure Portainer settings in the admin panel.' + }; + } + + const testResponse = await fetch('/api/admin/test-portainer-connection', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': csrfToken + }, + body: JSON.stringify(portainerSettings) + }); + + if (!testResponse.ok) { + const error = await testResponse.json(); + console.error('Portainer connection error:', error); + return { + success: false, + error: error.error || 'Failed to connect to Portainer' + }; + } + + return { success: true }; + } catch (error) { + console.error('Error checking Portainer connection:', error); + return { + success: false, + error: error.message || 'Error checking Portainer connection' + }; + } +} + function updateStatus(step, message, type = 'info', details = '') { const statusElement = document.getElementById(`${step}Status`); const detailsElement = document.getElementById(`${step}Details`); @@ -508,7 +594,7 @@ async function createProxyHost(domains, port, sslCertificateId) { forward_host: '192.168.68.124', forward_port: parseInt(port), ssl_forced: true, - caching_enabled: false, + caching_enabled: true, block_exploits: true, allow_websocket_upgrade: true, http2_support: true,