Files
CyberPanel/websiteFunctions/dockerviews.py
2025-04-12 14:12:21 +05:00

417 lines
19 KiB
Python

import json
import docker
from django.http import HttpResponse
from .models import DockerSites
from loginSystem.models import Administrator
from plogical.acl import ACLManager
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 datetime
import requests
def require_login(view_func):
def wrapper(request, *args, **kwargs):
try:
userID = request.session['userID']
return view_func(request, *args, **kwargs)
except KeyError:
return redirect(loadLoginPage)
return wrapper
class DockerManager:
def __init__(self):
self.client = docker.from_env()
def get_container(self, container_id):
try:
return self.client.containers.get(container_id)
except docker.errors.NotFound:
return None
except Exception as e:
logging.writeToFile(f"Error getting container {container_id}: {str(e)}")
return None
@csrf_exempt
@require_login
def startContainer(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'
}))
container.start()
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 stopContainer(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'
}))
container.stop()
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 restartContainer(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'
}))
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 n8n_container_operation(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')
operation = data.get('operation')
# Get the container
docker_manager = DockerManager()
container = docker_manager.get_container(container_id)
if not container:
return HttpResponse(json.dumps({
'status': 0,
'error_message': 'Container not found'
}))
# Handle different operations
if operation == 'create_backup':
try:
# Determine the port where n8n is running
container_info = container.attrs
port_bindings = container_info.get('HostConfig', {}).get('PortBindings', {})
n8n_port = None
for container_port, host_ports in port_bindings.items():
if container_port.startswith('5678'):
n8n_port = host_ports[0]['HostPort']
break
if not n8n_port:
return HttpResponse(json.dumps({
'status': 0,
'error_message': 'Could not determine n8n port'
}))
# Get backup options from request
backup_options = data.get('options', {})
include_credentials = backup_options.get('includeCredentials', True)
include_executions = backup_options.get('includeExecutions', False)
# Set up n8n API URL
host_ip = request.get_host().split(':')[0]
n8n_base_url = f"http://{host_ip}:{n8n_port}/api/v1"
# Initialize the backup data dictionary
backup_data = {}
# Fetch workflows
# Get n8n workflows (no authentication required for basic n8n)
workflows_response = requests.get(f"{n8n_base_url}/workflows")
if workflows_response.status_code == 200:
backup_data['workflows'] = workflows_response.json()
else:
logging.writeToFile(f"Failed to fetch n8n workflows: {workflows_response.status_code} - {workflows_response.text}")
return HttpResponse(json.dumps({
'status': 0,
'error_message': f'Failed to fetch workflows: {workflows_response.text}'
}))
# Get credentials if requested
if include_credentials:
credentials_response = requests.get(f"{n8n_base_url}/credentials")
if credentials_response.status_code == 200:
backup_data['credentials'] = credentials_response.json()
else:
logging.writeToFile(f"Failed to fetch n8n credentials: {credentials_response.status_code} - {credentials_response.text}")
# Don't fail the whole backup just because credentials failed
# Get execution data if requested
if include_executions:
executions_response = requests.get(f"{n8n_base_url}/executions")
if executions_response.status_code == 200:
backup_data['executions'] = executions_response.json()
else:
logging.writeToFile(f"Failed to fetch n8n executions: {executions_response.status_code} - {executions_response.text}")
# Don't fail the whole backup just because executions failed
# Include metadata
backup_data['metadata'] = {
'timestamp': datetime.datetime.now().isoformat(),
'container_id': container_id,
'container_name': container.name,
'include_credentials': include_credentials,
'include_executions': include_executions
}
# Create a response with the backup data
return HttpResponse(json.dumps({
'status': 1,
'message': 'Backup created successfully',
'backup': backup_data,
'filename': f'n8n-backup-{container.name}-{datetime.datetime.now().strftime("%Y%m%d-%H%M%S")}.json'
}))
except Exception as e:
logging.writeToFile(f"Error creating n8n backup: {str(e)}")
return HttpResponse(json.dumps({
'status': 0,
'error_message': f'Error creating backup: {str(e)}'
}))
elif operation == 'restore_backup':
try:
# Determine the port where n8n is running
container_info = container.attrs
port_bindings = container_info.get('HostConfig', {}).get('PortBindings', {})
n8n_port = None
for container_port, host_ports in port_bindings.items():
if container_port.startswith('5678'):
n8n_port = host_ports[0]['HostPort']
break
if not n8n_port:
return HttpResponse(json.dumps({
'status': 0,
'error_message': 'Could not determine n8n port'
}))
# Get backup data from request
backup_data = data.get('backup_data')
if not backup_data:
return HttpResponse(json.dumps({
'status': 0,
'error_message': 'No backup data provided'
}))
# Set up n8n API URL
host_ip = request.get_host().split(':')[0]
n8n_base_url = f"http://{host_ip}:{n8n_port}/api/v1"
# Restore workflows
if 'workflows' in backup_data:
# First, get the list of existing workflows to avoid duplicates
existing_workflows_response = requests.get(f"{n8n_base_url}/workflows")
if existing_workflows_response.status_code != 200:
logging.writeToFile(f"Failed to fetch existing workflows: {existing_workflows_response.status_code} - {existing_workflows_response.text}")
return HttpResponse(json.dumps({
'status': 0,
'error_message': f'Failed to fetch existing workflows: {existing_workflows_response.text}'
}))
existing_workflows = existing_workflows_response.json()
existing_workflow_names = {wf['name']: wf['id'] for wf in existing_workflows}
# Now restore each workflow
for workflow in backup_data['workflows']:
# Remove ID from the backup data to create a new workflow
if 'id' in workflow:
workflow_id = workflow.pop('id')
# Check if workflow with the same name already exists
if workflow['name'] in existing_workflow_names:
# Update existing workflow
update_response = requests.put(
f"{n8n_base_url}/workflows/{existing_workflow_names[workflow['name']]}",
json=workflow
)
if update_response.status_code not in [200, 201]:
logging.writeToFile(f"Failed to update workflow: {update_response.status_code} - {update_response.text}")
else:
# Create new workflow
create_response = requests.post(
f"{n8n_base_url}/workflows",
json=workflow
)
if create_response.status_code not in [200, 201]:
logging.writeToFile(f"Failed to create workflow: {create_response.status_code} - {create_response.text}")
# Restore credentials if included in backup
if 'credentials' in backup_data:
# First, get existing credentials to avoid duplicates
existing_creds_response = requests.get(f"{n8n_base_url}/credentials")
if existing_creds_response.status_code == 200:
existing_creds = existing_creds_response.json()
existing_cred_names = {cred['name']: cred['id'] for cred in existing_creds}
# Now restore each credential
for credential in backup_data['credentials']:
# Remove ID from the backup data to create a new credential
if 'id' in credential:
credential_id = credential.pop('id')
# Check if credential with the same name already exists
if credential['name'] in existing_cred_names:
# Update existing credential
update_response = requests.put(
f"{n8n_base_url}/credentials/{existing_cred_names[credential['name']]}",
json=credential
)
if update_response.status_code not in [200, 201]:
logging.writeToFile(f"Failed to update credential: {update_response.status_code} - {update_response.text}")
else:
# Create new credential
create_response = requests.post(
f"{n8n_base_url}/credentials",
json=credential
)
if create_response.status_code not in [200, 201]:
logging.writeToFile(f"Failed to create credential: {create_response.status_code} - {create_response.text}")
else:
logging.writeToFile(f"Failed to fetch existing credentials: {existing_creds_response.status_code} - {existing_creds_response.text}")
return HttpResponse(json.dumps({
'status': 1,
'message': 'Backup restored successfully'
}))
except Exception as e:
logging.writeToFile(f"Error restoring n8n backup: {str(e)}")
return HttpResponse(json.dumps({
'status': 0,
'error_message': f'Error restoring backup: {str(e)}'
}))
else:
return HttpResponse(json.dumps({
'status': 0,
'error_message': f'Unknown operation: {operation}'
}))
return HttpResponse('Not allowed')
except Exception as e:
return HttpResponse(json.dumps({
'status': 0,
'error_message': str(e)
}))