From b92bd63a265cefbb055e72402526df198f2918fb Mon Sep 17 00:00:00 2001 From: usmannasir Date: Sat, 12 Apr 2025 13:28:43 +0500 Subject: [PATCH] fix issue with design on n8n page --- websiteFunctions/n8n_api.py | 201 ++++++++++++++++-- .../websiteFunctions/DockerContainers.js | 106 +++++++++ .../websiteFunctions/DockerSiteHome.html | 140 +++++++++++- websiteFunctions/urls.py | 9 +- 4 files changed, 431 insertions(+), 25 deletions(-) diff --git a/websiteFunctions/n8n_api.py b/websiteFunctions/n8n_api.py index 93c8cc45a..7d331f673 100644 --- a/websiteFunctions/n8n_api.py +++ b/websiteFunctions/n8n_api.py @@ -113,7 +113,9 @@ class N8nAPI: include_executions: Whether to include execution history in the backup Returns: - Backup data or None if there was an error + Dict containing backup data or error information + - On success: {'success': True, 'data': backup_data} + - On failure: {'success': False, 'error': error_message} """ try: response = self.client.post( @@ -121,16 +123,43 @@ class N8nAPI: json={ "includeCredentials": include_credentials, "includeExecutions": include_executions - } + }, + timeout=30 # Add a 30 second timeout ) - if response.status_code == 200: - return response.json() - logging.writeToFile(f"Error creating backup. Status code: {response.status_code}, Response: {response.text}") - return None + if response.status_code == 200: + # Validate the response contains expected data + backup_data = response.json() + if not isinstance(backup_data, dict): + logging.writeToFile(f"Invalid backup data format: {type(backup_data)}") + return {'success': False, 'error': 'Invalid backup data format returned from n8n'} + + return {'success': True, 'data': backup_data} + + error_msg = f"Error creating backup. Status code: {response.status_code}" + if response.text: + try: + error_data = response.json() + if 'message' in error_data: + error_msg += f", Message: {error_data['message']}" + except: + error_msg += f", Response: {response.text[:200]}" + + logging.writeToFile(error_msg) + return {'success': False, 'error': error_msg} + + except requests.exceptions.Timeout: + error_msg = "Timeout while connecting to n8n API" + logging.writeToFile(error_msg) + return {'success': False, 'error': error_msg} + except requests.exceptions.ConnectionError: + error_msg = "Connection error while connecting to n8n API" + logging.writeToFile(error_msg) + return {'success': False, 'error': error_msg} except Exception as e: - logging.writeToFile(f"Error creating backup: {str(e)}") - return None + error_msg = f"Error creating backup: {str(e)}" + logging.writeToFile(error_msg) + return {'success': False, 'error': error_msg} def restore_backup(self, backup_data): """ @@ -302,39 +331,67 @@ def create_n8n_backup(request): # Get container info client = docker.from_env() - container = client.containers.get(container_id) + try: + container = client.containers.get(container_id) + except docker.errors.NotFound: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': f'Container with ID {container_id} not found' + })) + except Exception as e: + error_msg = f"Error getting Docker container: {str(e)}" + logging.writeToFile(error_msg) + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': error_msg + })) # Extract container metadata container_info = container.attrs n8n_port = None - # Find the n8n port + # Find the n8n port - check different formats ports = container_info.get('NetworkSettings', {}).get('Ports', {}) + port_mappings = [] + if '5678/tcp' in ports and ports['5678/tcp']: n8n_port = ports['5678/tcp'][0].get('HostPort') + elif '5678' in ports and ports['5678']: + n8n_port = ports['5678'][0].get('HostPort') + + # If still no port found, try to check exposed ports + if not n8n_port: + exposed_ports = container_info.get('Config', {}).get('ExposedPorts', {}) + port_mappings = list(exposed_ports.keys()) + + # Find any port that might be mapped to 5678 + for port in ports: + port_mappings.append(f"{port} -> {ports[port]}") if not n8n_port: - logging.writeToFile(f"Could not find n8n port mapping in {ports}") + port_details = ", ".join(port_mappings) if port_mappings else "No port mappings found" + error_msg = f"Could not find n8n port mapping. Available ports: {port_details}" + logging.writeToFile(error_msg) return HttpResponse(json.dumps({ 'status': 0, - 'error_message': 'Could not find n8n port mapping' + 'error_message': error_msg })) # Create N8nAPI instance n8n_api = N8nAPI(container, 'localhost', n8n_port) # Create backup - backup = n8n_api.create_backup(include_credentials, include_executions) + backup_result = n8n_api.create_backup(include_credentials, include_executions) - if backup: + if backup_result['success']: return HttpResponse(json.dumps({ 'status': 1, - 'backup': backup + 'backup': backup_result['data'] })) else: return HttpResponse(json.dumps({ 'status': 0, - 'error_message': 'Failed to create backup' + 'error_message': backup_result['error'] })) return HttpResponse(json.dumps({ @@ -342,10 +399,11 @@ def create_n8n_backup(request): 'error_message': 'Invalid request method' })) except Exception as e: - logging.writeToFile(f"Error in create_n8n_backup: {str(e)}") + error_msg = f"Error in create_n8n_backup: {str(e)}" + logging.writeToFile(error_msg) return HttpResponse(json.dumps({ 'status': 0, - 'error_message': str(e) + 'error_message': error_msg })) @csrf_exempt @@ -404,4 +462,111 @@ def restore_n8n_backup(request): return HttpResponse(json.dumps({ 'status': 0, 'error_message': str(e) + })) + +@csrf_exempt +def diagnose_n8n_api(request): + try: + if request.method == 'POST': + userID = request.session['userID'] + currentACL = ACLManager.loadedACL(userID) + admin = Administrator.objects.get(pk=userID) + + data = json.loads(request.body) + container_id = data.get('container_id') + + # Get container info + client = docker.from_env() + try: + container = client.containers.get(container_id) + except docker.errors.NotFound: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': f'Container with ID {container_id} not found', + 'diagnostics': { + 'container_exists': False + } + })) + + # Container exists + diagnostics = { + 'container_exists': True, + 'container_status': container.status, + 'container_running': container.status == 'running', + 'container_names': container.name, + 'port_mappings': {}, + 'exposed_ports': {}, + 'n8n_port_found': False, + 'api_accessible': False + } + + # Extract port mappings + container_info = container.attrs + ports = container_info.get('NetworkSettings', {}).get('Ports', {}) + exposed_ports = container_info.get('Config', {}).get('ExposedPorts', {}) + + diagnostics['port_mappings'] = ports + diagnostics['exposed_ports'] = exposed_ports + + # Find the n8n port + n8n_port = None + if '5678/tcp' in ports and ports['5678/tcp']: + n8n_port = ports['5678/tcp'][0].get('HostPort') + diagnostics['n8n_port'] = n8n_port + diagnostics['n8n_port_found'] = True + elif '5678' in ports and ports['5678']: + n8n_port = ports['5678'][0].get('HostPort') + diagnostics['n8n_port'] = n8n_port + diagnostics['n8n_port_found'] = True + + # Only proceed if container is running and port was found + if diagnostics['container_running'] and diagnostics['n8n_port_found']: + # Test n8n API connection + try: + n8n_api = N8nAPI(container, 'localhost', n8n_port) + + # Try to access the health endpoint + response = n8n_api.client.get(f"{n8n_api.base_url}/rest/health", timeout=5) + diagnostics['api_response_code'] = response.status_code + diagnostics['api_accessible'] = response.status_code == 200 + + if diagnostics['api_accessible']: + # Try to get some basic info + try: + # Check if workflows endpoint is accessible + workflow_response = n8n_api.client.get(f"{n8n_api.base_url}/rest/workflows", timeout=5) + diagnostics['workflows_accessible'] = workflow_response.status_code == 200 + + # Check if credentials endpoint is accessible + cred_response = n8n_api.client.get(f"{n8n_api.base_url}/rest/credentials", timeout=5) + diagnostics['credentials_accessible'] = cred_response.status_code == 200 + + # Try a simple export operation + export_response = n8n_api.client.post( + f"{n8n_api.base_url}/rest/export", + json={"includeCredentials": False, "includeExecutions": False}, + timeout=5 + ) + diagnostics['export_accessible'] = export_response.status_code == 200 + + except Exception as e: + diagnostics['additional_error'] = str(e) + except Exception as e: + diagnostics['api_error'] = str(e) + + return HttpResponse(json.dumps({ + 'status': 1, + 'diagnostics': diagnostics + })) + + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Invalid request method' + })) + except Exception as e: + error_msg = f"Error in diagnose_n8n_api: {str(e)}" + logging.writeToFile(error_msg) + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': error_msg })) \ No newline at end of file diff --git a/websiteFunctions/static/websiteFunctions/DockerContainers.js b/websiteFunctions/static/websiteFunctions/DockerContainers.js index 12ae843ea..a23e7d38f 100644 --- a/websiteFunctions/static/websiteFunctions/DockerContainers.js +++ b/websiteFunctions/static/websiteFunctions/DockerContainers.js @@ -440,6 +440,112 @@ app.controller('ListDockersitecontainer', function ($scope, $http) { }); }; + // Diagnostic function to troubleshoot n8n connectivity issues + $scope.diagnoseN8n = function(container) { + $scope.cyberpanelLoading = false; + $('#cyberpanelLoading').show(); + + var url = "/websites/n8n/diagnose"; + + var data = { + 'container_id': container.id + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config) + .then(function(response) { + $scope.cyberpanelLoading = true; + $('#cyberpanelLoading').hide(); + + if (response.data.status === 1) { + var diagnostics = response.data.diagnostics; + + // Initialize diagnostic results if not exists + if (!container.diagnosticResults) { + container.diagnosticResults = {}; + } + + // Store diagnostic results + container.diagnosticResults = diagnostics; + container.showDiagnostics = true; + + // Show summary notification + var summaryMessage = ""; + + if (!diagnostics.container_exists) { + summaryMessage = "Container not found"; + } else if (!diagnostics.container_running) { + summaryMessage = "Container exists but is not running. Current status: " + diagnostics.container_status; + } else if (!diagnostics.n8n_port_found) { + summaryMessage = "Container is running but n8n port (5678) is not mapped. Available ports: " + + JSON.stringify(diagnostics.port_mappings); + } else if (!diagnostics.api_accessible) { + summaryMessage = "n8n port found but API is not accessible. Check if n8n is running inside the container."; + if (diagnostics.api_error) { + summaryMessage += " Error: " + diagnostics.api_error; + } + } else { + summaryMessage = "n8n API is accessible. Endpoints status:"; + if (diagnostics.workflows_accessible) { + summaryMessage += " Workflows: OK"; + } else { + summaryMessage += " Workflows: Failed"; + } + + if (diagnostics.credentials_accessible) { + summaryMessage += " | Credentials: OK"; + } else { + summaryMessage += " | Credentials: Failed"; + } + + if (diagnostics.export_accessible) { + summaryMessage += " | Export: OK"; + } else { + summaryMessage += " | Export: Failed"; + } + } + + new PNotify({ + title: 'Diagnostic Results', + text: summaryMessage, + type: 'info', + hide: false + }); + + } else { + new PNotify({ + title: 'Diagnostic Failed', + text: response.data.error_message || 'Failed to diagnose n8n container', + type: 'error' + }); + } + }) + .catch(function(error) { + $scope.cyberpanelLoading = true; + $('#cyberpanelLoading').hide(); + + var errorMessage = 'Connection disrupted, refresh the page.'; + if (error.data && error.data.error_message) { + errorMessage = error.data.error_message; + } else if (error.statusText) { + errorMessage = 'Server error: ' + error.statusText; + } + + new PNotify({ + title: 'Diagnostic Failed', + text: errorMessage, + type: 'error' + }); + + console.error('Error during diagnosis:', error); + }); + }; + // Restore from a backup $scope.restoreFromBackup = function(container) { // Check if a file has been selected diff --git a/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html b/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html index 70189915a..d40a523b3 100644 --- a/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html +++ b/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html @@ -607,9 +607,14 @@ - +

@@ -705,6 +710,14 @@ +

@@ -1863,6 +1876,127 @@ +
+
+
+
+
+
N8n API Diagnostics
+
+ +
+
+
+
+
+
+ N8n API Status: AccessibleNot Accessible +
+
+ +
+
+
+
+
Container Status
+
+
+ + + + + + + + + + + + + + + + + +
Container Exists
Container Status{{container.diagnosticResults.container_status}}
Container Running
Container Name{{container.diagnosticResults.container_names}}
+
+
+
+ +
+
+
+
N8n API Connectivity
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
N8n Port Found
N8n Port{{container.diagnosticResults.n8n_port}}
API Accessible
Workflows API
Credentials API
Export API
+
+
+
+
+ +
+
+
+
+
Port Mappings
+
+
+
{{container.diagnosticResults.port_mappings | json}}
+
+
+
+
+ +
+
+
+
+
API Error
+
+
+
{{container.diagnosticResults.api_error}}
+
+
+
+
+
+
+

Click "Run Diagnostics" to check N8n API connectivity.

+
+
+
+
+
+
+ diff --git a/websiteFunctions/urls.py b/websiteFunctions/urls.py index 23731d84f..f92788ca9 100755 --- a/websiteFunctions/urls.py +++ b/websiteFunctions/urls.py @@ -202,8 +202,9 @@ urlpatterns = [ path('', views.domain, name='domain'), # N8N API endpoints - path('n8n/get_workflows', views.n8n_api.get_n8n_workflows, name='get_n8n_workflows'), - path('n8n/toggle_workflow', views.n8n_api.toggle_workflow, name='toggle_workflow'), - path('n8n/create_backup', views.n8n_api.create_n8n_backup, name='create_n8n_backup'), - path('n8n/restore_backup', views.n8n_api.restore_n8n_backup, name='restore_n8n_backup'), + path('websites/n8n/get_workflows', views.n8n_api.get_n8n_workflows, name='get_n8n_workflows'), + path('websites/n8n/toggle_workflow', views.n8n_api.toggle_workflow, name='toggle_workflow'), + path('websites/n8n/create_backup', views.n8n_api.create_n8n_backup, name='create_n8n_backup'), + path('websites/n8n/restore_backup', views.n8n_api.restore_n8n_backup, name='restore_n8n_backup'), + path('websites/n8n/diagnose', views.n8n_api.diagnose_n8n_api, name='diagnose_n8n_api'), ]