diff --git a/websiteFunctions/n8n_api.py b/websiteFunctions/n8n_api.py index 7d331f673..dd6c6e6a3 100644 --- a/websiteFunctions/n8n_api.py +++ b/websiteFunctions/n8n_api.py @@ -11,41 +11,11 @@ from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging class N8nAPI: def __init__(self, container, host, port): - """ - Initialize the N8nAPI with a container reference and base URL - - Args: - container: Docker container object - host: Host address for n8n (usually 'localhost') - port: Port number for n8n API - """ self.container = container - self.host = host - self.port = port self.base_url = f"http://{host}:{port}" - - # Set up a requests session for API calls self.client = requests.Session() - self.client.headers.update({ - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }) - - # Check if the API is accessible - try: - response = self.client.get(f"{self.base_url}/rest/health") - if response.status_code != 200: - logging.writeToFile(f"n8n health check failed: {response.status_code}") - except Exception as e: - logging.writeToFile(f"Error connecting to n8n API: {str(e)}") def get_workflows(self): - """ - Get all workflows from n8n - - Returns: - List of workflow objects or None if there was an error - """ try: response = self.client.get(f"{self.base_url}/rest/workflows") if response.status_code == 200: @@ -66,35 +36,18 @@ class N8nAPI: logging.writeToFile(f"Error fetching workflow executions: {str(e)}") return None - def toggle_workflow(self, workflow_id, active): - """ - Activate or deactivate a workflow - - Args: - workflow_id: ID of the workflow to toggle - active: Boolean indicating whether to activate (True) or deactivate (False) - - Returns: - Boolean indicating success - """ + def toggle_workflow_active(self, workflow_id, active): try: - url = f"{self.base_url}/rest/workflows/{workflow_id}/activate" - if not active: - url = f"{self.base_url}/rest/workflows/{workflow_id}/deactivate" - - response = self.client.post(url) - return response.status_code in [200, 201] + response = self.client.patch(f"{self.base_url}/rest/workflows/{workflow_id}/activate", + json={"active": active}) + if response.status_code in [200, 201]: + return True + return False except Exception as e: - logging.writeToFile(f"Error toggling workflow: {str(e)}") + logging.writeToFile(f"Error toggling workflow status: {str(e)}") return False def get_credentials(self): - """ - Get all credentials from n8n - - Returns: - List of credential objects or None if there was an error - """ try: response = self.client.get(f"{self.base_url}/rest/credentials") if response.status_code == 200: @@ -105,81 +58,25 @@ class N8nAPI: return None def create_backup(self, include_credentials=True, include_executions=False): - """ - Create a backup of n8n data - - Args: - include_credentials: Whether to include credentials in the backup - include_executions: Whether to include execution history in the backup - - Returns: - 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( - f"{self.base_url}/rest/export", - json={ - "includeCredentials": include_credentials, - "includeExecutions": include_executions - }, - timeout=30 # Add a 30 second timeout - ) - + response = self.client.post(f"{self.base_url}/rest/export", + json={ + "includeCredentials": include_credentials, + "includeExecutions": include_executions + }) 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} + return response.json() + return None except Exception as e: - error_msg = f"Error creating backup: {str(e)}" - logging.writeToFile(error_msg) - return {'success': False, 'error': error_msg} + logging.writeToFile(f"Error creating backup: {str(e)}") + return None def restore_backup(self, backup_data): - """ - Restore from a backup - - Args: - backup_data: Backup data to restore - - Returns: - Boolean indicating success - """ try: - response = self.client.post( - f"{self.base_url}/rest/import", - json=backup_data - ) + response = self.client.post(f"{self.base_url}/rest/import", + json=backup_data) if response.status_code in [200, 201]: return True - - logging.writeToFile(f"Error restoring backup. Status code: {response.status_code}, Response: {response.text}") return False except Exception as e: logging.writeToFile(f"Error restoring backup: {str(e)}") @@ -223,15 +120,6 @@ def get_n8n_workflows(request): workflows = n8n_api.get_workflows() if workflows: - # Add dummy statistics for demonstration - import random - from datetime import datetime, timedelta - - for workflow in workflows: - workflow['active'] = workflow.get('active', False) - workflow['lastExecution'] = (datetime.now() - timedelta(days=random.randint(0, 7))).isoformat() - workflow['successRate'] = random.randint(60, 100) - return HttpResponse(json.dumps({ 'status': 1, 'workflows': workflows @@ -247,7 +135,6 @@ def get_n8n_workflows(request): 'error_message': 'Invalid request method' })) except Exception as e: - logging.writeToFile(f"Error in get_n8n_workflows: {str(e)}") return HttpResponse(json.dumps({ 'status': 0, 'error_message': str(e) @@ -288,19 +175,18 @@ def toggle_workflow(request): # Create N8nAPI instance n8n_api = N8nAPI(container, 'localhost', n8n_port) - # Toggle workflow - success = n8n_api.toggle_workflow(workflow_id, active) + # Toggle workflow status + success = n8n_api.toggle_workflow_active(workflow_id, active) if success: return HttpResponse(json.dumps({ 'status': 1, - 'workflow_id': workflow_id, - 'active': active + 'message': f"Workflow {'activated' if active else 'deactivated'} successfully" })) else: return HttpResponse(json.dumps({ 'status': 0, - 'error_message': f'Failed to {"activate" if active else "deactivate"} workflow' + 'error_message': 'Failed to update workflow status' })) return HttpResponse(json.dumps({ @@ -308,7 +194,6 @@ def toggle_workflow(request): 'error_message': 'Invalid request method' })) except Exception as e: - logging.writeToFile(f"Error in toggle_workflow: {str(e)}") return HttpResponse(json.dumps({ 'status': 0, 'error_message': str(e) @@ -327,97 +212,6 @@ def create_n8n_backup(request): include_credentials = data.get('include_credentials', True) include_executions = data.get('include_executions', False) - logging.writeToFile(f"Creating backup for container {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' - })) - 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 - 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: - 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': error_msg - })) - - # Create N8nAPI instance - n8n_api = N8nAPI(container, 'localhost', n8n_port) - - # Create backup - backup_result = n8n_api.create_backup(include_credentials, include_executions) - - if backup_result['success']: - return HttpResponse(json.dumps({ - 'status': 1, - 'backup': backup_result['data'] - })) - else: - return HttpResponse(json.dumps({ - 'status': 0, - 'error_message': backup_result['error'] - })) - - return HttpResponse(json.dumps({ - 'status': 0, - 'error_message': 'Invalid request method' - })) - except Exception as e: - error_msg = f"Error in create_n8n_backup: {str(e)}" - logging.writeToFile(error_msg) - return HttpResponse(json.dumps({ - 'status': 0, - 'error_message': error_msg - })) - -@csrf_exempt -def restore_n8n_backup(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') - backup_data = data.get('backup_data') - # Get container info client = docker.from_env() container = client.containers.get(container_id) @@ -440,17 +234,18 @@ def restore_n8n_backup(request): # Create N8nAPI instance n8n_api = N8nAPI(container, 'localhost', n8n_port) - # Restore backup - success = n8n_api.restore_backup(backup_data) + # Create backup + backup = n8n_api.create_backup(include_credentials, include_executions) - if success: + if backup: return HttpResponse(json.dumps({ - 'status': 1 + 'status': 1, + 'backup': backup })) else: return HttpResponse(json.dumps({ 'status': 0, - 'error_message': 'Failed to restore backup' + 'error_message': 'Failed to create backup' })) return HttpResponse(json.dumps({ @@ -458,115 +253,7 @@ def restore_n8n_backup(request): 'error_message': 'Invalid request method' })) except Exception as e: - logging.writeToFile(f"Error in restore_n8n_backup: {str(e)}") 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 089a3258a..e75fb19ad 100644 --- a/websiteFunctions/static/websiteFunctions/DockerContainers.js +++ b/websiteFunctions/static/websiteFunctions/DockerContainers.js @@ -1,17 +1,6 @@ -// Add the json filter to the application -app.filter('json', function() { - return function(input) { - if (input === undefined || input === null) { - return ''; - } - return JSON.stringify(input, null, 2); - }; -}); - app.controller('ListDockersitecontainer', function ($scope, $http) { $scope.cyberPanelLoading = true; $scope.conatinerview = true; - $scope.JSON = window.JSON; // Make JSON available to templates $('#cyberpanelLoading').hide(); // Format bytes to human readable @@ -378,17 +367,18 @@ app.controller('ListDockersitecontainer', function ($scope, $http) { // Create a backup $scope.createBackup = function(container) { - // Initialize backup options if they don't exist - $scope.initBackupOptions(container); - $scope.cyberpanelLoading = false; $('#cyberpanelLoading').show(); + // Initialize backup options if they don't exist + $scope.initBackupOptions(container); + var url = "/websites/n8n/create_backup"; var data = { 'container_id': container.id, - 'include_credentials': container.backupOptions.includeCredentials + 'include_credentials': container.backupOptions.includeCredentials, + 'include_executions': container.backupOptions.includeExecutions }; var config = { @@ -397,181 +387,53 @@ app.controller('ListDockersitecontainer', function ($scope, $http) { } }; - $http.post(url, data, config) - .then(function(response) { - $scope.cyberpanelLoading = true; - $('#cyberpanelLoading').hide(); + $http.post(url, data, config).then(function(response) { + $scope.cyberpanelLoading = true; + $('#cyberpanelLoading').hide(); + + if (response.data.status === 1) { + // Download the backup file + var backupData = response.data.backup; + var fileName = 'n8n-backup-' + new Date().toISOString().slice(0, 10) + '.json'; - if (response.data.status === 1) { - // Download the backup file - var backupData = response.data.backup; - var fileName = 'n8n-backup-' + new Date().toISOString().slice(0, 10) + '.json'; - - // Create a download link - var a = document.createElement('a'); - var blob = new Blob([JSON.stringify(backupData)], {type: 'application/json'}); - var url = window.URL.createObjectURL(blob); - a.href = url; - a.download = fileName; - a.click(); - window.URL.revokeObjectURL(url); - - new PNotify({ - title: 'Success!', - text: 'Backup created and downloaded successfully.', - type: 'success' - }); - } else { - new PNotify({ - title: 'Operation Failed!', - text: response.data.error_message || 'Unknown error occurred during backup creation', - type: 'error' - }); - console.error('Backup creation failed:', response.data); - } - }) - .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; - } + // Create a download link + var a = document.createElement('a'); + var blob = new Blob([JSON.stringify(backupData)], {type: 'application/json'}); + var url = window.URL.createObjectURL(blob); + a.href = url; + a.download = fileName; + a.click(); + window.URL.revokeObjectURL(url); + new PNotify({ + title: 'Success!', + text: 'Backup created and downloaded successfully.', + type: 'success' + }); + } else { new PNotify({ title: 'Operation Failed!', - text: errorMessage, + text: response.data.error_message, type: 'error' }); - - console.error('Error creating backup:', error); - }); - }; - - // 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; - - // Create a formatted diagnostic message - var summaryMessage = "
" + portMappingsString + "
@@ -710,23 +705,12 @@ -
Open n8n - @@ -847,7 +831,7 @@{$ selectedExecution.inputData $}
+ {$ selectedExecution.inputData | json $}
{$ selectedExecution.outputData $}
+ {$ selectedExecution.outputData | json $}
| Container Exists | -- |
|---|---|
| Container Status | -{{container.diagnosticResults.container_status}} | -
| Container Running | -- |
| Container Name | -{{container.diagnosticResults.container_names}} | -
| N8n Port Found | -- |
|---|---|
| N8n Port | -{{container.diagnosticResults.n8n_port}} | -
| API Accessible | -- |
| Workflows API | -- |
| Credentials API | -- |
| Export API | -- |
{$ JSON.stringify(container.diagnosticResults.port_mappings) $}
- {{JSON.stringify(container.diagnosticResults.api_error, null, 2)}}
- Click "Run Diagnostics" to check N8n API connectivity.
-