mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-05-06 17:07:04 +02:00
Docker Manager: fix container update flow and UI sync
Align updateContainer with the panel (name vs containerName), pull new images before removing the old container, and sync the Containers model after a successful update. getContainerList now shows live Config.Image so tags match Docker. Add notification-center progress for updates, guard overlapping requests, and return new_image on success.
This commit is contained in:
@@ -410,6 +410,63 @@
|
||||
text-decoration: underline;
|
||||
color: #3730a3 !important;
|
||||
}
|
||||
|
||||
/* Ephemeral notifications (e.g. Docker update progress) */
|
||||
.notification-center-item-ephemeral {
|
||||
border-color: #c7d2fe;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #eef2ff 100%);
|
||||
}
|
||||
.notification-center-progress-track {
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: #e5e7eb;
|
||||
overflow: hidden;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.notification-center-progress-bar {
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, #4f46e5, #6366f1);
|
||||
transition: width 0.35s ease;
|
||||
}
|
||||
.notification-center-progress-bar.indeterminate {
|
||||
width: 35% !important;
|
||||
animation: cp-nc-progress-indet 1.2s ease-in-out infinite;
|
||||
}
|
||||
.notification-center-progress-bar.success {
|
||||
width: 100% !important;
|
||||
background: linear-gradient(90deg, #16a34a, #22c55e);
|
||||
animation: none;
|
||||
}
|
||||
.notification-center-progress-bar.error {
|
||||
width: 100% !important;
|
||||
background: linear-gradient(90deg, #dc2626, #ef4444);
|
||||
animation: none;
|
||||
}
|
||||
@keyframes cp-nc-progress-indet {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(280%); }
|
||||
}
|
||||
.notification-center-ephemeral-dismiss {
|
||||
margin-top: 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
color: #6b7280;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.notification-center-ephemeral-dismiss:hover { color: #111827; }
|
||||
.notification-center-btn.has-active-operation {
|
||||
animation: cp-nc-bell-pulse 1.5s ease-in-out infinite;
|
||||
border-color: #6366f1;
|
||||
color: #4f46e5;
|
||||
}
|
||||
@keyframes cp-nc-bell-pulse {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(79, 70, 229, 0.35); }
|
||||
50% { box-shadow: 0 0 0 6px rgba(79, 70, 229, 0); }
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
#sidebar {
|
||||
@@ -2504,7 +2561,7 @@
|
||||
<script src="{% static 'serverStatus/serverStatus.js' %}?v={{ CP_VERSION }}" data-cfasync="false"></script>
|
||||
<script src="{% static 'firewall/firewall.js' %}?v={{ CP_VERSION }}&fw={{ FIREWALL_STATIC_VERSION|default:CP_VERSION }}&cb=4" data-cfasync="false"></script>
|
||||
<script src="{% static 'emailPremium/emailPremium.js' %}?v={{ CP_VERSION }}" data-cfasync="false"></script>
|
||||
<script src="{% static 'manageServices/manageServices.js' %}?v={{ CP_VERSION }}&msModal=20260402c" data-cfasync="false"></script>
|
||||
<script src="{% static 'manageServices/manageServices.js' %}?v={{ CP_VERSION }}&msModal=20260402d" data-cfasync="false"></script>
|
||||
<script src="{% static 'CLManager/CLManager.js' %}?v={{ CP_VERSION }}" data-cfasync="false"></script>
|
||||
|
||||
<!-- Scripts -->
|
||||
@@ -2750,6 +2807,93 @@
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
window.__cpEphemeralNotifications = window.__cpEphemeralNotifications || [];
|
||||
|
||||
function cpEscapeHtmlNC(str) {
|
||||
if (str == null || str === '') return '';
|
||||
var div = document.createElement('div');
|
||||
div.textContent = String(str);
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function cpEscapeAttrNC(s) {
|
||||
return String(s || '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function renderEphemeralNotificationsHTML() {
|
||||
var items = window.__cpEphemeralNotifications || [];
|
||||
return items.map(function(notif) {
|
||||
var nameEsc = cpEscapeHtmlNC(notif.containerName);
|
||||
var imgEsc = cpEscapeHtmlNC(notif.imageRef);
|
||||
var idAttr = cpEscapeAttrNC(notif.id);
|
||||
var textHtml = '';
|
||||
var barHtml = '';
|
||||
if (notif.state === 'running') {
|
||||
textHtml = '<p style="margin:0 0 0.5rem 0">Updating <strong>' + nameEsc + '</strong> to <code style="font-size:0.9em">' + imgEsc + '</code>. Pulling image and recreating the container — you can leave this tab open.</p>';
|
||||
barHtml = '<div class="notification-center-progress-track"><div class="notification-center-progress-bar indeterminate"></div></div>';
|
||||
} else if (notif.state === 'done_ok') {
|
||||
textHtml = '<p style="margin:0 0 0.5rem 0"><strong>' + nameEsc + '</strong> updated successfully.</p><p style="margin:0;color:#15803d;font-size:0.9em">' + cpEscapeHtmlNC(notif.resultMessage) + '</p>';
|
||||
barHtml = '<div class="notification-center-progress-track"><div class="notification-center-progress-bar success"></div></div>';
|
||||
} else {
|
||||
textHtml = '<p style="margin:0 0 0.5rem 0">Update failed for <strong>' + nameEsc + '</strong>.</p><p style="margin:0;color:#b91c1c;font-size:0.9em">' + cpEscapeHtmlNC(notif.resultMessage) + '</p>';
|
||||
barHtml = '<div class="notification-center-progress-track"><div class="notification-center-progress-bar error"></div></div>';
|
||||
}
|
||||
return '<div class="notification-center-item notification-center-item-ephemeral" data-ephemeral-id="' + idAttr + '">' +
|
||||
'<div class="notification-center-item-title"><i class="fab fa-docker"></i><span>Docker update</span></div>' +
|
||||
'<div class="notification-center-item-text" style="margin-bottom:0">' + textHtml + '</div>' +
|
||||
barHtml +
|
||||
'<button type="button" class="notification-center-ephemeral-dismiss" data-cp-ephemeral-dismiss="' + idAttr + '">Dismiss</button>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
window.cpDismissEphemeralNotification = function(id) {
|
||||
window.__cpEphemeralNotifications = (window.__cpEphemeralNotifications || []).filter(function(n) { return n.id !== id; });
|
||||
var stillRunning = (window.__cpEphemeralNotifications || []).some(function(n) { return n.state === 'running'; });
|
||||
var btn = document.getElementById('notification-center-btn');
|
||||
if (btn && !stillRunning) btn.classList.remove('has-active-operation');
|
||||
loadNotificationCenter();
|
||||
};
|
||||
|
||||
window.cpDockerUpdateNotifyStart = function(containerName, imageRef) {
|
||||
window.__cpEphemeralNotifications = window.__cpEphemeralNotifications || [];
|
||||
var id = 'docker-update-' + Date.now() + '-' + Math.random().toString(36).slice(2, 9);
|
||||
window.__cpEphemeralNotifications.unshift({
|
||||
id: id,
|
||||
kind: 'docker-update',
|
||||
containerName: String(containerName || ''),
|
||||
imageRef: String(imageRef || ''),
|
||||
state: 'running',
|
||||
resultMessage: ''
|
||||
});
|
||||
var dd = document.getElementById('notification-center-dropdown');
|
||||
if (dd) dd.classList.add('show');
|
||||
var bell = document.getElementById('notification-center-btn');
|
||||
if (bell) bell.classList.add('has-active-operation');
|
||||
loadNotificationCenter();
|
||||
return id;
|
||||
};
|
||||
|
||||
window.cpDockerUpdateNotifyEnd = function(nid, ok, message) {
|
||||
window.__cpEphemeralNotifications = window.__cpEphemeralNotifications || [];
|
||||
var n = null;
|
||||
for (var i = 0; i < window.__cpEphemeralNotifications.length; i++) {
|
||||
if (window.__cpEphemeralNotifications[i].id === nid) { n = window.__cpEphemeralNotifications[i]; break; }
|
||||
}
|
||||
if (!n) return;
|
||||
n.state = ok ? 'done_ok' : 'done_err';
|
||||
n.resultMessage = message ? String(message) : (ok ? 'Done.' : 'Unknown error.');
|
||||
var stillRunning = window.__cpEphemeralNotifications.some(function(x) { return x.state === 'running'; });
|
||||
var btn = document.getElementById('notification-center-btn');
|
||||
if (btn && !stillRunning) btn.classList.remove('has-active-operation');
|
||||
loadNotificationCenter();
|
||||
};
|
||||
|
||||
function toggleNotificationCenter() {
|
||||
const dropdown = document.getElementById('notification-center-dropdown');
|
||||
if (dropdown) {
|
||||
@@ -2779,10 +2923,10 @@
|
||||
learnMoreLink: 'https://cyberpanel.net/cyberpanel-htaccess-module',
|
||||
dismissed: isNotificationDismissed('htaccess-notification') }
|
||||
];
|
||||
if (notifications.length === 0) {
|
||||
list.innerHTML = '<div class="notification-center-empty">No notifications available</div>';
|
||||
} else {
|
||||
list.innerHTML = notifications.map(notif => {
|
||||
const ephemeralHtml = renderEphemeralNotificationsHTML();
|
||||
let staticHtml = '';
|
||||
if (notifications.length > 0) {
|
||||
staticHtml = notifications.map(notif => {
|
||||
let linkIcon = notif.linkText.includes('Configure') ? '<i class="fas fa-cog"></i>' :
|
||||
notif.linkText.includes('Start') ? '<i class="fas fa-rocket"></i>' :
|
||||
(notif.linkText.includes('View') || notif.linkText.includes('Details')) ? '<i class="fas fa-external-link-alt"></i>' : '<i class="fas fa-arrow-right"></i>';
|
||||
@@ -2802,7 +2946,14 @@
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
const activeCount = notifications.filter(n => !n.dismissed).length;
|
||||
if (!ephemeralHtml && !staticHtml) {
|
||||
list.innerHTML = '<div class="notification-center-empty">No notifications available</div>';
|
||||
} else {
|
||||
list.innerHTML = ephemeralHtml + staticHtml;
|
||||
}
|
||||
const staticActive = notifications.filter(n => !n.dismissed).length;
|
||||
const runningEphem = (window.__cpEphemeralNotifications || []).filter(function(n) { return n.state === 'running'; }).length;
|
||||
const activeCount = staticActive + runningEphem;
|
||||
const badge = document.getElementById('notification-badge');
|
||||
if (badge) {
|
||||
badge.textContent = activeCount;
|
||||
@@ -2819,6 +2970,16 @@
|
||||
|
||||
// Check all notification statuses when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var ncList = document.getElementById('notification-center-list');
|
||||
if (ncList) {
|
||||
ncList.addEventListener('click', function(ev) {
|
||||
var dismissBtn = ev.target.closest('[data-cp-ephemeral-dismiss]');
|
||||
if (dismissBtn && window.cpDismissEphemeralNotification) {
|
||||
var eid = dismissBtn.getAttribute('data-cp-ephemeral-dismiss');
|
||||
if (eid) window.cpDismissEphemeralNotification(eid);
|
||||
}
|
||||
});
|
||||
}
|
||||
loadNotificationCenter();
|
||||
checkBackupStatus();
|
||||
// Optional: open notification dropdown for testing (e.g. ?showNotifications=1)
|
||||
|
||||
@@ -772,9 +772,28 @@ class ContainerManager(multi.Thread):
|
||||
end = start + items_per_page
|
||||
page_containers = all_containers[start:end]
|
||||
|
||||
client = docker.from_env()
|
||||
rows = []
|
||||
for items in page_containers:
|
||||
rows.append({'name': items.name, 'admin': items.admin.userName, 'tag': items.tag, 'image': items.image})
|
||||
disp_image = items.image
|
||||
disp_tag = items.tag
|
||||
try:
|
||||
running = client.containers.get(items.name)
|
||||
cfg_ref = running.attrs.get('Config', {}).get('Image') or ''
|
||||
if cfg_ref and '@' not in cfg_ref:
|
||||
if ':' in cfg_ref:
|
||||
disp_image, disp_tag = cfg_ref.rsplit(':', 1)
|
||||
else:
|
||||
disp_image, disp_tag = cfg_ref, 'latest'
|
||||
elif running.image and running.image.tags:
|
||||
ref = running.image.tags[0]
|
||||
if ':' in ref:
|
||||
disp_image, disp_tag = ref.rsplit(':', 1)
|
||||
else:
|
||||
disp_image, disp_tag = ref, 'latest'
|
||||
except Exception:
|
||||
pass
|
||||
rows.append({'name': items.name, 'admin': items.admin.userName, 'tag': disp_tag, 'image': disp_image})
|
||||
json_data = json.dumps(rows)
|
||||
|
||||
final_dic = {
|
||||
@@ -2346,15 +2365,28 @@ class ContainerManager(multi.Thread):
|
||||
client = docker.from_env()
|
||||
dockerAPI = docker.APIClient()
|
||||
|
||||
containerName = data['containerName']
|
||||
newImage = data['newImage']
|
||||
newTag = data.get('newTag', 'latest')
|
||||
# UI (dockerManager.js) sends "name"; older callers may use "containerName"
|
||||
containerName = (data.get('containerName') or data.get('name') or '').strip()
|
||||
if not containerName:
|
||||
data_ret = {'updateContainerStatus': 0, 'error_message': 'Container name is required'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
newImage = (data.get('newImage') or '').strip()
|
||||
newTag = (data.get('newTag') or 'latest').strip() or 'latest'
|
||||
|
||||
# Get the current container
|
||||
try:
|
||||
currentContainer = client.containers.get(containerName)
|
||||
except docker.errors.NotFound:
|
||||
data_ret = {'updateContainerStatus': 0, 'error_message': f'Container {containerName} not found'}
|
||||
data_ret = {
|
||||
'updateContainerStatus': 0,
|
||||
'error_message': (
|
||||
f'Container {containerName} not found. '
|
||||
'If you clicked Update twice, wait for the first request to finish; '
|
||||
'do not start another update until you see success or failure.'
|
||||
),
|
||||
}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except Exception as e:
|
||||
@@ -2365,6 +2397,19 @@ class ContainerManager(multi.Thread):
|
||||
# Get container configuration for recreation
|
||||
containerConfig = currentContainer.attrs['Config']
|
||||
hostConfig = currentContainer.attrs['HostConfig']
|
||||
|
||||
# If no new image specified, use current image repository (same as first updateContainer implementation)
|
||||
if not newImage:
|
||||
current_image = containerConfig.get('Image', '') or ''
|
||||
if ':' in current_image:
|
||||
newImage = current_image.split(':')[0]
|
||||
else:
|
||||
newImage = current_image
|
||||
newTag = 'latest'
|
||||
if not newImage:
|
||||
data_ret = {'updateContainerStatus': 0, 'error_message': 'Could not determine image name for update'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
# Extract volumes for data preservation
|
||||
volumes = {}
|
||||
@@ -2399,6 +2444,20 @@ class ContainerManager(multi.Thread):
|
||||
if memory_limit > 0:
|
||||
memory_limit = memory_limit // 1048576 # Convert bytes to MB
|
||||
|
||||
image_name = f"{newImage}:{newTag}"
|
||||
|
||||
# Pull BEFORE stop/remove so a slow/failed pull never leaves the container deleted
|
||||
# (double-clicks or retries would otherwise see "container not found").
|
||||
try:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Pulling new image {image_name} (container still running)')
|
||||
client.images.pull(newImage, tag=newTag)
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Successfully pulled image {image_name}')
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Error pulling image {newImage}:{newTag}: {str(e)}')
|
||||
data_ret = {'updateContainerStatus': 0, 'error_message': f'Error pulling new image: {str(e)}'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
# Stop the current container
|
||||
try:
|
||||
if currentContainer.status == 'running':
|
||||
@@ -2420,18 +2479,6 @@ class ContainerManager(multi.Thread):
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
# Pull the new image
|
||||
try:
|
||||
image_name = f"{newImage}:{newTag}"
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Pulling new image {image_name}')
|
||||
client.images.pull(newImage, tag=newTag)
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Successfully pulled image {image_name}')
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Error pulling image {newImage}:{newTag}: {str(e)}')
|
||||
data_ret = {'updateContainerStatus': 0, 'error_message': f'Error pulling new image: {str(e)}'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
# Create new container with same configuration but new image
|
||||
try:
|
||||
containerArgs = {
|
||||
@@ -2475,12 +2522,27 @@ class ContainerManager(multi.Thread):
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
# Sync DB — container list UI reads image/tag from Containers model, not Docker
|
||||
try:
|
||||
container_record = Containers.objects.get(name=containerName)
|
||||
container_record.image = newImage
|
||||
container_record.tag = newTag
|
||||
container_record.cid = newContainer.short_id
|
||||
container_record.save()
|
||||
except Containers.DoesNotExist:
|
||||
pass
|
||||
except Exception as db_err:
|
||||
logging.CyberCPLogFileWriter.writeToFile(
|
||||
f'updateContainer DB sync failed for {containerName}: {db_err}'
|
||||
)
|
||||
|
||||
# Log successful update
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Successfully updated container {containerName} to image {image_name}')
|
||||
|
||||
data_ret = {
|
||||
'updateContainerStatus': 1,
|
||||
'updateContainerStatus': 1,
|
||||
'error_message': 'None',
|
||||
'new_image': image_name,
|
||||
'message': f'Container {containerName} successfully updated to {image_name}'
|
||||
}
|
||||
json_data = json.dumps(data_ret)
|
||||
|
||||
@@ -974,6 +974,15 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.dockerUpdateInProgress) {
|
||||
new PNotify({
|
||||
title: 'Update in progress',
|
||||
text: 'Wait until the current update finishes before starting another.',
|
||||
type: 'warning'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If no new image specified, use current image
|
||||
if (!$scope.newImage) {
|
||||
$scope.newImage = $scope.currentImage;
|
||||
@@ -1000,9 +1009,18 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
history: false
|
||||
}
|
||||
})).get().on('pnotify.confirm', function () {
|
||||
var dockerUpdateNotificationId = null;
|
||||
$scope.dockerUpdateInProgress = true;
|
||||
$('#imageLoading').show();
|
||||
$("#updateContainer").modal("hide");
|
||||
|
||||
if (typeof window.cpDockerUpdateNotifyStart === 'function') {
|
||||
dockerUpdateNotificationId = window.cpDockerUpdateNotifyStart(
|
||||
$scope.updateContainerName,
|
||||
$scope.newImage + ':' + $scope.newTag
|
||||
);
|
||||
}
|
||||
|
||||
url = "/docker/updateContainer";
|
||||
var data = {
|
||||
name: $scope.updateContainerName,
|
||||
@@ -1020,15 +1038,27 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
|
||||
function ListInitialData(response) {
|
||||
console.log(response);
|
||||
$scope.dockerUpdateInProgress = false;
|
||||
$('#imageLoading').hide();
|
||||
|
||||
if (response.data.updateContainerStatus === 1) {
|
||||
var ok = response.data && response.data.updateContainerStatus === 1;
|
||||
var imgLabel = ok
|
||||
? (response.data.new_image || response.data.message || 'Updated')
|
||||
: (response.data && response.data.error_message ? response.data.error_message : 'Update failed');
|
||||
|
||||
if (typeof window.cpDockerUpdateNotifyEnd === 'function' && dockerUpdateNotificationId) {
|
||||
window.cpDockerUpdateNotifyEnd(dockerUpdateNotificationId, ok, imgLabel);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
new PNotify({
|
||||
title: 'Container Updated Successfully',
|
||||
text: `Container updated to ${response.data.new_image}`,
|
||||
text: 'Container updated to ' + (response.data.new_image || response.data.message || 'new image'),
|
||||
type: 'success'
|
||||
});
|
||||
location.reload();
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 2200);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
@@ -1039,7 +1069,11 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.dockerUpdateInProgress = false;
|
||||
$('#imageLoading').hide();
|
||||
if (typeof window.cpDockerUpdateNotifyEnd === 'function' && dockerUpdateNotificationId) {
|
||||
window.cpDockerUpdateNotifyEnd(dockerUpdateNotificationId, false, 'Could not connect to server');
|
||||
}
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
text: 'Could not connect to server',
|
||||
|
||||
@@ -974,6 +974,15 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($scope.dockerUpdateInProgress) {
|
||||
new PNotify({
|
||||
title: 'Update in progress',
|
||||
text: 'Wait until the current update finishes before starting another.',
|
||||
type: 'warning'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If no new image specified, use current image
|
||||
if (!$scope.newImage) {
|
||||
$scope.newImage = $scope.currentImage;
|
||||
@@ -1000,9 +1009,18 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
history: false
|
||||
}
|
||||
})).get().on('pnotify.confirm', function () {
|
||||
var dockerUpdateNotificationId = null;
|
||||
$scope.dockerUpdateInProgress = true;
|
||||
$('#imageLoading').show();
|
||||
$("#updateContainer").modal("hide");
|
||||
|
||||
if (typeof window.cpDockerUpdateNotifyStart === 'function') {
|
||||
dockerUpdateNotificationId = window.cpDockerUpdateNotifyStart(
|
||||
$scope.updateContainerName,
|
||||
$scope.newImage + ':' + $scope.newTag
|
||||
);
|
||||
}
|
||||
|
||||
url = "/docker/updateContainer";
|
||||
var data = {
|
||||
name: $scope.updateContainerName,
|
||||
@@ -1020,15 +1038,27 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
|
||||
function ListInitialData(response) {
|
||||
console.log(response);
|
||||
$scope.dockerUpdateInProgress = false;
|
||||
$('#imageLoading').hide();
|
||||
|
||||
if (response.data.updateContainerStatus === 1) {
|
||||
var ok = response.data && response.data.updateContainerStatus === 1;
|
||||
var imgLabel = ok
|
||||
? (response.data.new_image || response.data.message || 'Updated')
|
||||
: (response.data && response.data.error_message ? response.data.error_message : 'Update failed');
|
||||
|
||||
if (typeof window.cpDockerUpdateNotifyEnd === 'function' && dockerUpdateNotificationId) {
|
||||
window.cpDockerUpdateNotifyEnd(dockerUpdateNotificationId, ok, imgLabel);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
new PNotify({
|
||||
title: 'Container Updated Successfully',
|
||||
text: `Container updated to ${response.data.new_image}`,
|
||||
text: 'Container updated to ' + (response.data.new_image || response.data.message || 'new image'),
|
||||
type: 'success'
|
||||
});
|
||||
location.reload();
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 2200);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
@@ -1039,7 +1069,11 @@ app.controller('listContainers', function ($scope, $http) {
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.dockerUpdateInProgress = false;
|
||||
$('#imageLoading').hide();
|
||||
if (typeof window.cpDockerUpdateNotifyEnd === 'function' && dockerUpdateNotificationId) {
|
||||
window.cpDockerUpdateNotifyEnd(dockerUpdateNotificationId, false, 'Could not connect to server');
|
||||
}
|
||||
new PNotify({
|
||||
title: 'Update Failed',
|
||||
text: 'Could not connect to server',
|
||||
|
||||
Reference in New Issue
Block a user