mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-07 22:06:05 +01:00
additional n8n functions
This commit is contained in:
@@ -1197,6 +1197,7 @@
|
||||
<script src="{% static 'baseTemplate/custom-js/pnotify.custom.min.js' %}"></script>
|
||||
<script src="{% static 'packages/packages.js' %}?ver={{ version }}"></script>
|
||||
<script src="{% static 'websiteFunctions/websiteFunctions.js' %}?ver={{ version }}"></script>
|
||||
<script src="{% static 'websiteFunctions/dockerController.js' %}?ver={{ version }}"></script>
|
||||
<script src="{% static 'tuning/tuning.js' %}?ver={{ version }}"></script>
|
||||
<script src="{% static 'serverStatus/serverStatus.js' %}?ver={{ version }}"></script>
|
||||
<script src="{% static 'dns/dns.js' %}?ver={{ version }}"></script>
|
||||
|
||||
@@ -1151,37 +1151,6 @@ class ContainerManager(multi.Thread):
|
||||
'cpu_usage': container.stats(stream=False)['cpu_stats']['cpu_usage'].get('total_usage', 0)
|
||||
}
|
||||
|
||||
# Add N8N specific health metrics if this is an N8N container
|
||||
if 'n8n' in container.name.lower():
|
||||
try:
|
||||
# Get N8N port from environment variables or port bindings
|
||||
n8n_port = None
|
||||
for port_config in container_info.get('NetworkSettings', {}).get('Ports', {}).values():
|
||||
if port_config:
|
||||
n8n_port = port_config[0].get('HostPort')
|
||||
break
|
||||
|
||||
if n8n_port:
|
||||
# Try to get N8N health metrics from the API
|
||||
health_url = f"http://localhost:{n8n_port}/api/v1/health"
|
||||
response = requests.get(health_url, timeout=5)
|
||||
if response.status_code == 200:
|
||||
health_data = response.json()
|
||||
details['n8nStats'] = {
|
||||
'dbConnected': health_data.get('db', {}).get('status') == 'ok',
|
||||
'activeWorkflows': len(health_data.get('activeWorkflows', [])),
|
||||
'queuedExecutions': health_data.get('executionQueue', {}).get('waiting', 0),
|
||||
'lastExecution': health_data.get('lastExecution')
|
||||
}
|
||||
except:
|
||||
# If we can't get N8N stats, provide default values
|
||||
details['n8nStats'] = {
|
||||
'dbConnected': None,
|
||||
'activeWorkflows': 0,
|
||||
'queuedExecutions': 0,
|
||||
'lastExecution': None
|
||||
}
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None', 'data': [1, details]}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
@@ -1333,181 +1302,3 @@ class ContainerManager(multi.Thread):
|
||||
data_ret = {'removeImageStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def _get_backup_dir(self, container_id):
|
||||
"""Helper method to get the backup directory for a container"""
|
||||
return f"/home/docker/backups/{container_id}"
|
||||
|
||||
def createBackup(self, userID=None, data=None):
|
||||
try:
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if admin.acl.adminStatus != 1:
|
||||
return ACLManager.loadError()
|
||||
|
||||
container_id = data['id']
|
||||
client = docker.from_env()
|
||||
container = client.containers.get(container_id)
|
||||
|
||||
# Create backup directory if it doesn't exist
|
||||
backup_dir = self._get_backup_dir(container_id)
|
||||
os.makedirs(backup_dir, exist_ok=True)
|
||||
|
||||
# Create timestamp for backup
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
backup_file = f"{backup_dir}/backup_{timestamp}.tar.gz"
|
||||
|
||||
# Get N8N data directory from container mounts
|
||||
n8n_data_dir = None
|
||||
for mount in container.attrs.get('Mounts', []):
|
||||
if mount.get('Destination', '').endswith('/.n8n'):
|
||||
n8n_data_dir = mount.get('Source')
|
||||
break
|
||||
|
||||
if not n8n_data_dir:
|
||||
raise Exception("N8N data directory not found in container mounts")
|
||||
|
||||
# Create backup using tar
|
||||
backup_cmd = f"tar -czf {backup_file} -C {n8n_data_dir} ."
|
||||
result = subprocess.run(backup_cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"Backup failed: {result.stderr}")
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None', 'backup_file': backup_file}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
except Exception as e:
|
||||
data_ret = {'status': 0, 'error_message': str(e)}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
def listBackups(self, userID=None, data=None):
|
||||
try:
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if admin.acl.adminStatus != 1:
|
||||
return ACLManager.loadError()
|
||||
|
||||
container_id = data['id']
|
||||
backup_dir = self._get_backup_dir(container_id)
|
||||
|
||||
if not os.path.exists(backup_dir):
|
||||
backups = []
|
||||
else:
|
||||
backups = []
|
||||
for file in os.listdir(backup_dir):
|
||||
if file.startswith('backup_') and file.endswith('.tar.gz'):
|
||||
file_path = os.path.join(backup_dir, file)
|
||||
file_stat = os.stat(file_path)
|
||||
backups.append({
|
||||
'id': file,
|
||||
'date': datetime.fromtimestamp(file_stat.st_mtime).isoformat(),
|
||||
'size': file_stat.st_size
|
||||
})
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None', 'backups': backups}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
except Exception as e:
|
||||
data_ret = {'status': 0, 'error_message': str(e)}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
def restoreBackup(self, userID=None, data=None):
|
||||
try:
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if admin.acl.adminStatus != 1:
|
||||
return ACLManager.loadError()
|
||||
|
||||
container_id = data['id']
|
||||
backup_id = data['backup_id']
|
||||
client = docker.from_env()
|
||||
container = client.containers.get(container_id)
|
||||
|
||||
backup_dir = self._get_backup_dir(container_id)
|
||||
backup_file = os.path.join(backup_dir, backup_id)
|
||||
|
||||
if not os.path.exists(backup_file):
|
||||
raise Exception("Backup file not found")
|
||||
|
||||
# Get N8N data directory from container mounts
|
||||
n8n_data_dir = None
|
||||
for mount in container.attrs.get('Mounts', []):
|
||||
if mount.get('Destination', '').endswith('/.n8n'):
|
||||
n8n_data_dir = mount.get('Source')
|
||||
break
|
||||
|
||||
if not n8n_data_dir:
|
||||
raise Exception("N8N data directory not found in container mounts")
|
||||
|
||||
# Stop the container
|
||||
container.stop()
|
||||
|
||||
try:
|
||||
# Clear existing data
|
||||
shutil.rmtree(n8n_data_dir)
|
||||
os.makedirs(n8n_data_dir)
|
||||
|
||||
# Restore from backup
|
||||
restore_cmd = f"tar -xzf {backup_file} -C {n8n_data_dir}"
|
||||
result = subprocess.run(restore_cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"Restore failed: {result.stderr}")
|
||||
|
||||
# Start the container
|
||||
container.start()
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None'}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
except Exception as e:
|
||||
# Try to start the container even if restore failed
|
||||
try:
|
||||
container.start()
|
||||
except:
|
||||
pass
|
||||
raise e
|
||||
|
||||
except Exception as e:
|
||||
data_ret = {'status': 0, 'error_message': str(e)}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
def deleteBackup(self, userID=None, data=None):
|
||||
try:
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if admin.acl.adminStatus != 1:
|
||||
return ACLManager.loadError()
|
||||
|
||||
container_id = data['id']
|
||||
backup_id = data['backup_id']
|
||||
backup_dir = self._get_backup_dir(container_id)
|
||||
backup_file = os.path.join(backup_dir, backup_id)
|
||||
|
||||
if os.path.exists(backup_file):
|
||||
os.remove(backup_file)
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None'}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
except Exception as e:
|
||||
data_ret = {'status': 0, 'error_message': str(e)}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
|
||||
def downloadBackup(self, userID=None, data=None):
|
||||
try:
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if admin.acl.adminStatus != 1:
|
||||
return ACLManager.loadError()
|
||||
|
||||
container_id = data['id']
|
||||
backup_id = data['backup_id']
|
||||
backup_dir = self._get_backup_dir(container_id)
|
||||
backup_file = os.path.join(backup_dir, backup_id)
|
||||
|
||||
if not os.path.exists(backup_file):
|
||||
raise Exception("Backup file not found")
|
||||
|
||||
with open(backup_file, 'rb') as f:
|
||||
response = HttpResponse(f.read(), content_type='application/gzip')
|
||||
response['Content-Disposition'] = f'attachment; filename="{backup_id}"'
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
data_ret = {'status': 0, 'error_message': str(e)}
|
||||
return HttpResponse(json.dumps(data_ret))
|
||||
@@ -27,7 +27,7 @@ urlpatterns = [
|
||||
re_path(r'^recreateContainer$', views.recreateContainer, name='recreateContainer'),
|
||||
re_path(r'^installDocker$', views.installDocker, name='installDocker'),
|
||||
re_path(r'^images$', views.images, name='containerImage'),
|
||||
re_path(r'^view/(?P<n>.+)$', views.viewContainer, name='viewContainer'),
|
||||
re_path(r'^view/(?P<name>.+)$', views.viewContainer, name='viewContainer'),
|
||||
|
||||
path('manage/<int:dockerapp>/app', Dockersitehome, name='Dockersitehome'),
|
||||
path('getDockersiteList', views.getDockersiteList, name='getDockersiteList'),
|
||||
@@ -36,9 +36,4 @@ urlpatterns = [
|
||||
path('recreateappcontainer', views.recreateappcontainer, name='recreateappcontainer'),
|
||||
path('RestartContainerAPP', views.RestartContainerAPP, name='RestartContainerAPP'),
|
||||
path('StopContainerAPP', views.StopContainerAPP, name='StopContainerAPP'),
|
||||
path('createBackup', views.createBackup, name='createBackup'),
|
||||
path('listBackups', views.listBackups, name='listBackups'),
|
||||
path('restoreBackup', views.restoreBackup, name='restoreBackup'),
|
||||
path('deleteBackup', views.deleteBackup, name='deleteBackup'),
|
||||
path('downloadBackup', views.downloadBackup, name='downloadBackup'),
|
||||
]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.shortcuts import redirect, HttpResponse
|
||||
@@ -536,93 +536,3 @@ def StopContainerAPP(request):
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def createBackup(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.createBackup(userID, json.loads(request.body))
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def listBackups(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.listBackups(userID, json.loads(request.body))
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def restoreBackup(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.restoreBackup(userID, json.loads(request.body))
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def deleteBackup(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.deleteBackup(userID, json.loads(request.body))
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def downloadBackup(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.downloadBackup(userID, json.loads(request.body))
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
@@ -1,7 +1,6 @@
|
||||
app.controller('DockerContainerManager', function ($scope, $http) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
app.controller('ListDockersitecontainer', function ($scope, $http) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
$scope.conatinerview = true;
|
||||
$scope.ContainerList = [];
|
||||
$('#cyberpanelLoading').hide();
|
||||
|
||||
// Format bytes to human readable
|
||||
@@ -30,7 +29,7 @@ app.controller('DockerContainerManager', function ($scope, $http) {
|
||||
function ListInitialData(response) {
|
||||
$('#cyberpanelLoading').hide();
|
||||
if (response.data.status === 1) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberPanelLoading = true;
|
||||
var finalData = JSON.parse(response.data.data[1]);
|
||||
$scope.ContainerList = finalData;
|
||||
$("#listFail").hide();
|
||||
@@ -41,7 +40,7 @@ app.controller('DockerContainerManager', function ($scope, $http) {
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberPanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
@@ -98,20 +97,18 @@ app.controller('DockerContainerManager', function ($scope, $http) {
|
||||
|
||||
// Environment Variables
|
||||
$scope.ContainerList[i].environment = containerInfo.environment;
|
||||
|
||||
// N8N Stats
|
||||
$scope.ContainerList[i].n8nStats = containerInfo.n8nStats || {
|
||||
dbConnected: null,
|
||||
activeWorkflows: 0,
|
||||
queuedExecutions: 0,
|
||||
lastExecution: null
|
||||
};
|
||||
|
||||
// Load backups
|
||||
$scope.refreshBackups($scope.ContainerList[i].id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get container logs
|
||||
$scope.getcontainerlog(containerid);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,209 +117,16 @@ app.controller('DockerContainerManager', function ($scope, $http) {
|
||||
$('#cyberpanelLoading').hide();
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message || 'Could not connect to server',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.createBackup = function(containerId) {
|
||||
$scope.cyberpanelLoading = false;
|
||||
$('#cyberpanelLoading').show();
|
||||
|
||||
var url = "/docker/createBackup";
|
||||
var data = {
|
||||
'name': $('#sitename').html(),
|
||||
'id': containerId
|
||||
};
|
||||
|
||||
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) {
|
||||
// Refresh backups list
|
||||
$scope.refreshBackups(containerId);
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'Backup created successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}, function(error) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.refreshBackups = function(containerId) {
|
||||
var url = "/docker/listBackups";
|
||||
var data = {
|
||||
'name': $('#sitename').html(),
|
||||
'id': containerId
|
||||
};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.status === 1) {
|
||||
// Find the container and update its backups
|
||||
for (var i = 0; i < $scope.ContainerList.length; i++) {
|
||||
if ($scope.ContainerList[i].id === containerId) {
|
||||
$scope.ContainerList[i].backups = response.data.backups;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.restoreBackup = function(containerId, backupId) {
|
||||
if (!confirm("Are you sure you want to restore this backup? The container will be stopped during restoration.")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.cyberpanelLoading = false;
|
||||
$('#cyberpanelLoading').show();
|
||||
|
||||
var url = "/docker/restoreBackup";
|
||||
var data = {
|
||||
'name': $('#sitename').html(),
|
||||
'id': containerId,
|
||||
'backup_id': backupId
|
||||
};
|
||||
|
||||
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) {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'Backup restored successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
// Refresh container info
|
||||
$scope.Lunchcontainer(containerId);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}, function(error) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteBackup = function(containerId, backupId) {
|
||||
if (!confirm("Are you sure you want to delete this backup?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = "/docker/deleteBackup";
|
||||
var data = {
|
||||
'name': $('#sitename').html(),
|
||||
'id': containerId,
|
||||
'backup_id': backupId
|
||||
};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'Backup deleted successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
// Refresh backups list
|
||||
$scope.refreshBackups(containerId);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}, function(error) {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not connect to server.',
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.downloadBackup = function(containerId, backupId) {
|
||||
window.location.href = "/docker/downloadBackup?name=" + encodeURIComponent($('#sitename').html()) +
|
||||
"&id=" + encodeURIComponent(containerId) +
|
||||
"&backup_id=" + encodeURIComponent(backupId);
|
||||
};
|
||||
|
||||
$scope.openN8NEditor = function(container) {
|
||||
// Find the N8N port from the container's port bindings
|
||||
var n8nPort = null;
|
||||
if (container.ports) {
|
||||
for (var port in container.ports) {
|
||||
if (container.ports[port] && container.ports[port].length > 0) {
|
||||
n8nPort = container.ports[port][0].HostPort;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n8nPort) {
|
||||
window.open("http://localhost:" + n8nPort, "_blank");
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Error!',
|
||||
text: 'Could not find N8N port configuration.',
|
||||
text: 'Connection disrupted, refresh the page.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getcontainerlog = function (containerid) {
|
||||
$scope.cyberpanelLoading = false;
|
||||
var url = "/docker/getContainerApplog";
|
||||
|
||||
var data = {
|
||||
'name': $('#sitename').html(),
|
||||
'id': containerid
|
||||
@@ -334,16 +138,195 @@ app.controller('DockerContainerManager', function ($scope, $http) {
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
function ListInitialData(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.conatinerview = false;
|
||||
$('#cyberpanelLoading').hide();
|
||||
|
||||
if (response.data.status === 1) {
|
||||
// Find the container and update its logs
|
||||
// Find the container in the list and update its logs
|
||||
for (var i = 0; i < $scope.ContainerList.length; i++) {
|
||||
if ($scope.ContainerList[i].id === containerid) {
|
||||
$scope.ContainerList[i].logs = response.data.data[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
$scope.conatinerview = false;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Connection disrupted, refresh the page.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Auto-refresh container info every 30 seconds
|
||||
var refreshInterval;
|
||||
$scope.$watch('conatinerview', function(newValue, oldValue) {
|
||||
if (newValue === false) { // When container view is shown
|
||||
refreshInterval = setInterval(function() {
|
||||
if ($scope.cid) {
|
||||
$scope.Lunchcontainer($scope.cid);
|
||||
}
|
||||
}, 30000); // 30 seconds
|
||||
} else { // When container view is hidden
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up on controller destruction
|
||||
$scope.$on('$destroy', function() {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
$scope.getcontainer();
|
||||
|
||||
// Keep your existing functions
|
||||
$scope.recreateappcontainer = function() { /* ... */ };
|
||||
$scope.refreshStatus = function() { /* ... */ };
|
||||
$scope.restarthStatus = function() { /* ... */ };
|
||||
$scope.StopContainerAPP = function() { /* ... */ };
|
||||
$scope.cAction = function(action) {
|
||||
$scope.cyberpanelLoading = false;
|
||||
$('#cyberpanelLoading').show();
|
||||
|
||||
var url;
|
||||
switch(action) {
|
||||
case 'start':
|
||||
url = "/docker/StartContainerAPP";
|
||||
break;
|
||||
case 'stop':
|
||||
url = "/docker/StopContainerAPP";
|
||||
break;
|
||||
case 'restart':
|
||||
url = "/docker/RestartContainerAPP";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var data = {
|
||||
'name': $('#sitename').html(),
|
||||
'id': $scope.selectedContainer.id
|
||||
};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
function ListInitialData(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'Container ' + action + ' successful.',
|
||||
type: 'success'
|
||||
});
|
||||
// Refresh container info after action
|
||||
$scope.Lunchcontainer($scope.selectedContainer.id);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Connection disrupted, refresh the page.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.openSettings = function(container) {
|
||||
$scope.selectedContainer = container;
|
||||
$('#settings').modal('show');
|
||||
};
|
||||
|
||||
$scope.saveSettings = function() {
|
||||
$scope.cyberpanelLoading = false;
|
||||
$('#cyberpanelLoading').show();
|
||||
|
||||
var url = "/docker/updateContainerSettings";
|
||||
var data = {
|
||||
'name': $('#sitename').html(),
|
||||
'id': $scope.selectedContainer.id,
|
||||
'memoryLimit': $scope.selectedContainer.memoryLimit,
|
||||
'startOnReboot': $scope.selectedContainer.startOnReboot
|
||||
};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
function ListInitialData(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success!',
|
||||
text: 'Container settings updated successfully.',
|
||||
type: 'success'
|
||||
});
|
||||
$('#settings').modal('hide');
|
||||
// Refresh container info after update
|
||||
$scope.Lunchcontainer($scope.selectedContainer.id);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$('#cyberpanelLoading').hide();
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Connection disrupted, refresh the page.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Add location service to the controller for the n8n URL
|
||||
$scope.location = window.location;
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,6 @@
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<script src="{% static 'websiteFunctions/dockerController.js' %}"></script>
|
||||
|
||||
<style>
|
||||
.info-box {
|
||||
background: #fff;
|
||||
@@ -149,7 +147,7 @@
|
||||
</script>
|
||||
|
||||
|
||||
<div class="container" ng-controller="DockerContainerManager">
|
||||
<div class="container" ng-controller="ListDockersitecontainer">
|
||||
|
||||
<div id="page-title">
|
||||
<h2 id="domainNamePage">{% trans "Containers" %} <img id="cyberpanelLoading" ng-hide="cyberpanelLoading"
|
||||
@@ -306,31 +304,27 @@
|
||||
<div id="page-title" class="mb-4">
|
||||
<h2 id="domainNamePage" class="d-flex justify-content-between align-items-center">
|
||||
<span>{% trans "Currently managing: " %} {$ web.name $}</span>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-success" ng-click="cAction('start')" ng-if="web.status !== 'running'">
|
||||
<i class="fa fa-play"></i> Start
|
||||
</button>
|
||||
<button class="btn btn-warning" ng-click="cAction('restart')" ng-if="web.status === 'running'">
|
||||
<i class="fa fa-refresh"></i> Restart
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-click="cAction('stop')" ng-if="web.status === 'running'">
|
||||
<i class="fa fa-stop"></i> Stop
|
||||
</button>
|
||||
<button class="btn btn-primary" ng-click="openSettings(web)">
|
||||
<i class="fa fa-cog"></i> Settings
|
||||
</button>
|
||||
<a class="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
|
||||
</a>
|
||||
</div>
|
||||
</h2>
|
||||
<p class="text-muted">
|
||||
{% trans "Container ID" %}: <code>{$ web.id $}</code>
|
||||
</p>
|
||||
|
||||
<div class="action-buttons mb-4">
|
||||
<button class="btn btn-primary me-2" ng-click="openN8NEditor(web)">
|
||||
<i class="fa fa-external-link"></i> Open N8N Editor
|
||||
</button>
|
||||
<button class="btn btn-success me-2" ng-click="startContainer(web.id)" ng-if="web.status !== 'running'">
|
||||
<i class="fa fa-play"></i> Start
|
||||
</button>
|
||||
<button class="btn btn-warning me-2" ng-click="restartContainer(web.id)" ng-if="web.status === 'running'">
|
||||
<i class="fa fa-refresh"></i> Restart
|
||||
</button>
|
||||
<button class="btn btn-danger me-2" ng-click="stopContainer(web.id)" ng-if="web.status === 'running'">
|
||||
<i class="fa fa-stop"></i> Stop
|
||||
</button>
|
||||
<button class="btn btn-info me-2" data-toggle="modal" data-target="#settings">
|
||||
<i class="fa fa-cog"></i> Settings
|
||||
</button>
|
||||
<button class="btn btn-secondary" ng-click="showProcesses(web.id)">
|
||||
<i class="fa fa-tasks"></i> Processes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid p-0">
|
||||
@@ -383,80 +377,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box shadow-sm mt-4">
|
||||
<h4 class="border-bottom pb-2 mb-3">N8N Health Status</h4>
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="mb-0">Active Workflows</label>
|
||||
<span class="badge bg-primary">{$ web.n8nStats.activeWorkflows || 0 $}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="mb-0">Queued Executions</label>
|
||||
<span class="badge bg-warning">{$ web.n8nStats.queuedExecutions || 0 $}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="mb-0">Database Connection</label>
|
||||
<span class="badge" ng-class="{'bg-success': web.n8nStats.dbConnected, 'bg-danger': !web.n8nStats.dbConnected}">
|
||||
{$ web.n8nStats.dbConnected ? 'Connected' : 'Disconnected' $}
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label class="mb-0">Last Execution</label>
|
||||
<span>{$ web.n8nStats.lastExecution | date:'medium' $}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box shadow-sm mt-4">
|
||||
<h4 class="border-bottom pb-2 mb-3">Backup Management</h4>
|
||||
<div class="mb-3">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8">
|
||||
<button class="btn btn-primary" ng-click="createBackup(web.id)">
|
||||
<i class="fa fa-download"></i> Create New Backup
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<button class="btn btn-outline-primary" ng-click="refreshBackups(web.id)">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive" ng-if="web.backups && web.backups.length > 0">
|
||||
<table class="table table-striped table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Size</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="backup in web.backups">
|
||||
<td>{$ backup.date | date:'medium' $}</td>
|
||||
<td>{$ backup.size | bytes $}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-success me-1" ng-click="restoreBackup(web.id, backup.id)">
|
||||
<i class="fa fa-upload"></i> Restore
|
||||
</button>
|
||||
<button class="btn btn-sm btn-primary me-1" ng-click="downloadBackup(web.id, backup.id)">
|
||||
<i class="fa fa-download"></i> Download
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" ng-click="deleteBackup(web.id, backup.id)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div ng-if="!web.backups || web.backups.length === 0" class="text-muted">
|
||||
<p class="mb-0">No backups available</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
|
||||
Reference in New Issue
Block a user