mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-07 22:06:05 +01:00
fix issue with design on n8n page
This commit is contained in:
@@ -11,41 +11,11 @@ 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:
|
||||||
@@ -66,35 +36,18 @@ 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(self, workflow_id, active):
|
def toggle_workflow_active(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:
|
||||||
url = f"{self.base_url}/rest/workflows/{workflow_id}/activate"
|
response = self.client.patch(f"{self.base_url}/rest/workflows/{workflow_id}/activate",
|
||||||
if not active:
|
json={"active": active})
|
||||||
url = f"{self.base_url}/rest/workflows/{workflow_id}/deactivate"
|
if response.status_code in [200, 201]:
|
||||||
|
return True
|
||||||
response = self.client.post(url)
|
return False
|
||||||
return response.status_code in [200, 201]
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.writeToFile(f"Error toggling workflow: {str(e)}")
|
logging.writeToFile(f"Error toggling workflow status: {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:
|
||||||
@@ -105,81 +58,25 @@ 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:
|
|
||||||
Dict containing backup data or error information
|
|
||||||
- On success: {'success': True, 'data': backup_data}
|
|
||||||
- On failure: {'success': False, 'error': error_message}
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
response = self.client.post(
|
response = self.client.post(f"{self.base_url}/rest/export",
|
||||||
f"{self.base_url}/rest/export",
|
|
||||||
json={
|
json={
|
||||||
"includeCredentials": include_credentials,
|
"includeCredentials": include_credentials,
|
||||||
"includeExecutions": include_executions
|
"includeExecutions": include_executions
|
||||||
},
|
})
|
||||||
timeout=30 # Add a 30 second timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
# Validate the response contains expected data
|
return response.json()
|
||||||
backup_data = response.json()
|
return None
|
||||||
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}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = f"Error creating backup: {str(e)}"
|
logging.writeToFile(f"Error creating backup: {str(e)}")
|
||||||
logging.writeToFile(error_msg)
|
return None
|
||||||
return {'success': False, 'error': error_msg}
|
|
||||||
|
|
||||||
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(
|
response = self.client.post(f"{self.base_url}/rest/import",
|
||||||
f"{self.base_url}/rest/import",
|
json=backup_data)
|
||||||
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)}")
|
||||||
@@ -223,15 +120,6 @@ 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
|
||||||
@@ -247,7 +135,6 @@ 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)
|
||||||
@@ -288,19 +175,18 @@ 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
|
# Toggle workflow status
|
||||||
success = n8n_api.toggle_workflow(workflow_id, active)
|
success = n8n_api.toggle_workflow_active(workflow_id, active)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 1,
|
'status': 1,
|
||||||
'workflow_id': workflow_id,
|
'message': f"Workflow {'activated' if active else 'deactivated'} successfully"
|
||||||
'active': active
|
|
||||||
}))
|
}))
|
||||||
else:
|
else:
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'error_message': f'Failed to {"activate" if active else "deactivate"} workflow'
|
'error_message': 'Failed to update workflow status'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
@@ -308,7 +194,6 @@ 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)
|
||||||
@@ -327,97 +212,6 @@ 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
|
|
||||||
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
|
# Get container info
|
||||||
client = docker.from_env()
|
client = docker.from_env()
|
||||||
container = client.containers.get(container_id)
|
container = client.containers.get(container_id)
|
||||||
@@ -440,17 +234,18 @@ def restore_n8n_backup(request):
|
|||||||
# Create N8nAPI instance
|
# Create N8nAPI instance
|
||||||
n8n_api = N8nAPI(container, 'localhost', n8n_port)
|
n8n_api = N8nAPI(container, 'localhost', n8n_port)
|
||||||
|
|
||||||
# Restore backup
|
# Create backup
|
||||||
success = n8n_api.restore_backup(backup_data)
|
backup = n8n_api.create_backup(include_credentials, include_executions)
|
||||||
|
|
||||||
if success:
|
if backup:
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 1
|
'status': 1,
|
||||||
|
'backup': backup
|
||||||
}))
|
}))
|
||||||
else:
|
else:
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'error_message': 'Failed to restore backup'
|
'error_message': 'Failed to create backup'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return HttpResponse(json.dumps({
|
return HttpResponse(json.dumps({
|
||||||
@@ -458,115 +253,7 @@ def restore_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 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)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@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
|
|
||||||
}))
|
|
||||||
@@ -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) {
|
app.controller('ListDockersitecontainer', function ($scope, $http) {
|
||||||
$scope.cyberPanelLoading = true;
|
$scope.cyberPanelLoading = true;
|
||||||
$scope.conatinerview = true;
|
$scope.conatinerview = true;
|
||||||
$scope.JSON = window.JSON; // Make JSON available to templates
|
|
||||||
$('#cyberpanelLoading').hide();
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
// Format bytes to human readable
|
// Format bytes to human readable
|
||||||
@@ -378,17 +367,18 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
|
|
||||||
// Create a backup
|
// Create a backup
|
||||||
$scope.createBackup = function(container) {
|
$scope.createBackup = function(container) {
|
||||||
// Initialize backup options if they don't exist
|
|
||||||
$scope.initBackupOptions(container);
|
|
||||||
|
|
||||||
$scope.cyberpanelLoading = false;
|
$scope.cyberpanelLoading = false;
|
||||||
$('#cyberpanelLoading').show();
|
$('#cyberpanelLoading').show();
|
||||||
|
|
||||||
|
// Initialize backup options if they don't exist
|
||||||
|
$scope.initBackupOptions(container);
|
||||||
|
|
||||||
var url = "/websites/n8n/create_backup";
|
var url = "/websites/n8n/create_backup";
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
'container_id': container.id,
|
'container_id': container.id,
|
||||||
'include_credentials': container.backupOptions.includeCredentials
|
'include_credentials': container.backupOptions.includeCredentials,
|
||||||
|
'include_executions': container.backupOptions.includeExecutions
|
||||||
};
|
};
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
@@ -397,8 +387,7 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$http.post(url, data, config)
|
$http.post(url, data, config).then(function(response) {
|
||||||
.then(function(response) {
|
|
||||||
$scope.cyberpanelLoading = true;
|
$scope.cyberpanelLoading = true;
|
||||||
$('#cyberpanelLoading').hide();
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
@@ -424,146 +413,19 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
} else {
|
} else {
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Operation Failed!',
|
title: 'Operation Failed!',
|
||||||
text: response.data.error_message || 'Unknown error occurred during backup creation',
|
text: response.data.error_message,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
console.error('Backup creation failed:', response.data);
|
|
||||||
}
|
}
|
||||||
})
|
}, function(response) {
|
||||||
.catch(function(error) {
|
|
||||||
$scope.cyberpanelLoading = true;
|
$scope.cyberpanelLoading = true;
|
||||||
$('#cyberpanelLoading').hide();
|
$('#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Operation Failed!',
|
title: 'Operation Failed!',
|
||||||
text: errorMessage,
|
text: 'Connection disrupted, refresh the page.',
|
||||||
type: 'error'
|
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 = "<h4>Diagnostic Results</h4>";
|
|
||||||
summaryMessage += "<ul>";
|
|
||||||
|
|
||||||
if (!diagnostics.container_exists) {
|
|
||||||
summaryMessage += "<li>Container not found</li>";
|
|
||||||
} else {
|
|
||||||
summaryMessage += "<li>Container exists: <span style='color:green'>✓</span></li>";
|
|
||||||
summaryMessage += "<li>Container status: " + diagnostics.container_status + "</li>";
|
|
||||||
summaryMessage += "<li>Container running: " + (diagnostics.container_running ? "<span style='color:green'>✓</span>" : "<span style='color:red'>✗</span>") + "</li>";
|
|
||||||
|
|
||||||
if (diagnostics.n8n_port_found) {
|
|
||||||
summaryMessage += "<li>n8n port found (" + diagnostics.n8n_port + "): <span style='color:green'>✓</span></li>";
|
|
||||||
} else {
|
|
||||||
summaryMessage += "<li>n8n port (5678) not mapped: <span style='color:red'>✗</span></li>";
|
|
||||||
var portMappingsString = JSON.stringify(diagnostics.port_mappings, null, 2);
|
|
||||||
summaryMessage += "<li>Available ports: <pre>" + portMappingsString + "</pre></li>";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diagnostics.container_running && diagnostics.n8n_port_found) {
|
|
||||||
if (diagnostics.api_accessible) {
|
|
||||||
summaryMessage += "<li>n8n API accessible: <span style='color:green'>✓</span></li>";
|
|
||||||
summaryMessage += "<li>Workflows endpoint: " + (diagnostics.workflows_accessible ? "<span style='color:green'>✓</span>" : "<span style='color:red'>✗</span>") + "</li>";
|
|
||||||
summaryMessage += "<li>Credentials endpoint: " + (diagnostics.credentials_accessible ? "<span style='color:green'>✓</span>" : "<span style='color:red'>✗</span>") + "</li>";
|
|
||||||
summaryMessage += "<li>Export endpoint: " + (diagnostics.export_accessible ? "<span style='color:green'>✓</span>" : "<span style='color:red'>✗</span>") + "</li>";
|
|
||||||
} else {
|
|
||||||
summaryMessage += "<li>n8n API accessible: <span style='color:red'>✗</span></li>";
|
|
||||||
if (diagnostics.api_error) {
|
|
||||||
summaryMessage += "<li>Error: " + diagnostics.api_error + "</li>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
summaryMessage += "</ul>";
|
|
||||||
|
|
||||||
// Display diagnostic results in a modal
|
|
||||||
var modal = "<div class='modal fade' id='diagnosticModal' tabindex='-1' role='dialog' aria-labelledby='diagnosticModalLabel'>" +
|
|
||||||
"<div class='modal-dialog' role='document'>" +
|
|
||||||
"<div class='modal-content'>" +
|
|
||||||
"<div class='modal-header'>" +
|
|
||||||
"<button type='button' class='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>×</span></button>" +
|
|
||||||
"<h4 class='modal-title' id='diagnosticModalLabel'>n8n Diagnostics</h4>" +
|
|
||||||
"</div>" +
|
|
||||||
"<div class='modal-body'>" + summaryMessage + "</div>" +
|
|
||||||
"<div class='modal-footer'>" +
|
|
||||||
"<button type='button' class='btn btn-default' data-dismiss='modal'>Close</button>" +
|
|
||||||
"</div>" +
|
|
||||||
"</div>" +
|
|
||||||
"</div>" +
|
|
||||||
"</div>";
|
|
||||||
|
|
||||||
// Add modal to document if it doesn't exist
|
|
||||||
if ($('#diagnosticModal').length === 0) {
|
|
||||||
$('body').append(modal);
|
|
||||||
} else {
|
|
||||||
$('#diagnosticModal .modal-body').html(summaryMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show modal
|
|
||||||
$('#diagnosticModal').modal('show');
|
|
||||||
|
|
||||||
} else {
|
|
||||||
new PNotify({
|
|
||||||
title: 'Diagnostic Failed',
|
|
||||||
text: response.data.error_message || 'Failed to diagnose n8n container',
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
new PNotify({
|
|
||||||
title: 'Diagnostic Failed',
|
|
||||||
text: errorMessage,
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
|
|
||||||
console.error('Error during diagnosis:', error);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -571,7 +433,7 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
$scope.restoreFromBackup = function(container) {
|
$scope.restoreFromBackup = function(container) {
|
||||||
// Check if a file has been selected
|
// Check if a file has been selected
|
||||||
var fileInput = document.getElementById('backupFile');
|
var fileInput = document.getElementById('backupFile');
|
||||||
if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
|
if (!fileInput.files || fileInput.files.length === 0) {
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Error!',
|
title: 'Error!',
|
||||||
text: 'Please select a backup file to restore.',
|
text: 'Please select a backup file to restore.',
|
||||||
@@ -585,7 +447,6 @@ 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);
|
||||||
@@ -603,8 +464,7 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$http.post(url, data, config)
|
$http.post(url, data, config).then(function(response) {
|
||||||
.then(function(response) {
|
|
||||||
$scope.cyberpanelLoading = true;
|
$scope.cyberpanelLoading = true;
|
||||||
$('#cyberpanelLoading').hide();
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
@@ -615,37 +475,24 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset file input
|
|
||||||
fileInput.value = '';
|
|
||||||
|
|
||||||
// Refresh workflows after restore
|
// Refresh workflows after restore
|
||||||
setTimeout(function() {
|
|
||||||
$scope.refreshWorkflows(container);
|
$scope.refreshWorkflows(container);
|
||||||
}, 1000);
|
|
||||||
} else {
|
} else {
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Operation Failed!',
|
title: 'Operation Failed!',
|
||||||
text: response.data.error_message || 'Unknown error occurred',
|
text: response.data.error_message,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
}, function(response) {
|
||||||
.catch(function(error) {
|
|
||||||
$scope.cyberpanelLoading = true;
|
$scope.cyberpanelLoading = true;
|
||||||
$('#cyberpanelLoading').hide();
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
var errorMessage = 'Connection disrupted, refresh the page.';
|
|
||||||
if (error.data && error.data.error_message) {
|
|
||||||
errorMessage = error.data.error_message;
|
|
||||||
}
|
|
||||||
|
|
||||||
new PNotify({
|
new PNotify({
|
||||||
title: 'Operation Failed!',
|
title: 'Operation Failed!',
|
||||||
text: errorMessage,
|
text: 'Connection disrupted, refresh the page.',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
|
|
||||||
console.error('Error restoring backup:', error);
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
$scope.cyberpanelLoading = true;
|
$scope.cyberpanelLoading = true;
|
||||||
@@ -656,22 +503,9 @@ 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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.onerror = function() {
|
|
||||||
$scope.cyberpanelLoading = true;
|
|
||||||
$('#cyberpanelLoading').hide();
|
|
||||||
|
|
||||||
new PNotify({
|
|
||||||
title: 'Error!',
|
|
||||||
text: 'Failed to read the backup file.',
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsText(fileInput.files[0]);
|
reader.readAsText(fileInput.files[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1157,23 +991,18 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
$scope.cyberpanelLoading = true;
|
$scope.cyberpanelLoading = true;
|
||||||
$('#cyberpanelLoading').hide();
|
$('#cyberpanelLoading').hide();
|
||||||
|
|
||||||
// Sample response data
|
// Simulate response
|
||||||
var headers = {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
'server': 'n8n'
|
|
||||||
};
|
|
||||||
|
|
||||||
var body = {
|
|
||||||
success: true,
|
|
||||||
executionId: '123456789'
|
|
||||||
};
|
|
||||||
|
|
||||||
// Simulate response with pre-stringified JSON
|
|
||||||
container.webhookTools.testResult = {
|
container.webhookTools.testResult = {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: 'OK',
|
statusText: 'OK',
|
||||||
headers: JSON.stringify(headers, null, 2),
|
headers: {
|
||||||
body: JSON.stringify(body, null, 2)
|
'content-type': 'application/json',
|
||||||
|
'server': 'n8n'
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
success: true,
|
||||||
|
executionId: '123456789'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
new PNotify({
|
new PNotify({
|
||||||
@@ -1552,23 +1381,4 @@ app.controller('ListDockersitecontainer', function ($scope, $http) {
|
|||||||
|
|
||||||
// Add location service to the controller for the n8n URL
|
// Add location service to the controller for the n8n URL
|
||||||
$scope.location = window.location;
|
$scope.location = window.location;
|
||||||
|
|
||||||
// Function to view execution data details
|
|
||||||
$scope.viewExecutionData = function(execution) {
|
|
||||||
// Pre-stringify JSON data for template
|
|
||||||
$scope.selectedExecution = {
|
|
||||||
id: execution.id,
|
|
||||||
status: execution.status,
|
|
||||||
startedAt: execution.startedAt,
|
|
||||||
duration: execution.duration,
|
|
||||||
mode: execution.mode,
|
|
||||||
inputData: JSON.stringify(execution.inputData || {}, null, 2),
|
|
||||||
outputData: JSON.stringify(execution.outputData || {}, null, 2),
|
|
||||||
error: execution.error
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($scope.selectedExecution.error && $scope.selectedExecution.error.stack) {
|
|
||||||
$scope.selectedExecution.error.stack = $scope.selectedExecution.error.stack.replace(/\\n/g, '\n');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
@@ -607,15 +607,10 @@
|
|||||||
<button class="btn btn-danger btn-sm" ng-click="handleAction('stop', web)" ng-if="web.status === 'running'" title="Stop Container">
|
<button class="btn btn-danger btn-sm" ng-click="handleAction('stop', web)" ng-if="web.status === 'running'" title="Stop Container">
|
||||||
<i class="fa fa-stop"></i> Stop
|
<i class="fa fa-stop"></i> Stop
|
||||||
</button>
|
</button>
|
||||||
<div class="nav navbar-right btn-group">
|
<button class="btn btn-primary btn-sm" ng-click="openSettings(web)" title="Container Settings">
|
||||||
<a href="javascript:void(0)" class="btn btn-sm btn-primary" ng-click="openSettings(container)">
|
<i class="fa fa-cog"></i> Settings
|
||||||
<i class="fas fa-cog"></i> Settings
|
|
||||||
</a>
|
|
||||||
<button class="btn btn-primary" ng-click="diagnoseN8n(web)">
|
|
||||||
<i class="fas fa-stethoscope"></i> Run Diagnostics
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-muted">
|
<p class="text-muted">
|
||||||
{% trans "Container ID" %}: <code>{$ web.id $}</code>
|
{% trans "Container ID" %}: <code>{$ web.id $}</code>
|
||||||
@@ -710,23 +705,12 @@
|
|||||||
<button class="quick-action-btn btn-danger" ng-click="handleAction('stop', web)" ng-if="web.status === 'running'">
|
<button class="quick-action-btn btn-danger" ng-click="handleAction('stop', web)" ng-if="web.status === 'running'">
|
||||||
<i class="fa fa-stop"></i> Stop
|
<i class="fa fa-stop"></i> Stop
|
||||||
</button>
|
</button>
|
||||||
<div class="nav navbar-right btn-group">
|
|
||||||
<a href="javascript:void(0)" class="btn btn-sm btn-primary" ng-click="openSettings(container)">
|
|
||||||
<i class="fas fa-cog"></i> Settings
|
|
||||||
</a>
|
|
||||||
<button class="btn btn-primary" ng-click="diagnoseN8n(web)">
|
|
||||||
<i class="fas fa-stethoscope"></i> Run Diagnostics
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button class="quick-action-btn btn-primary" ng-click="openSettings(web)">
|
<button class="quick-action-btn btn-primary" ng-click="openSettings(web)">
|
||||||
<i class="fa fa-cog"></i> Settings
|
<i class="fa fa-cog"></i> Settings
|
||||||
</button>
|
</button>
|
||||||
<a class="quick-action-btn btn-info" href="http://{$ location.hostname $}:{$ web.ports['5678/tcp'][0].HostPort $}" target="_blank" ng-if="web.status === 'running'">
|
<a class="quick-action-btn btn-info" href="http://{$ location.hostname $}:{$ web.ports['5678/tcp'][0].HostPort $}" target="_blank" ng-if="web.status === 'running'">
|
||||||
<i class="fa fa-external-link"></i> Open n8n
|
<i class="fa fa-external-link"></i> Open n8n
|
||||||
</a>
|
</a>
|
||||||
<button class="quick-action-btn btn-warning" ng-click="diagnoseN8n(web)" title="Run diagnostics">
|
|
||||||
<i class="fa fa-stethoscope"></i> Diagnose
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -847,7 +831,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label">Select backup file</label>
|
<label class="control-label">Select backup file</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="file" id="backupFile" class="form-control">
|
<input type="file" id="backupFile" class="form-control" ng-model="web.restoreFile">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-primary" ng-click="restoreFromBackup(web)">
|
<button class="btn btn-primary" ng-click="restoreFromBackup(web)">
|
||||||
<i class="fa fa-upload"></i> Restore
|
<i class="fa fa-upload"></i> Restore
|
||||||
@@ -1300,11 +1284,11 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label>Response Headers</label>
|
<label>Response Headers</label>
|
||||||
<pre class="bg-light p-3">{$ web.webhookTools.testResult.headers $}</pre>
|
<pre class="bg-light p-3">{$ web.webhookTools.testResult.headers | json $}</pre>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Response Body</label>
|
<label>Response Body</label>
|
||||||
<pre class="bg-light p-3">{$ web.webhookTools.testResult.body $}</pre>
|
<pre class="bg-light p-3">{$ web.webhookTools.testResult.body | json $}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1717,10 +1701,10 @@
|
|||||||
|
|
||||||
<div class="tab-content mt-3">
|
<div class="tab-content mt-3">
|
||||||
<div id="input" class="tab-pane fade in active">
|
<div id="input" class="tab-pane fade in active">
|
||||||
<pre class="bg-light p-3">{$ selectedExecution.inputData $}</pre>
|
<pre class="bg-light p-3">{$ selectedExecution.inputData | json $}</pre>
|
||||||
</div>
|
</div>
|
||||||
<div id="output" class="tab-pane fade">
|
<div id="output" class="tab-pane fade">
|
||||||
<pre class="bg-light p-3">{$ selectedExecution.outputData $}</pre>
|
<pre class="bg-light p-3">{$ selectedExecution.outputData | json $}</pre>
|
||||||
</div>
|
</div>
|
||||||
<div id="error" class="tab-pane fade" ng-if="selectedExecution.error">
|
<div id="error" class="tab-pane fade" ng-if="selectedExecution.error">
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
@@ -1879,127 +1863,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane" id="n8nDiagnostics">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="card-title">N8n API Diagnostics</h5>
|
|
||||||
<div class="card-tools">
|
|
||||||
<button class="btn btn-primary" ng-click="diagnoseN8n(web)">
|
|
||||||
<i class="fas fa-stethoscope"></i> Run Diagnostics
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div ng-if="container.showDiagnostics">
|
|
||||||
<div class="alert" ng-class="{'alert-success': container.diagnosticResults.api_accessible, 'alert-danger': !container.diagnosticResults.api_accessible}">
|
|
||||||
<h5><i class="icon" ng-class="{'fas fa-check': container.diagnosticResults.api_accessible, 'fas fa-ban': !container.diagnosticResults.api_accessible}"></i>
|
|
||||||
N8n API Status: <span ng-if="container.diagnosticResults.api_accessible">Accessible</span><span ng-if="!container.diagnosticResults.api_accessible">Not Accessible</span>
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="card-title">Container Status</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<table class="table table-bordered">
|
|
||||||
<tr>
|
|
||||||
<th>Container Exists</th>
|
|
||||||
<td><i class="fas" ng-class="{'fa-check text-success': container.diagnosticResults.container_exists, 'fa-times text-danger': !container.diagnosticResults.container_exists}"></i></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Container Status</th>
|
|
||||||
<td>{{container.diagnosticResults.container_status}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Container Running</th>
|
|
||||||
<td><i class="fas" ng-class="{'fa-check text-success': container.diagnosticResults.container_running, 'fa-times text-danger': !container.diagnosticResults.container_running}"></i></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Container Name</th>
|
|
||||||
<td>{{container.diagnosticResults.container_names}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="card-title">N8n API Connectivity</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<table class="table table-bordered">
|
|
||||||
<tr>
|
|
||||||
<th>N8n Port Found</th>
|
|
||||||
<td><i class="fas" ng-class="{'fa-check text-success': container.diagnosticResults.n8n_port_found, 'fa-times text-danger': !container.diagnosticResults.n8n_port_found}"></i></td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-if="container.diagnosticResults.n8n_port_found">
|
|
||||||
<th>N8n Port</th>
|
|
||||||
<td>{{container.diagnosticResults.n8n_port}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>API Accessible</th>
|
|
||||||
<td><i class="fas" ng-class="{'fa-check text-success': container.diagnosticResults.api_accessible, 'fa-times text-danger': !container.diagnosticResults.api_accessible}"></i></td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-if="container.diagnosticResults.api_accessible">
|
|
||||||
<th>Workflows API</th>
|
|
||||||
<td><i class="fas" ng-class="{'fa-check text-success': container.diagnosticResults.workflows_accessible, 'fa-times text-danger': !container.diagnosticResults.workflows_accessible}"></i></td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-if="container.diagnosticResults.api_accessible">
|
|
||||||
<th>Credentials API</th>
|
|
||||||
<td><i class="fas" ng-class="{'fa-check text-success': container.diagnosticResults.credentials_accessible, 'fa-times text-danger': !container.diagnosticResults.credentials_accessible}"></i></td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-if="container.diagnosticResults.api_accessible">
|
|
||||||
<th>Export API</th>
|
|
||||||
<td><i class="fas" ng-class="{'fa-check text-success': container.diagnosticResults.export_accessible, 'fa-times text-danger': !container.diagnosticResults.export_accessible}"></i></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row" ng-if="container.diagnosticResults.port_mappings">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="card-title">Port Mappings</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<pre>{$ JSON.stringify(container.diagnosticResults.port_mappings) $}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row" ng-if="container.diagnosticResults.api_error">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="card-title">API Error</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<pre class="text-danger">{{JSON.stringify(container.diagnosticResults.api_error, null, 2)}}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ng-if="!container.showDiagnostics">
|
|
||||||
<p>Click "Run Diagnostics" to check N8n API connectivity.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -202,9 +202,7 @@ urlpatterns = [
|
|||||||
path('<domain>', views.domain, name='domain'),
|
path('<domain>', views.domain, name='domain'),
|
||||||
|
|
||||||
# N8N API endpoints
|
# N8N API endpoints
|
||||||
path('websites/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('websites/n8n/toggle_workflow', views.n8n_api.toggle_workflow, name='toggle_workflow'),
|
path('n8n/toggle_workflow', views.n8n_api.toggle_workflow, name='toggle_workflow'),
|
||||||
path('websites/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('websites/n8n/restore_backup', views.n8n_api.restore_n8n_backup, name='restore_n8n_backup'),
|
|
||||||
path('websites/n8n/diagnose', views.n8n_api.diagnose_n8n_api, name='diagnose_n8n_api'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user