diff --git a/websiteFunctions/dockerviews.py b/websiteFunctions/dockerviews.py index 2d4c6136c..096ae6e05 100644 --- a/websiteFunctions/dockerviews.py +++ b/websiteFunctions/dockerviews.py @@ -8,6 +8,8 @@ from django.shortcuts import redirect from loginSystem.views import loadLoginPage from django.views.decorators.csrf import csrf_exempt from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging +import requests +import re def require_login(view_func): def wrapper(request, *args, **kwargs): @@ -30,6 +32,34 @@ class DockerManager: except Exception as e: logging.writeToFile(f"Error getting container {container_id}: {str(e)}") return None + + def get_n8n_version(self, container): + try: + # Execute npm list command in the container to get n8n version + exec_result = container.exec_run( + cmd="npm list n8n --json", + workdir="/usr/local/lib/node_modules/n8n" + ) + if exec_result.exit_code == 0: + npm_output = json.loads(exec_result.output.decode()) + # Extract version from npm output + if 'dependencies' in npm_output and 'n8n' in npm_output['dependencies']: + return npm_output['dependencies']['n8n']['version'] + return None + except Exception as e: + logging.writeToFile(f"Error getting n8n version: {str(e)}") + return None + + def get_latest_n8n_version(self): + try: + # Fetch latest version from npm registry + response = requests.get('https://registry.npmjs.org/n8n/latest') + if response.status_code == 200: + return response.json()['version'] + return None + except Exception as e: + logging.writeToFile(f"Error fetching latest n8n version: {str(e)}") + return None @csrf_exempt @require_login @@ -162,6 +192,150 @@ def restartContainer(request): container.restart() return HttpResponse(json.dumps({'status': 1})) + return HttpResponse('Not allowed') + except Exception as e: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': str(e) + })) + +@csrf_exempt +@require_login +def check_n8n_version(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') + site_name = data.get('name') + + # Verify Docker site ownership + try: + docker_site = DockerSites.objects.get(SiteName=site_name) + if currentACL['admin'] != 1 and docker_site.admin != admin and docker_site.admin.owner != admin.pk: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Not authorized to access this container' + })) + except DockerSites.DoesNotExist: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Docker site not found' + })) + + docker_manager = DockerManager() + container = docker_manager.get_container(container_id) + + if not container: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Container not found' + })) + + # Get current version + current_version = docker_manager.get_n8n_version(container) + if not current_version: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Could not determine current n8n version' + })) + + # Get latest version + latest_version = docker_manager.get_latest_n8n_version() + if not latest_version: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Could not fetch latest n8n version' + })) + + # Compare versions + update_available = current_version != latest_version + + return HttpResponse(json.dumps({ + 'status': 1, + 'current_version': current_version, + 'latest_version': latest_version, + 'update_available': update_available + })) + + return HttpResponse('Not allowed') + except Exception as e: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': str(e) + })) + +@csrf_exempt +@require_login +def update_n8n(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') + site_name = data.get('name') + + # Verify Docker site ownership + try: + docker_site = DockerSites.objects.get(SiteName=site_name) + if currentACL['admin'] != 1 and docker_site.admin != admin and docker_site.admin.owner != admin.pk: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Not authorized to access this container' + })) + except DockerSites.DoesNotExist: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Docker site not found' + })) + + docker_manager = DockerManager() + container = docker_manager.get_container(container_id) + + if not container: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': 'Container not found' + })) + + # Update n8n + try: + # Run npm update in the container + exec_result = container.exec_run( + cmd="npm install -g n8n@latest", + workdir="/usr/local/lib/node_modules/n8n" + ) + + if exec_result.exit_code != 0: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': f'Update failed: {exec_result.output.decode()}' + })) + + # Get new version after update + new_version = docker_manager.get_n8n_version(container) + + # Restart the container to apply changes + container.restart() + + return HttpResponse(json.dumps({ + 'status': 1, + 'message': 'n8n updated successfully', + 'new_version': new_version + })) + + except Exception as e: + return HttpResponse(json.dumps({ + 'status': 0, + 'error_message': f'Update failed: {str(e)}' + })) + return HttpResponse('Not allowed') except Exception as e: return HttpResponse(json.dumps({ diff --git a/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html b/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html index 4804b25c8..d7ac33948 100644 --- a/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html +++ b/websiteFunctions/templates/websiteFunctions/DockerSiteHome.html @@ -916,7 +916,7 @@ }); // Add n8n version checking and update functionality - angular.module('WebsitesApp').run(['$rootScope', function($rootScope) { + angular.module('WebsitesApp').run(['$rootScope', '$http', function($rootScope, $http) { // Add custom icon rendering for container actions $rootScope.renderIcon = function(iconName) { return ''; @@ -951,24 +951,49 @@ // Initialize n8n version info for containers $rootScope.initializeN8nVersion = function(container) { - // This would normally come from the backend - container.n8nVersion = { - current: '1.0.0', - latest: '1.1.0', - updateAvailable: true - }; + // Call backend to check version + $http.post('/websiteFunctions/docker/check-n8n-version', { + container_id: container.id, + name: container.name + }).then(function(response) { + if (response.data.status === 1) { + container.n8nVersion = { + current: response.data.current_version, + latest: response.data.latest_version, + updateAvailable: response.data.update_available + }; + } else { + console.error('Error checking n8n version:', response.data.error_message); + } + }).catch(function(error) { + console.error('Error checking n8n version:', error); + }); }; // Add n8n update function $rootScope.updateN8n = function(container) { container.updatingN8n = true; - // Simulate update process - setTimeout(function() { - container.n8nVersion.current = container.n8nVersion.latest; - container.n8nVersion.updateAvailable = false; + $http.post('/websiteFunctions/docker/update-n8n', { + container_id: container.id, + name: container.name + }).then(function(response) { + if (response.data.status === 1) { + container.n8nVersion.current = response.data.new_version; + container.n8nVersion.updateAvailable = false; + // Show success notification + $rootScope.notify({message: response.data.message, type: 'success'}); + } else { + // Show error notification + $rootScope.notify({message: response.data.error_message, type: 'error'}); + } + }).catch(function(error) { + console.error('Error updating n8n:', error); + // Show error notification + $rootScope.notify({message: 'Failed to update n8n', type: 'error'}); + }).finally(function() { container.updatingN8n = false; - }, 3000); + }); }; // Initialize version info when loading containers @@ -977,7 +1002,10 @@ $rootScope.Lunchcontainer = function(containerId) { var result = originalLunchcontainer(containerId); // Initialize version info after container is loaded - $rootScope.initializeN8nVersion($rootScope.web); + if ($rootScope.web && $rootScope.web.environment && + $rootScope.web.environment.some(function(env) { return env.includes('n8n'); })) { + $rootScope.initializeN8nVersion($rootScope.web); + } return result; }; } diff --git a/websiteFunctions/urls.py b/websiteFunctions/urls.py index 263492a83..06fd31076 100755 --- a/websiteFunctions/urls.py +++ b/websiteFunctions/urls.py @@ -1,5 +1,7 @@ from django.urls import path from . import views +from django.conf.urls import url +from . import dockerviews urlpatterns = [ path('', views.loadWebsitesHome, name='loadWebsitesHome'), @@ -180,9 +182,11 @@ urlpatterns = [ path('fetchDockersite', views.fetchDockersite, name='fetchDockersite'), # Docker Container Actions - path('docker/startContainer', views.startContainer, name='startContainer'), - path('docker/stopContainer', views.stopContainer, name='stopContainer'), - path('docker/restartContainer', views.restartContainer, name='restartContainer'), + path('docker/startContainer', dockerviews.startContainer, name='startContainer'), + path('docker/stopContainer', dockerviews.stopContainer, name='stopContainer'), + path('docker/restartContainer', dockerviews.restartContainer, name='restartContainer'), + path('docker/check-n8n-version', dockerviews.check_n8n_version, name='check_n8n_version'), + path('docker/update-n8n', dockerviews.update_n8n, name='update_n8n'), # SSH Configs path('getSSHConfigs', views.getSSHConfigs, name='getSSHConfigs'), @@ -200,4 +204,8 @@ urlpatterns = [ # Catch all for domains path('/', views.launchChild, name='launchChild'), path('', views.domain, name='domain'), + + # New n8n version endpoints + url(r'^check-n8n-version$', dockerviews.check_n8n_version, name='check_n8n_version'), + url(r'^update-n8n$', dockerviews.update_n8n, name='update_n8n'), ]