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

409 lines
18 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'
}))
# 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'
}))
# Set up n8n base URL
host_ip = request.get_host().split(':')[0]
# Try different authentication methods
# Method 1: Try direct access to REST API (in some setups, this works without auth)
direct_api_url = f"http://{host_ip}:{n8n_port}/rest"
# Method 2: Try API v1 with various auth methods
api_v1_url = f"http://{host_ip}:{n8n_port}/api/v1"
# Extract authentication info from environment variables
environment_vars = container_info.get('Config', {}).get('Env', [])
# Initialize default headers
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
# Variables for auth info
n8n_basic_auth_user = None
n8n_basic_auth_password = None
n8n_api_key = None
n8n_jwt_auth = None
# Extract auth information from environment variables
for env_var in environment_vars:
if env_var.startswith('N8N_API_KEY='):
n8n_api_key = env_var.split('=', 1)[1]
elif env_var.startswith('N8N_BASIC_AUTH_USER='):
n8n_basic_auth_user = env_var.split('=', 1)[1]
elif env_var.startswith('N8N_BASIC_AUTH_PASSWORD='):
n8n_basic_auth_password = env_var.split('=', 1)[1]
elif env_var.startswith('N8N_JWT_AUTH_ACTIVE=') and 'true' in env_var.lower():
n8n_jwt_auth = True
elif env_var.startswith('N8N_AUTH_ACTIVE=') and 'true' not in env_var.lower():
# If auth is explicitly disabled, we can use direct access
pass
# Log the authentication methods available
logging.writeToFile(f"N8N auth methods - API Key: {n8n_api_key is not None}, Basic Auth: {n8n_basic_auth_user is not None}, JWT: {n8n_jwt_auth is not None}")
# Handle different operations
if operation == 'create_backup':
try:
# Get backup options from request
backup_options = data.get('options', {})
include_credentials = backup_options.get('includeCredentials', True)
include_executions = backup_options.get('includeExecutions', False)
# Initialize the backup data dictionary
backup_data = {}
# Try to fetch workflows using different authentication methods
workflows_response = None
auth = None
# Try direct access first
try:
logging.writeToFile(f"Trying direct access to n8n REST API")
workflows_response = requests.get(f"{direct_api_url}/workflows", headers=headers, timeout=5)
if workflows_response.status_code == 200:
logging.writeToFile(f"Direct REST API access successful")
api_url = direct_api_url
except Exception as e:
logging.writeToFile(f"Direct REST API access failed: {str(e)}")
# If direct access failed, try with API key
if not workflows_response or workflows_response.status_code != 200:
if n8n_api_key:
try:
logging.writeToFile(f"Trying API key authentication")
api_headers = headers.copy()
api_headers['X-N8N-API-KEY'] = n8n_api_key
workflows_response = requests.get(f"{api_v1_url}/workflows", headers=api_headers, timeout=5)
if workflows_response.status_code == 200:
logging.writeToFile(f"API key authentication successful")
api_url = api_v1_url
headers = api_headers
except Exception as e:
logging.writeToFile(f"API key authentication failed: {str(e)}")
# If API key failed, try basic auth
if not workflows_response or workflows_response.status_code != 200:
if n8n_basic_auth_user and n8n_basic_auth_password:
try:
logging.writeToFile(f"Trying basic authentication")
auth = (n8n_basic_auth_user, n8n_basic_auth_password)
workflows_response = requests.get(f"{api_v1_url}/workflows", headers=headers, auth=auth, timeout=5)
if workflows_response.status_code == 200:
logging.writeToFile(f"Basic authentication successful")
api_url = api_v1_url
except Exception as e:
logging.writeToFile(f"Basic authentication failed: {str(e)}")
# If all authentication methods failed
if not workflows_response or workflows_response.status_code != 200:
# Last resort: try without any authentication at the Admin UI port
try:
logging.writeToFile(f"Trying Admin UI direct access")
admin_url = f"http://{host_ip}:{n8n_port}"
workflows_response = requests.get(f"{admin_url}/rest/workflows", headers=headers, timeout=5)
if workflows_response.status_code == 200:
logging.writeToFile(f"Admin UI direct access successful")
api_url = f"{admin_url}/rest"
except Exception as e:
logging.writeToFile(f"Admin UI direct access failed: {str(e)}")
# Check if any method succeeded
if not workflows_response or workflows_response.status_code != 200:
error_message = "Failed to authenticate with n8n API. Please check the container logs for more information."
if workflows_response:
error_message = f"Authentication failed: {workflows_response.text}"
logging.writeToFile(f"All authentication methods failed: {error_message}")
return HttpResponse(json.dumps({
'status': 0,
'error_message': error_message
}))
# If we made it here, one of the authentication methods worked
backup_data['workflows'] = workflows_response.json()
# Get credentials if requested
if include_credentials:
try:
if auth:
credentials_response = requests.get(f"{api_url}/credentials", headers=headers, auth=auth, timeout=5)
else:
credentials_response = requests.get(f"{api_url}/credentials", headers=headers, timeout=5)
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}")
except Exception as e:
logging.writeToFile(f"Error fetching credentials: {str(e)}")
# Get execution data if requested
if include_executions:
try:
if auth:
executions_response = requests.get(f"{api_url}/executions", headers=headers, auth=auth, timeout=5)
else:
executions_response = requests.get(f"{api_url}/executions", headers=headers, timeout=5)
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}")
except Exception as e:
logging.writeToFile(f"Error fetching executions: {str(e)}")
# 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':
# Similar approach for restore operation...
# Will implement the same authentication handling for restore operation
return HttpResponse(json.dumps({
'status': 0,
'error_message': 'Restore operation temporarily unavailable while we update authentication methods'
}))
else:
return HttpResponse(json.dumps({
'status': 0,
'error_message': f'Unknown operation: {operation}'
}))
return HttpResponse('Not allowed')
except Exception as e:
logging.writeToFile(f"Error in n8n_container_operation: {str(e)}")
return HttpResponse(json.dumps({
'status': 0,
'error_message': str(e)
}))