mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-07 13:56:01 +01:00
fix issue with design on n8n page
This commit is contained in:
@@ -11,11 +11,41 @@ from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
|||||||
|
|
||||||
class N8nAPI:
|
class N8nAPI:
|
||||||
def __init__(self, container, host, port):
|
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.container = container
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
self.base_url = f"http://{host}:{port}"
|
self.base_url = f"http://{host}:{port}"
|
||||||
|
|
||||||
|
# Set up a requests session for API calls
|
||||||
self.client = requests.Session()
|
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):
|
def get_workflows(self):
|
||||||
|
"""
|
||||||
|
Get all workflows from n8n
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of workflow objects or None if there was an error
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
response = self.client.get(f"{self.base_url}/rest/workflows")
|
response = self.client.get(f"{self.base_url}/rest/workflows")
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
@@ -36,18 +66,35 @@ class N8nAPI:
|
|||||||
logging.writeToFile(f"Error fetching workflow executions: {str(e)}")
|
logging.writeToFile(f"Error fetching workflow executions: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def toggle_workflow_active(self, workflow_id, active):
|
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
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
response = self.client.patch(f"{self.base_url}/rest/workflows/{workflow_id}/activate",
|
url = f"{self.base_url}/rest/workflows/{workflow_id}/activate"
|
||||||
json={"active": active})
|
if not active:
|
||||||
if response.status_code in [200, 201]:
|
url = f"{self.base_url}/rest/workflows/{workflow_id}/deactivate"
|
||||||
return True
|
|
||||||
return False
|
response = self.client.post(url)
|
||||||
|
return response.status_code in [200, 201]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.writeToFile(f"Error toggling workflow status: {str(e)}")
|
logging.writeToFile(f"Error toggling workflow: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_credentials(self):
|
def get_credentials(self):
|
||||||
|
"""
|
||||||
|
Get all credentials from n8n
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of credential objects or None if there was an error
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
response = self.client.get(f"{self.base_url}/rest/credentials")
|
response = self.client.get(f"{self.base_url}/rest/credentials")
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
@@ -58,25 +105,52 @@ class N8nAPI:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def create_backup(self, include_credentials=True, include_executions=False):
|
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:
|
||||||
|
Backup data or None if there was an error
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
response = self.client.post(f"{self.base_url}/rest/export",
|
response = self.client.post(
|
||||||
json={
|
f"{self.base_url}/rest/export",
|
||||||
"includeCredentials": include_credentials,
|
json={
|
||||||
"includeExecutions": include_executions
|
"includeCredentials": include_credentials,
|
||||||
})
|
"includeExecutions": include_executions
|
||||||
|
}
|
||||||
|
)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
|
logging.writeToFile(f"Error creating backup. Status code: {response.status_code}, Response: {response.text}")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.writeToFile(f"Error creating backup: {str(e)}")
|
logging.writeToFile(f"Error creating backup: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def restore_backup(self, backup_data):
|
def restore_backup(self, backup_data):
|
||||||
|
"""
|
||||||
|
Restore from a backup
|
||||||
|
|
||||||
|
Args:
|
||||||
|
backup_data: Backup data to restore
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Boolean indicating success
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
response = self.client.post(f"{self.base_url}/rest/import",
|
response = self.client.post(
|
||||||
json=backup_data)
|
f"{self.base_url}/rest/import",
|
||||||
|
json=backup_data
|
||||||
|
)
|
||||||
if response.status_code in [200, 201]:
|
if response.status_code in [200, 201]:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
logging.writeToFile(f"Error restoring backup. Status code: {response.status_code}, Response: {response.text}")
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.writeToFile(f"Error restoring backup: {str(e)}")
|
logging.writeToFile(f"Error restoring backup: {str(e)}")
|
||||||
@@ -120,6 +194,15 @@ def get_n8n_workflows(request):
|
|||||||
workflows = n8n_api.get_workflows()
|
workflows = n8n_api.get_workflows()
|
||||||
|
|
||||||
if 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({
|
return HttpResponse(json.dumps({
|
||||||
'status': 1,
|
'status': 1,
|
||||||
'workflows': workflows
|
'workflows': workflows
|
||||||
@@ -135,6 +218,7 @@ def get_n8n_workflows(request):
|
|||||||
'error_message': 'Invalid request method'
|
'error_message': 'Invalid request method'
|
||||||
}))
|
}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logging.writeToFile(f"Error in get_n8n_workflows: {str(e)}")
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'error_message': str(e)
|
'error_message': str(e)
|
||||||
@@ -175,18 +259,19 @@ def toggle_workflow(request):
|
|||||||
# Create N8nAPI instance
|
# Create N8nAPI instance
|
||||||
n8n_api = N8nAPI(container, 'localhost', n8n_port)
|
n8n_api = N8nAPI(container, 'localhost', n8n_port)
|
||||||
|
|
||||||
# Toggle workflow status
|
# Toggle workflow
|
||||||
success = n8n_api.toggle_workflow_active(workflow_id, active)
|
success = n8n_api.toggle_workflow(workflow_id, active)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 1,
|
'status': 1,
|
||||||
'message': f"Workflow {'activated' if active else 'deactivated'} successfully"
|
'workflow_id': workflow_id,
|
||||||
|
'active': active
|
||||||
}))
|
}))
|
||||||
else:
|
else:
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'error_message': 'Failed to update workflow status'
|
'error_message': f'Failed to {"activate" if active else "deactivate"} workflow'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
@@ -194,6 +279,7 @@ def toggle_workflow(request):
|
|||||||
'error_message': 'Invalid request method'
|
'error_message': 'Invalid request method'
|
||||||
}))
|
}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logging.writeToFile(f"Error in toggle_workflow: {str(e)}")
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'error_message': str(e)
|
'error_message': str(e)
|
||||||
@@ -212,6 +298,8 @@ def create_n8n_backup(request):
|
|||||||
include_credentials = data.get('include_credentials', True)
|
include_credentials = data.get('include_credentials', True)
|
||||||
include_executions = data.get('include_executions', False)
|
include_executions = data.get('include_executions', False)
|
||||||
|
|
||||||
|
logging.writeToFile(f"Creating backup for container {container_id}")
|
||||||
|
|
||||||
# Get container info
|
# Get container info
|
||||||
client = docker.from_env()
|
client = docker.from_env()
|
||||||
container = client.containers.get(container_id)
|
container = client.containers.get(container_id)
|
||||||
@@ -226,6 +314,7 @@ def create_n8n_backup(request):
|
|||||||
n8n_port = ports['5678/tcp'][0].get('HostPort')
|
n8n_port = ports['5678/tcp'][0].get('HostPort')
|
||||||
|
|
||||||
if not n8n_port:
|
if not n8n_port:
|
||||||
|
logging.writeToFile(f"Could not find n8n port mapping in {ports}")
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'error_message': 'Could not find n8n port mapping'
|
'error_message': 'Could not find n8n port mapping'
|
||||||
@@ -253,6 +342,65 @@ def create_n8n_backup(request):
|
|||||||
'error_message': 'Invalid request method'
|
'error_message': 'Invalid request method'
|
||||||
}))
|
}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logging.writeToFile(f"Error in create_n8n_backup: {str(e)}")
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': str(e)
|
||||||
|
}))
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
# Extract container metadata
|
||||||
|
container_info = container.attrs
|
||||||
|
n8n_port = None
|
||||||
|
|
||||||
|
# Find the n8n port
|
||||||
|
ports = container_info.get('NetworkSettings', {}).get('Ports', {})
|
||||||
|
if '5678/tcp' in ports and ports['5678/tcp']:
|
||||||
|
n8n_port = ports['5678/tcp'][0].get('HostPort')
|
||||||
|
|
||||||
|
if not n8n_port:
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': 'Could not find n8n port mapping'
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Create N8nAPI instance
|
||||||
|
n8n_api = N8nAPI(container, 'localhost', n8n_port)
|
||||||
|
|
||||||
|
# Restore backup
|
||||||
|
success = n8n_api.restore_backup(backup_data)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 1
|
||||||
|
}))
|
||||||
|
else:
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': 'Failed to restore backup'
|
||||||
|
}))
|
||||||
|
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 0,
|
||||||
|
'error_message': 'Invalid request method'
|
||||||
|
}))
|
||||||
|
except Exception as e:
|
||||||
|
logging.writeToFile(f"Error in restore_n8n_backup: {str(e)}")
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'error_message': str(e)
|
'error_message': str(e)
|
||||||
|
|||||||
@@ -387,46 +387,53 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$http.post(url, data, config).then(function(response) {
|
$http.post(url, data, config).then(
|
||||||
$scope.cyberpanelLoading = true;
|
// Success handler
|
||||||
$('#cyberpanelLoading').hide();
|
function(response) {
|
||||||
|
$scope.cyberpanelLoading = true;
|
||||||
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
if (response.data.status === 1) {
|
if (response.data.status === 1) {
|
||||||
// Download the backup file
|
// Download the backup file
|
||||||
var backupData = response.data.backup;
|
var backupData = response.data.backup;
|
||||||
var fileName = 'n8n-backup-' + new Date().toISOString().slice(0, 10) + '.json';
|
var fileName = 'n8n-backup-' + new Date().toISOString().slice(0, 10) + '.json';
|
||||||
|
|
||||||
// Create a download link
|
// Create a download link
|
||||||
var a = document.createElement('a');
|
var a = document.createElement('a');
|
||||||
var blob = new Blob([JSON.stringify(backupData)], {type: 'application/json'});
|
var blob = new Blob([JSON.stringify(backupData)], {type: 'application/json'});
|
||||||
var url = window.URL.createObjectURL(blob);
|
var url = window.URL.createObjectURL(blob);
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = fileName;
|
a.download = fileName;
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
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',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Error handler
|
||||||
|
function(error) {
|
||||||
|
$scope.cyberpanelLoading = true;
|
||||||
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
new PNotify({
|
|
||||||
title: 'Success!',
|
|
||||||
text: 'Backup created and downloaded successfully.',
|
|
||||||
type: 'success'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Operation Failed!',
|
title: 'Operation Failed!',
|
||||||
text: response.data.error_message,
|
text: 'Connection disrupted, refresh the page.',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}, function(response) {
|
|
||||||
$scope.cyberpanelLoading = true;
|
|
||||||
$('#cyberpanelLoading').hide();
|
|
||||||
|
|
||||||
new PNotify({
|
console.error('Error creating backup:', error);
|
||||||
title: 'Operation Failed!',
|
}
|
||||||
text: 'Connection disrupted, refresh the page.',
|
);
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Restore from a backup
|
// Restore from a backup
|
||||||
@@ -447,6 +454,7 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
|
|
||||||
// Read the backup file
|
// Read the backup file
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = function(e) {
|
reader.onload = function(e) {
|
||||||
try {
|
try {
|
||||||
var backupData = JSON.parse(e.target.result);
|
var backupData = JSON.parse(e.target.result);
|
||||||
@@ -464,36 +472,43 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$http.post(url, data, config).then(function(response) {
|
$http.post(url, data, config).then(
|
||||||
$scope.cyberpanelLoading = true;
|
// Success handler
|
||||||
$('#cyberpanelLoading').hide();
|
function(response) {
|
||||||
|
$scope.cyberpanelLoading = true;
|
||||||
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
if (response.data.status === 1) {
|
if (response.data.status === 1) {
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Success!',
|
title: 'Success!',
|
||||||
text: 'Backup restored successfully.',
|
text: 'Backup restored successfully.',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Refresh workflows after restore
|
||||||
|
$scope.refreshWorkflows(container);
|
||||||
|
} else {
|
||||||
|
new PNotify({
|
||||||
|
title: 'Operation Failed!',
|
||||||
|
text: response.data.error_message || 'Unknown error occurred',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Error handler
|
||||||
|
function(error) {
|
||||||
|
$scope.cyberpanelLoading = true;
|
||||||
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
// Refresh workflows after restore
|
|
||||||
$scope.refreshWorkflows(container);
|
|
||||||
} else {
|
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Operation Failed!',
|
title: 'Operation Failed!',
|
||||||
text: response.data.error_message,
|
text: 'Connection disrupted, refresh the page.',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}, function(response) {
|
|
||||||
$scope.cyberpanelLoading = true;
|
|
||||||
$('#cyberpanelLoading').hide();
|
|
||||||
|
|
||||||
new PNotify({
|
console.error('Error restoring backup:', error);
|
||||||
title: 'Operation Failed!',
|
}
|
||||||
text: 'Connection disrupted, refresh the page.',
|
);
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
$scope.cyberpanelLoading = true;
|
$scope.cyberpanelLoading = true;
|
||||||
$('#cyberpanelLoading').hide();
|
$('#cyberpanelLoading').hide();
|
||||||
@@ -503,6 +518,8 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
text: 'Invalid backup file format: ' + error.message,
|
text: 'Invalid backup file format: ' + error.message,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.error('Error parsing backup file:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -205,4 +205,5 @@ urlpatterns = [
|
|||||||
path('n8n/get_workflows', views.n8n_api.get_n8n_workflows, name='get_n8n_workflows'),
|
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/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/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'),
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user