mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-03-18 02:00:16 +01:00
fix(docker): listContainers HTML page – avoid JSON/cache mix-up
- Add GET /docker/containers for HTML page; GET /docker/listContainers redirects there - POST /docker/listContainers returns 405 (page uses getContainerList for data) - Remove duplicate listContainers Angular controller; fix pagination (getContainerList) - Extend getContainerList API: totalCount, totalPages, currentPage, itemsPerPage - Add ACTIVITY BOARD-style pagination: Prev/Next, Go to page, Showing X–Y of Z - Update menu/templates/JS redirects to /docker/containers - Sync dockerManager.js across app static, STATIC_ROOT, public/static - Cache-Control on HTML response; cache-bust script ?v=4 Fixes raw JSON instead of UI when loading /docker/listContainers (cache/proxy serving stored JSON for GET). Use /docker/containers for the page.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
{% load i18n %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% with CP_VERSION="2.4.4.1" %}
|
||||
{% with CP_VERSION="2.5.5-dev-fix" %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="CyberCP">
|
||||
<html lang="en" ng-app="CyberCP" {% if cosmetic.MainDashboardCSS %}data-dynamic-css="{{ cosmetic.MainDashboardCSS|escape }}"{% endif %}>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@@ -17,30 +17,39 @@
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<style>
|
||||
{{ cosmetic.MainDashboardCSS | safe }}
|
||||
/* Custom dashboard CSS - base styles */
|
||||
body { visibility: visible; }
|
||||
</style>
|
||||
|
||||
<!-- Mobile Responsive CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'baseTemplate/assets/mobile-responsive.css' %}?v={{ CP_VERSION }}">
|
||||
|
||||
<!-- Readability Fixes CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'baseTemplate/assets/readability-fixes.css' %}?v={{ CP_VERSION }}">
|
||||
|
||||
<!-- Core Scripts -->
|
||||
<!-- Core Scripts - AngularJS MUST load FIRST before all other scripts -->
|
||||
<script src="{% static 'baseTemplate/angularjs.1.6.5.js' %}?v={{ CP_VERSION }}"></script>
|
||||
|
||||
<!-- Dynamic CSS from Django template (injected via JavaScript to avoid linter errors) -->
|
||||
<script>
|
||||
// Inject dynamic CSS from data attribute to avoid CSS/JS linter errors
|
||||
(function() {
|
||||
var htmlElement = document.documentElement;
|
||||
var cssContent = htmlElement.getAttribute('data-dynamic-css');
|
||||
if (cssContent) {
|
||||
var style = document.createElement('style');
|
||||
style.textContent = cssContent;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
|
||||
<!-- Bootstrap JavaScript -->
|
||||
|
||||
<script src="{% static 'baseTemplate/assets/bootstrap/js/bootstrap.min.js' %}?v={{ CP_VERSION }}"></script>
|
||||
<script src="{% static 'baseTemplate/bootstrap-toggle.min.js' %}?v={{ CP_VERSION }}"></script>
|
||||
<script src="{% static 'baseTemplate/custom-js/qrious.min.js' %}?v={{ CP_VERSION }}"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrious/dist/qrious.min.js"></script>
|
||||
<script src="{% static 'baseTemplate/custom-js/system-status.js' %}?v={{ CP_VERSION }}"></script>
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
||||
<!-- Chart.js -->
|
||||
<script src="{% static 'baseTemplate/custom-js/chart.umd.min.js' %}?v={{ CP_VERSION }}"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<!-- PNotify -->
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'baseTemplate/custom-js/pnotify.custom.min.css' %}?v={{ CP_VERSION }}">
|
||||
@@ -1566,7 +1575,7 @@
|
||||
|
||||
{% block header_scripts %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<body data-has-backup-configured="{% if request.session.has_backup_configured %}true{% else %}false{% endif %}">
|
||||
<!-- Header -->
|
||||
<div id="header">
|
||||
<button id="mobile-menu-toggle" class="mobile-menu-toggle" onclick="toggleSidebar()" style="display: none;">
|
||||
@@ -1647,7 +1656,7 @@
|
||||
<div class="info-line">
|
||||
<strong>Uptime:</strong>
|
||||
<span ng-show="!uptimeLoaded">Loading...</span>
|
||||
<span ng-show="uptimeLoaded" ng-bind="uptime">N/A</span>
|
||||
<span ng-show="uptimeLoaded">{$ uptime $}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1803,9 +1812,6 @@
|
||||
<a href="{% url 'listChildDomains' %}" class="menu-item">
|
||||
<span>List Sub/Addon Domains</span>
|
||||
</a>
|
||||
<a href="{% url 'fixSubdomainLogs' %}" class="menu-item">
|
||||
<span>Fix Subdomain Logs</span>
|
||||
</a>
|
||||
{% if admin or modifyWebsite %}
|
||||
<a href="{% url 'modifyWebsite' %}" class="menu-item">
|
||||
<span>Modify Website</span>
|
||||
@@ -2157,7 +2163,7 @@
|
||||
<a href="{% url 'manageImages' %}" class="menu-item">
|
||||
<span>Manage Images</span>
|
||||
</a>
|
||||
<a href="{% url 'listContainers' %}" class="menu-item">
|
||||
<a href="{% url 'listContainersPage' %}" class="menu-item">
|
||||
<span>Manage Containers</span>
|
||||
</a>
|
||||
<a href="{% url 'containerImage' %}" class="menu-item">
|
||||
@@ -2287,9 +2293,6 @@
|
||||
<a href="{% url 'aiScannerHome' %}" class="menu-item">
|
||||
<span>AI Scanner</span>
|
||||
</a>
|
||||
<a href="#" class="menu-item" onclick="loadSecurityManagement(); return false;">
|
||||
<span>Security Management</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a href="#" class="menu-item" onclick="toggleSubmenu('mail-settings-submenu', this); return false;">
|
||||
@@ -2343,11 +2346,6 @@
|
||||
<a href="{% url 'managePureFtpd' %}" class="menu-item">
|
||||
<span>Manage FTP</span>
|
||||
</a>
|
||||
{% if admin %}
|
||||
<a href="#" class="menu-item" onclick="loadBandwidthManagement(); return false;">
|
||||
<span>Bandwidth Management</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<a href="#" class="menu-item" onclick="toggleSubmenu('plugins-submenu', this); return false;">
|
||||
@@ -2452,6 +2450,12 @@
|
||||
<script src="{% static 'CLManager/CLManager.js' %}?v={{ CP_VERSION }}"></script>
|
||||
|
||||
<!-- Scripts -->
|
||||
<!-- Set backup configuration status from Django template via data attribute -->
|
||||
<script>
|
||||
// Read backup configuration status from data attribute (set by Django template)
|
||||
var backupConfigElement = document.querySelector('[data-has-backup-configured]');
|
||||
window.hasBackupConfigured = backupConfigElement ? backupConfigElement.getAttribute('data-has-backup-configured') === 'true' : false;
|
||||
</script>
|
||||
<script>
|
||||
function toggleSidebar() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
@@ -2580,9 +2584,10 @@
|
||||
// Check if user has backup configured (you'll need to implement this API)
|
||||
// For now, we'll show it by default unless they have a backup plan
|
||||
// This should be replaced with an actual check
|
||||
{% if not request.session.has_backup_configured %}
|
||||
// Note: hasBackupConfigured is set via Django template before this script
|
||||
if (typeof window.hasBackupConfigured !== 'undefined' && !window.hasBackupConfigured) {
|
||||
showBackupNotification();
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
// For demonstration, let's show it if URL doesn't contain 'OneClickBackups'
|
||||
if (!window.location.href.includes('OneClickBackups')) {
|
||||
@@ -2819,7 +2824,44 @@
|
||||
item.classList.remove('active', 'has-active-child');
|
||||
});
|
||||
|
||||
// Find and activate the matching menu item
|
||||
// Special handling for plugin pages - expand Plugins submenu and highlight
|
||||
if (currentPath.startsWith('/plugins/')) {
|
||||
const pluginsSubmenu = document.getElementById('plugins-submenu');
|
||||
const pluginsMenuItem = pluginsSubmenu ? pluginsSubmenu.previousElementSibling : null;
|
||||
|
||||
if (pluginsSubmenu) {
|
||||
pluginsSubmenu.classList.add('show');
|
||||
}
|
||||
|
||||
if (pluginsMenuItem && pluginsMenuItem.classList.contains('menu-item')) {
|
||||
pluginsMenuItem.classList.add('expanded', 'has-active-child');
|
||||
}
|
||||
|
||||
// Check if it's the installed page
|
||||
if (currentPath === '/plugins/installed' || currentPath.startsWith('/plugins/installed')) {
|
||||
const installedLink = pluginsSubmenu ? pluginsSubmenu.querySelector('a[href*="installed"]') : null;
|
||||
if (installedLink) {
|
||||
installedLink.classList.add('active');
|
||||
}
|
||||
} else if (currentPath.match(/^\/plugins\/[^\/]+\/(settings|help)/)) {
|
||||
// It's a plugin settings/help page - highlight "Installed" menu item
|
||||
const installedLink = pluginsSubmenu ? pluginsSubmenu.querySelector('a[href*="installed"]') : null;
|
||||
if (installedLink) {
|
||||
installedLink.classList.add('active');
|
||||
}
|
||||
} else {
|
||||
// Try to match specific plugin links in the submenu
|
||||
const pluginLinks = pluginsSubmenu ? pluginsSubmenu.querySelectorAll('a.menu-item') : [];
|
||||
pluginLinks.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && (currentPath === href || currentPath.startsWith(href + '/'))) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Find and activate the matching menu item (exact matches)
|
||||
allMenuItems.forEach(item => {
|
||||
const href = item.getAttribute('href');
|
||||
if (href && href !== '#' && currentPath === href) {
|
||||
@@ -2917,4 +2959,4 @@
|
||||
{% block footer_scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
|
||||
@@ -698,9 +698,31 @@ class ContainerManager(multi.Thread):
|
||||
return ACLManager.loadErrorJson('listContainerStatus', 0)
|
||||
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
pageNumber = int(data['page'])
|
||||
json_data = self.findContainersJson(currentACL, userID, pageNumber)
|
||||
final_dic = {'listContainerStatus': 1, 'error_message': "None", "data": json_data}
|
||||
pageNumber = max(1, int(data.get('page', 1)))
|
||||
items_per_page = 10
|
||||
|
||||
all_containers = ACLManager.findContainersObjects(currentACL, userID)
|
||||
totalCount = len(all_containers)
|
||||
totalPages = max(1, int(ceil(float(totalCount) / float(items_per_page))))
|
||||
|
||||
start = (pageNumber - 1) * items_per_page
|
||||
end = start + items_per_page
|
||||
page_containers = all_containers[start:end]
|
||||
|
||||
rows = []
|
||||
for items in page_containers:
|
||||
rows.append({'name': items.name, 'admin': items.admin.userName, 'tag': items.tag, 'image': items.image})
|
||||
json_data = json.dumps(rows)
|
||||
|
||||
final_dic = {
|
||||
'listContainerStatus': 1,
|
||||
'error_message': 'None',
|
||||
'data': json_data,
|
||||
'totalCount': totalCount,
|
||||
'totalPages': totalPages,
|
||||
'currentPage': pageNumber,
|
||||
'itemsPerPage': items_per_page
|
||||
}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
except BaseException as msg:
|
||||
@@ -2407,9 +2429,9 @@ class ContainerManager(multi.Thread):
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def listContainers(self, userID=None):
|
||||
def listContainersJson(self, userID=None):
|
||||
"""
|
||||
Get list of all Docker containers
|
||||
Get list of all Docker containers as JSON (for Angular API).
|
||||
"""
|
||||
try:
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
@@ -2441,13 +2463,13 @@ class ContainerManager(multi.Thread):
|
||||
'containers': container_list
|
||||
}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
return HttpResponse(json_data, content_type='application/json')
|
||||
|
||||
except Exception as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [ContainerManager.listContainers]')
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [ContainerManager.listContainersJson]')
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
return HttpResponse(json_data, content_type='application/json')
|
||||
|
||||
def getDockerNetworks(self, userID=None):
|
||||
"""
|
||||
|
||||
@@ -729,6 +729,59 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
$scope.assignActive = "";
|
||||
$scope.dockerOwner = "";
|
||||
|
||||
/* Pagination (ACTIVITY BOARD-style) */
|
||||
var CONTAINERS_PER_PAGE = 10;
|
||||
$scope.pagination = { containers: { currentPage: 1, itemsPerPage: CONTAINERS_PER_PAGE } };
|
||||
$scope.gotoPageInput = { containers: 1 };
|
||||
$scope.totalCount = 0;
|
||||
$scope.totalPages = 1;
|
||||
$scope.Math = Math;
|
||||
|
||||
$scope.getTotalPages = function(section) {
|
||||
if (section === 'containers') return Math.max(1, $scope.totalPages || 1);
|
||||
return 1;
|
||||
};
|
||||
|
||||
$scope.goToPage = function(section, page) {
|
||||
if (section !== 'containers') return;
|
||||
var totalPages = $scope.getTotalPages(section);
|
||||
var p = parseInt(page, 10);
|
||||
if (isNaN(p) || p < 1) p = 1;
|
||||
if (p > totalPages) p = totalPages;
|
||||
$scope.getFurtherContainersFromDB(p);
|
||||
};
|
||||
|
||||
$scope.nextPage = function(section) {
|
||||
if (section !== 'containers') return;
|
||||
if ($scope.pagination.containers.currentPage < $scope.getTotalPages(section)) {
|
||||
$scope.getFurtherContainersFromDB($scope.pagination.containers.currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.prevPage = function(section) {
|
||||
if (section !== 'containers') return;
|
||||
if ($scope.pagination.containers.currentPage > 1) {
|
||||
$scope.getFurtherContainersFromDB($scope.pagination.containers.currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getPageNumbers = function(section) {
|
||||
if (section !== 'containers') return [];
|
||||
var totalPages = $scope.getTotalPages(section);
|
||||
var current = $scope.pagination.containers.currentPage;
|
||||
var maxVisible = 5;
|
||||
var pages = [];
|
||||
if (totalPages <= maxVisible) {
|
||||
for (var i = 1; i <= totalPages; i++) pages.push(i);
|
||||
} else {
|
||||
var start = Math.max(1, current - 2);
|
||||
var end = Math.min(totalPages, start + maxVisible - 1);
|
||||
if (end - start + 1 < maxVisible) start = Math.max(1, end - maxVisible + 1);
|
||||
for (var j = start; j <= end; j++) pages.push(j);
|
||||
}
|
||||
return pages;
|
||||
};
|
||||
|
||||
$scope.assignContainer = function (name) {
|
||||
console.log('assignContainer called with:', name);
|
||||
$scope.assignActive = name;
|
||||
@@ -783,7 +836,7 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
title: 'Container assigned successfully',
|
||||
type: 'success'
|
||||
});
|
||||
window.location.href = '/docker/listContainers';
|
||||
window.location.href = '/docker/containers';
|
||||
}
|
||||
else {
|
||||
new PNotify({
|
||||
@@ -1264,77 +1317,35 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
$scope.logInfo = null;
|
||||
};
|
||||
|
||||
url = "/docker/getContainerList";
|
||||
|
||||
var data = {page: 1};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
|
||||
function ListInitialData(response) {
|
||||
console.log(response);
|
||||
|
||||
function handleContainerListResponse(response) {
|
||||
if (response.data.listContainerStatus === 1) {
|
||||
|
||||
var finalData = JSON.parse(response.data.data);
|
||||
$scope.ContainerList = finalData;
|
||||
console.log($scope.ContainerList);
|
||||
$scope.totalCount = response.data.totalCount || 0;
|
||||
$scope.totalPages = Math.max(1, response.data.totalPages || 1);
|
||||
var cp = Math.max(1, parseInt(response.data.currentPage, 10) || 1);
|
||||
$scope.pagination.containers.currentPage = cp;
|
||||
$scope.gotoPageInput.containers = cp;
|
||||
$("#listFail").hide();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$("#listFail").fadeIn();
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
|
||||
$scope.errorMessage = response.data.error_message || 'Failed to load containers';
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
console.log("not good");
|
||||
$("#listFail").fadeIn();
|
||||
$scope.errorMessage = (response && response.data && response.data.error_message) ? response.data.error_message : 'Could not connect to server';
|
||||
}
|
||||
|
||||
var config = {
|
||||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||||
};
|
||||
$http.post("/docker/getContainerList", { page: 1 }, config).then(handleContainerListResponse, cantLoadInitialData);
|
||||
|
||||
$scope.getFurtherContainersFromDB = function (pageNumber) {
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
var data = {page: pageNumber};
|
||||
|
||||
|
||||
dataurl = "/docker/getContainerList";
|
||||
|
||||
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
|
||||
function ListInitialData(response) {
|
||||
if (response.data.listContainerStatus === 1) {
|
||||
|
||||
var finalData = JSON.parse(response.data.data);
|
||||
$scope.ContainerList = finalData;
|
||||
$("#listFail").hide();
|
||||
}
|
||||
else {
|
||||
$("#listFail").fadeIn();
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
console.log(response.data);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
console.log("not good");
|
||||
}
|
||||
|
||||
|
||||
var p = Math.max(1, parseInt(pageNumber, 10) || 1);
|
||||
$http.post("/docker/getContainerList", { page: p }, config).then(handleContainerListResponse, cantLoadInitialData);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1906,7 +1917,7 @@ app.controller('viewContainer', function ($scope, $http, $interval, $timeout) {
|
||||
text: 'Redirecting...',
|
||||
type: 'success'
|
||||
});
|
||||
window.location.href = '/docker/listContainers';
|
||||
window.location.href = '/docker/containers';
|
||||
}
|
||||
else {
|
||||
new PNotify({
|
||||
@@ -2703,257 +2714,3 @@ app.controller('manageImages', function ($scope, $http) {
|
||||
}
|
||||
});
|
||||
|
||||
// Container List Controller
|
||||
app.controller('listContainers', function ($scope, $http, $timeout, $window) {
|
||||
$scope.containers = [];
|
||||
$scope.loading = false;
|
||||
$scope.updateContainerName = '';
|
||||
$scope.currentImage = '';
|
||||
$scope.newImage = '';
|
||||
$scope.newTag = 'latest';
|
||||
|
||||
// Load containers list
|
||||
$scope.loadContainers = function() {
|
||||
$scope.loading = true;
|
||||
var url = '/docker/listContainers';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, {}, config).then(function(response) {
|
||||
$scope.loading = false;
|
||||
if (response.data.status === 1) {
|
||||
$scope.containers = response.data.containers || [];
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Error Loading Containers',
|
||||
text: response.data.error_message || 'Failed to load containers',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}, function(error) {
|
||||
$scope.loading = false;
|
||||
new PNotify({
|
||||
title: 'Connection Error',
|
||||
text: 'Could not connect to server',
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize containers on page load
|
||||
$scope.loadContainers();
|
||||
|
||||
// Open update container modal
|
||||
$scope.openUpdateModal = function(container) {
|
||||
$scope.updateContainerName = container.name;
|
||||
$scope.currentImage = container.image;
|
||||
$scope.newImage = '';
|
||||
$scope.newTag = 'latest';
|
||||
$('#updateContainer').modal('show');
|
||||
};
|
||||
|
||||
// Perform container update
|
||||
$scope.performUpdate = function() {
|
||||
if (!$scope.newImage && !$scope.newTag) {
|
||||
new PNotify({
|
||||
title: 'Missing Information',
|
||||
text: 'Please enter a new image name or tag',
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If no new image specified, use current image with new tag
|
||||
var imageToUse = $scope.newImage || $scope.currentImage.split(':')[0];
|
||||
var tagToUse = $scope.newTag || 'latest';
|
||||
|
||||
var data = {
|
||||
containerName: $scope.updateContainerName,
|
||||
newImage: imageToUse,
|
||||
newTag: tagToUse
|
||||
};
|
||||
|
||||
var url = '/docker/updateContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
// Show loading
|
||||
$('#updateContainer').modal('hide');
|
||||
new PNotify({
|
||||
title: 'Updating Container',
|
||||
text: 'Please wait while the container is being updated...',
|
||||
type: 'info',
|
||||
hide: false
|
||||
});
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.updateContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Updated Successfully',
|
||||
text: response.data.message || 'Container has been updated successfully',
|
||||
type: 'success'
|
||||
});
|
||||
// Reload containers list
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
text: response.data.error_message || 'Failed to update container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}, function(error) {
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
text: 'Could not connect to server',
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Container actions
|
||||
$scope.startContainer = function(containerName) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/startContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.startContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Started',
|
||||
text: 'Container has been started successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Start Failed',
|
||||
text: response.data.error_message || 'Failed to start container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.stopContainer = function(containerName) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/stopContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.stopContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Stopped',
|
||||
text: 'Container has been stopped successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Stop Failed',
|
||||
text: response.data.error_message || 'Failed to stop container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.restartContainer = function(containerName) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/restartContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.restartContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Restarted',
|
||||
text: 'Container has been restarted successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Restart Failed',
|
||||
text: response.data.error_message || 'Failed to restart container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteContainerWithData = function(containerName) {
|
||||
if (confirm('Are you sure you want to delete this container and all its data? This action cannot be undone.')) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/deleteContainerWithData';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.deleteContainerWithDataStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Deleted',
|
||||
text: 'Container and all data have been deleted successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Delete Failed',
|
||||
text: response.data.error_message || 'Failed to delete container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deleteContainerKeepData = function(containerName) {
|
||||
if (confirm('Are you sure you want to delete this container but keep the data? The container will be removed but volumes will be preserved.')) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/deleteContainerKeepData';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.deleteContainerKeepDataStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Deleted',
|
||||
text: 'Container has been deleted but data has been preserved',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Delete Failed',
|
||||
text: response.data.error_message || 'Failed to delete container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -433,7 +433,7 @@
|
||||
<i class="fas fa-search"></i>
|
||||
{% trans "Search & Manage Images" %}
|
||||
</a>
|
||||
<a href="{% url 'listContainers' %}" class="btn-secondary">
|
||||
<a href="{% url 'listContainersPage' %}" class="btn-secondary">
|
||||
<i class="fas fa-cube"></i>
|
||||
{% trans "View Containers" %}
|
||||
</a>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="example-box-wrapper">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<a href="{% url 'listContainers' %}" title="{% trans 'Manage Containers' %}" class="tile-box tile-box-shortcut btn-primary">
|
||||
<a href="{% url 'listContainersPage' %}" title="{% trans 'Manage Containers' %}" class="tile-box tile-box-shortcut btn-primary">
|
||||
<div class="tile-header">
|
||||
{% trans "Manage Containers" %}
|
||||
</div>
|
||||
|
||||
@@ -674,14 +674,37 @@
|
||||
<strong>{% trans "Error:" %}</strong> {$ errorMessage $}
|
||||
</div>
|
||||
|
||||
<div class="pagination" ng-if="pagination">
|
||||
<ul>
|
||||
{% for items in pagination %}
|
||||
<li ng-click="getFurtherContainersFromDB({{ forloop.counter }})">
|
||||
<a href="">{{ forloop.counter }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- Pagination Controls (ACTIVITY BOARD-style: Go to page) -->
|
||||
<div ng-if="pagination && pagination.containers && (ContainerList.length > 0 || totalCount > 0)" class="pagination-wrapper" style="margin-top: 20px; padding: 15px; background: #f8f9ff; border-radius: 8px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
|
||||
<button ng-click="prevPage('containers')" ng-disabled="!pagination.containers || (pagination.containers.currentPage || 1) === 1"
|
||||
class="pagination-btn" style="padding: 6px 12px; border: 1px solid #e8e9ff; background: white; border-radius: 6px; cursor: pointer; font-size: 12px; color: #2f3640;">
|
||||
<i class="fas fa-chevron-left"></i> {% trans "Prev" %}
|
||||
</button>
|
||||
<span style="font-size: 12px; color: #64748b; margin: 0 8px;">
|
||||
{% trans "Page" %} {$ (pagination.containers.currentPage || 1) $} {% trans "of" %} {$ (getTotalPages('containers') || 1) $}
|
||||
</span>
|
||||
<button ng-click="nextPage('containers')" ng-disabled="!pagination.containers || !getTotalPages || (pagination.containers.currentPage || 1) >= (getTotalPages('containers') || 1)"
|
||||
class="pagination-btn" style="padding: 6px 12px; border: 1px solid #e8e9ff; background: white; border-radius: 6px; cursor: pointer; font-size: 12px; color: #2f3640;">
|
||||
{% trans "Next" %} <i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
|
||||
<label style="font-size: 12px; color: #64748b; margin: 0;">{% trans "Go to page:" %}</label>
|
||||
<input type="number" min="1" max="{$ (getTotalPages('containers') || 1) $}"
|
||||
ng-model="gotoPageInput.containers"
|
||||
ng-keyup="$event.keyCode === 13 && goToPage('containers', gotoPageInput.containers)"
|
||||
style="width: 60px; padding: 6px; border: 1px solid #e8e9ff; border-radius: 6px; font-size: 12px; text-align: center;">
|
||||
<button ng-click="goToPage('containers', gotoPageInput.containers)"
|
||||
class="pagination-btn" style="padding: 6px 12px; border: 1px solid #5b5fcf; background: #5b5fcf; color: white; border-radius: 6px; cursor: pointer; font-size: 12px;">
|
||||
{% trans "Go" %}
|
||||
</button>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #64748b; margin: 0;" ng-if="totalCount > 0">
|
||||
<span ng-if="pagination && pagination.containers">
|
||||
{% trans "Showing" %} {$ ((pagination.containers.currentPage || 1) - 1) * (pagination.containers.itemsPerPage || 10) + 1 $}-{$ Math.min((pagination.containers.currentPage || 1) * (pagination.containers.itemsPerPage || 10), totalCount) $} {% trans "of" %} {$ totalCount $} {% trans "entries" %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -949,5 +972,5 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block footer_scripts %}
|
||||
<script src="{% static 'dockerManager/dockerManager.js' %}"></script>
|
||||
<script src="{% static 'dockerManager/dockerManager.js' %}?v=4"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,6 +9,7 @@ urlpatterns = [
|
||||
re_path(r'^getTags$', views.getTags, name='getTags'),
|
||||
re_path(r'^runContainer', views.runContainer, name='runContainer'),
|
||||
re_path(r'^submitContainerCreation$', views.submitContainerCreation, name='submitContainerCreation'),
|
||||
re_path(r'^containers$', views.listContainersPage, name='listContainersPage'),
|
||||
re_path(r'^listContainers$', views.listContainers, name='listContainers'),
|
||||
re_path(r'^getContainerList$', views.getContainerList, name='getContainerList'),
|
||||
re_path(r'^getContainerLogs$', views.getContainerLogs, name='getContainerLogs'),
|
||||
@@ -33,7 +34,6 @@ urlpatterns = [
|
||||
re_path(r'^updateContainerPorts$', views.updateContainerPorts, name='updateContainerPorts'),
|
||||
re_path(r'^manageNetworks$', views.manageNetworks, name='manageNetworks'),
|
||||
re_path(r'^updateContainer$', views.updateContainer, name='updateContainer'),
|
||||
re_path(r'^listContainers$', views.listContainers, name='listContainers'),
|
||||
re_path(r'^deleteContainerWithData$', views.deleteContainerWithData, name='deleteContainerWithData'),
|
||||
re_path(r'^deleteContainerKeepData$', views.deleteContainerKeepData, name='deleteContainerKeepData'),
|
||||
re_path(r'^recreateContainer$', views.recreateContainer, name='recreateContainer'),
|
||||
|
||||
@@ -179,14 +179,40 @@ def runContainer(request):
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def listContainers(request):
|
||||
def listContainersPage(request):
|
||||
"""
|
||||
GET /docker/containers: Render HTML page only. Separate URL avoids
|
||||
cache/proxy ever serving JSON (listContainers used to return JSON).
|
||||
"""
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
cm = ContainerManager()
|
||||
return cm.listContainers(request, userID)
|
||||
resp = cm.listContainers(request, userID)
|
||||
resp['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
|
||||
resp['Pragma'] = 'no-cache'
|
||||
resp['Expires'] = '0'
|
||||
return resp
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
|
||||
@preDockerRun
|
||||
def listContainers(request):
|
||||
"""
|
||||
GET: Redirect to /docker/containers (HTML). POST: 405.
|
||||
listContainers URL historically returned JSON; caches may serve stale JSON.
|
||||
Use /docker/containers for the page to avoid that.
|
||||
"""
|
||||
try:
|
||||
request.session['userID'] # ensure logged in
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
if request.method != 'GET':
|
||||
return HttpResponse('Method Not Allowed', status=405)
|
||||
from django.urls import reverse
|
||||
return redirect(reverse('listContainersPage'))
|
||||
|
||||
@preDockerRun
|
||||
def getContainerLogs(request):
|
||||
try:
|
||||
@@ -746,27 +772,6 @@ def getContainerEnv(request):
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def listContainers(request):
|
||||
"""
|
||||
Get list of all Docker containers
|
||||
"""
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
cm = ContainerManager()
|
||||
coreResult = cm.listContainers(userID)
|
||||
|
||||
return coreResult
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
@preDockerRun
|
||||
def getDockerNetworks(request):
|
||||
"""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -729,6 +729,59 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
$scope.assignActive = "";
|
||||
$scope.dockerOwner = "";
|
||||
|
||||
/* Pagination (ACTIVITY BOARD-style) */
|
||||
var CONTAINERS_PER_PAGE = 10;
|
||||
$scope.pagination = { containers: { currentPage: 1, itemsPerPage: CONTAINERS_PER_PAGE } };
|
||||
$scope.gotoPageInput = { containers: 1 };
|
||||
$scope.totalCount = 0;
|
||||
$scope.totalPages = 1;
|
||||
$scope.Math = Math;
|
||||
|
||||
$scope.getTotalPages = function(section) {
|
||||
if (section === 'containers') return Math.max(1, $scope.totalPages || 1);
|
||||
return 1;
|
||||
};
|
||||
|
||||
$scope.goToPage = function(section, page) {
|
||||
if (section !== 'containers') return;
|
||||
var totalPages = $scope.getTotalPages(section);
|
||||
var p = parseInt(page, 10);
|
||||
if (isNaN(p) || p < 1) p = 1;
|
||||
if (p > totalPages) p = totalPages;
|
||||
$scope.getFurtherContainersFromDB(p);
|
||||
};
|
||||
|
||||
$scope.nextPage = function(section) {
|
||||
if (section !== 'containers') return;
|
||||
if ($scope.pagination.containers.currentPage < $scope.getTotalPages(section)) {
|
||||
$scope.getFurtherContainersFromDB($scope.pagination.containers.currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.prevPage = function(section) {
|
||||
if (section !== 'containers') return;
|
||||
if ($scope.pagination.containers.currentPage > 1) {
|
||||
$scope.getFurtherContainersFromDB($scope.pagination.containers.currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getPageNumbers = function(section) {
|
||||
if (section !== 'containers') return [];
|
||||
var totalPages = $scope.getTotalPages(section);
|
||||
var current = $scope.pagination.containers.currentPage;
|
||||
var maxVisible = 5;
|
||||
var pages = [];
|
||||
if (totalPages <= maxVisible) {
|
||||
for (var i = 1; i <= totalPages; i++) pages.push(i);
|
||||
} else {
|
||||
var start = Math.max(1, current - 2);
|
||||
var end = Math.min(totalPages, start + maxVisible - 1);
|
||||
if (end - start + 1 < maxVisible) start = Math.max(1, end - maxVisible + 1);
|
||||
for (var j = start; j <= end; j++) pages.push(j);
|
||||
}
|
||||
return pages;
|
||||
};
|
||||
|
||||
$scope.assignContainer = function (name) {
|
||||
console.log('assignContainer called with:', name);
|
||||
$scope.assignActive = name;
|
||||
@@ -783,7 +836,7 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
title: 'Container assigned successfully',
|
||||
type: 'success'
|
||||
});
|
||||
window.location.href = '/docker/listContainers';
|
||||
window.location.href = '/docker/containers';
|
||||
}
|
||||
else {
|
||||
new PNotify({
|
||||
@@ -1264,77 +1317,35 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
$scope.logInfo = null;
|
||||
};
|
||||
|
||||
url = "/docker/getContainerList";
|
||||
|
||||
var data = {page: 1};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
|
||||
function ListInitialData(response) {
|
||||
console.log(response);
|
||||
|
||||
function handleContainerListResponse(response) {
|
||||
if (response.data.listContainerStatus === 1) {
|
||||
|
||||
var finalData = JSON.parse(response.data.data);
|
||||
$scope.ContainerList = finalData;
|
||||
console.log($scope.ContainerList);
|
||||
$scope.totalCount = response.data.totalCount || 0;
|
||||
$scope.totalPages = Math.max(1, response.data.totalPages || 1);
|
||||
var cp = Math.max(1, parseInt(response.data.currentPage, 10) || 1);
|
||||
$scope.pagination.containers.currentPage = cp;
|
||||
$scope.gotoPageInput.containers = cp;
|
||||
$("#listFail").hide();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$("#listFail").fadeIn();
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
|
||||
$scope.errorMessage = response.data.error_message || 'Failed to load containers';
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
console.log("not good");
|
||||
$("#listFail").fadeIn();
|
||||
$scope.errorMessage = (response && response.data && response.data.error_message) ? response.data.error_message : 'Could not connect to server';
|
||||
}
|
||||
|
||||
var config = {
|
||||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||||
};
|
||||
$http.post("/docker/getContainerList", { page: 1 }, config).then(handleContainerListResponse, cantLoadInitialData);
|
||||
|
||||
$scope.getFurtherContainersFromDB = function (pageNumber) {
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
var data = {page: pageNumber};
|
||||
|
||||
|
||||
dataurl = "/docker/getContainerList";
|
||||
|
||||
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
|
||||
function ListInitialData(response) {
|
||||
if (response.data.listContainerStatus === 1) {
|
||||
|
||||
var finalData = JSON.parse(response.data.data);
|
||||
$scope.ContainerList = finalData;
|
||||
$("#listFail").hide();
|
||||
}
|
||||
else {
|
||||
$("#listFail").fadeIn();
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
console.log(response.data);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
console.log("not good");
|
||||
}
|
||||
|
||||
|
||||
var p = Math.max(1, parseInt(pageNumber, 10) || 1);
|
||||
$http.post("/docker/getContainerList", { page: p }, config).then(handleContainerListResponse, cantLoadInitialData);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1906,7 +1917,7 @@ app.controller('viewContainer', function ($scope, $http, $interval, $timeout) {
|
||||
text: 'Redirecting...',
|
||||
type: 'success'
|
||||
});
|
||||
window.location.href = '/docker/listContainers';
|
||||
window.location.href = '/docker/containers';
|
||||
}
|
||||
else {
|
||||
new PNotify({
|
||||
@@ -2703,257 +2714,3 @@ app.controller('manageImages', function ($scope, $http) {
|
||||
}
|
||||
});
|
||||
|
||||
// Container List Controller
|
||||
app.controller('listContainers', function ($scope, $http, $timeout, $window) {
|
||||
$scope.containers = [];
|
||||
$scope.loading = false;
|
||||
$scope.updateContainerName = '';
|
||||
$scope.currentImage = '';
|
||||
$scope.newImage = '';
|
||||
$scope.newTag = 'latest';
|
||||
|
||||
// Load containers list
|
||||
$scope.loadContainers = function() {
|
||||
$scope.loading = true;
|
||||
var url = '/docker/listContainers';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, {}, config).then(function(response) {
|
||||
$scope.loading = false;
|
||||
if (response.data.status === 1) {
|
||||
$scope.containers = response.data.containers || [];
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Error Loading Containers',
|
||||
text: response.data.error_message || 'Failed to load containers',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}, function(error) {
|
||||
$scope.loading = false;
|
||||
new PNotify({
|
||||
title: 'Connection Error',
|
||||
text: 'Could not connect to server',
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize containers on page load
|
||||
$scope.loadContainers();
|
||||
|
||||
// Open update container modal
|
||||
$scope.openUpdateModal = function(container) {
|
||||
$scope.updateContainerName = container.name;
|
||||
$scope.currentImage = container.image;
|
||||
$scope.newImage = '';
|
||||
$scope.newTag = 'latest';
|
||||
$('#updateContainer').modal('show');
|
||||
};
|
||||
|
||||
// Perform container update
|
||||
$scope.performUpdate = function() {
|
||||
if (!$scope.newImage && !$scope.newTag) {
|
||||
new PNotify({
|
||||
title: 'Missing Information',
|
||||
text: 'Please enter a new image name or tag',
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If no new image specified, use current image with new tag
|
||||
var imageToUse = $scope.newImage || $scope.currentImage.split(':')[0];
|
||||
var tagToUse = $scope.newTag || 'latest';
|
||||
|
||||
var data = {
|
||||
containerName: $scope.updateContainerName,
|
||||
newImage: imageToUse,
|
||||
newTag: tagToUse
|
||||
};
|
||||
|
||||
var url = '/docker/updateContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
// Show loading
|
||||
$('#updateContainer').modal('hide');
|
||||
new PNotify({
|
||||
title: 'Updating Container',
|
||||
text: 'Please wait while the container is being updated...',
|
||||
type: 'info',
|
||||
hide: false
|
||||
});
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.updateContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Updated Successfully',
|
||||
text: response.data.message || 'Container has been updated successfully',
|
||||
type: 'success'
|
||||
});
|
||||
// Reload containers list
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
text: response.data.error_message || 'Failed to update container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}, function(error) {
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
text: 'Could not connect to server',
|
||||
type: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Container actions
|
||||
$scope.startContainer = function(containerName) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/startContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.startContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Started',
|
||||
text: 'Container has been started successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Start Failed',
|
||||
text: response.data.error_message || 'Failed to start container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.stopContainer = function(containerName) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/stopContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.stopContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Stopped',
|
||||
text: 'Container has been stopped successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Stop Failed',
|
||||
text: response.data.error_message || 'Failed to stop container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.restartContainer = function(containerName) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/restartContainer';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.restartContainerStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Restarted',
|
||||
text: 'Container has been restarted successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Restart Failed',
|
||||
text: response.data.error_message || 'Failed to restart container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteContainerWithData = function(containerName) {
|
||||
if (confirm('Are you sure you want to delete this container and all its data? This action cannot be undone.')) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/deleteContainerWithData';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.deleteContainerWithDataStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Deleted',
|
||||
text: 'Container and all data have been deleted successfully',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Delete Failed',
|
||||
text: response.data.error_message || 'Failed to delete container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deleteContainerKeepData = function(containerName) {
|
||||
if (confirm('Are you sure you want to delete this container but keep the data? The container will be removed but volumes will be preserved.')) {
|
||||
var data = { containerName: containerName };
|
||||
var url = '/docker/deleteContainerKeepData';
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
$http.post(url, data, config).then(function(response) {
|
||||
if (response.data.deleteContainerKeepDataStatus === 1) {
|
||||
new PNotify({
|
||||
title: 'Container Deleted',
|
||||
text: 'Container has been deleted but data has been preserved',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.loadContainers();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Delete Failed',
|
||||
text: response.data.error_message || 'Failed to delete container',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user