Fix missing column for 'Abort Incomplete MPU' in the lifecycle panel
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
(function() {
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const { formatBytes, escapeHtml, fallbackCopy, setupJsonAutoIndent } = window.BucketDetailUtils || {
|
||||
@@ -23,7 +23,7 @@
|
||||
.replace(/'/g, ''');
|
||||
},
|
||||
fallbackCopy: () => false,
|
||||
setupJsonAutoIndent: () => {}
|
||||
setupJsonAutoIndent: () => { }
|
||||
};
|
||||
|
||||
setupJsonAutoIndent(document.getElementById('policyDocument'));
|
||||
@@ -548,7 +548,7 @@
|
||||
} else if (msg.type === 'done') {
|
||||
streamingComplete = true;
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
flushPendingStreamObjects();
|
||||
@@ -2040,7 +2040,7 @@
|
||||
uploadCancelled = true;
|
||||
|
||||
activeXHRs.forEach(xhr => {
|
||||
try { xhr.abort(); } catch {}
|
||||
try { xhr.abort(); } catch { }
|
||||
});
|
||||
activeXHRs = [];
|
||||
|
||||
@@ -2049,7 +2049,7 @@
|
||||
const csrfToken = document.querySelector('input[name="csrf_token"]')?.value;
|
||||
try {
|
||||
await fetch(abortUrl, { method: 'DELETE', headers: { 'X-CSRFToken': csrfToken || '' } });
|
||||
} catch {}
|
||||
} catch { }
|
||||
activeMultipartUpload = null;
|
||||
}
|
||||
|
||||
@@ -2275,7 +2275,7 @@
|
||||
if (!uploadCancelled) {
|
||||
try {
|
||||
await fetch(abortUrl, { method: 'DELETE', headers: { 'X-CSRFToken': csrfToken || '' } });
|
||||
} catch {}
|
||||
} catch { }
|
||||
}
|
||||
activeMultipartUpload = null;
|
||||
throw err;
|
||||
@@ -3177,7 +3177,7 @@
|
||||
|
||||
const loadLifecycleRules = async () => {
|
||||
if (!lifecycleUrl || !lifecycleRulesBody) return;
|
||||
lifecycleRulesBody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-4"><div class="spinner-border spinner-border-sm me-2" role="status"></div>Loading...</td></tr>';
|
||||
lifecycleRulesBody.innerHTML = '<tr><td colspan="7" class="text-center text-muted py-4"><div class="spinner-border spinner-border-sm me-2" role="status"></div>Loading...</td></tr>';
|
||||
try {
|
||||
const resp = await fetch(lifecycleUrl);
|
||||
const data = await resp.json();
|
||||
@@ -3185,19 +3185,20 @@
|
||||
lifecycleRules = data.rules || [];
|
||||
renderLifecycleRules();
|
||||
} catch (err) {
|
||||
lifecycleRulesBody.innerHTML = `<tr><td colspan="6" class="text-center text-danger py-4">${escapeHtml(err.message)}</td></tr>`;
|
||||
lifecycleRulesBody.innerHTML = `<tr><td colspan="7" class="text-center text-danger py-4">${escapeHtml(err.message)}</td></tr>`;
|
||||
}
|
||||
};
|
||||
|
||||
const renderLifecycleRules = () => {
|
||||
if (!lifecycleRulesBody) return;
|
||||
if (lifecycleRules.length === 0) {
|
||||
lifecycleRulesBody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-4">No lifecycle rules configured</td></tr>';
|
||||
lifecycleRulesBody.innerHTML = '<tr><td colspan="7" class="text-center text-muted py-4">No lifecycle rules configured</td></tr>';
|
||||
return;
|
||||
}
|
||||
lifecycleRulesBody.innerHTML = lifecycleRules.map((rule, idx) => {
|
||||
const expiration = rule.Expiration?.Days ? `${rule.Expiration.Days}d` : '-';
|
||||
const noncurrent = rule.NoncurrentVersionExpiration?.NoncurrentDays ? `${rule.NoncurrentVersionExpiration.NoncurrentDays}d` : '-';
|
||||
const abortMpu = rule.AbortIncompleteMultipartUpload?.DaysAfterInitiation ? `${rule.AbortIncompleteMultipartUpload.DaysAfterInitiation}d` : '-';
|
||||
const statusClass = rule.Status === 'Enabled' ? 'bg-success' : 'bg-secondary';
|
||||
return `<tr>
|
||||
<td><code class="small">${escapeHtml(rule.ID || '')}</code></td>
|
||||
@@ -3205,6 +3206,7 @@
|
||||
<td><span class="badge ${statusClass}">${escapeHtml(rule.Status)}</span></td>
|
||||
<td class="small">${expiration}</td>
|
||||
<td class="small">${noncurrent}</td>
|
||||
<td class="small">${abortMpu}</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-outline-secondary" onclick="editLifecycleRule(${idx})" title="Edit rule">
|
||||
@@ -3490,7 +3492,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('objects-table')?.addEventListener('show.bs.dropdown', function(e) {
|
||||
document.getElementById('objects-table')?.addEventListener('show.bs.dropdown', function (e) {
|
||||
const dropdown = e.target.closest('.dropdown');
|
||||
const menu = dropdown?.querySelector('.dropdown-menu');
|
||||
const btn = e.target;
|
||||
@@ -3789,18 +3791,18 @@
|
||||
var form = document.getElementById(formId);
|
||||
if (!form) return;
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
window.UICore.submitFormAjax(form, {
|
||||
successMessage: options.successMessage || 'Operation completed',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
if (options.onSuccess) options.onSuccess(data);
|
||||
if (options.closeModal) {
|
||||
var modal = bootstrap.Modal.getInstance(document.getElementById(options.closeModal));
|
||||
if (modal) modal.hide();
|
||||
}
|
||||
if (options.reload) {
|
||||
setTimeout(function() { location.reload(); }, 500);
|
||||
setTimeout(function () { location.reload(); }, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -3855,11 +3857,11 @@
|
||||
var newForm = document.getElementById('enableVersioningForm');
|
||||
if (newForm) {
|
||||
newForm.setAttribute('action', window.BucketDetailConfig?.endpoints?.versioning || '');
|
||||
newForm.addEventListener('submit', function(e) {
|
||||
newForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
window.UICore.submitFormAjax(newForm, {
|
||||
successMessage: 'Versioning enabled',
|
||||
onSuccess: function() {
|
||||
onSuccess: function () {
|
||||
updateVersioningBadge(true);
|
||||
updateVersioningCard(true);
|
||||
}
|
||||
@@ -3949,7 +3951,7 @@
|
||||
'<p class="mb-0 small">No bucket policy is attached. Access is controlled by IAM policies only.</p></div>';
|
||||
}
|
||||
}
|
||||
document.querySelectorAll('.preset-btn').forEach(function(btn) {
|
||||
document.querySelectorAll('.preset-btn').forEach(function (btn) {
|
||||
btn.classList.remove('active');
|
||||
if (btn.dataset.preset === preset) btn.classList.add('active');
|
||||
});
|
||||
@@ -3963,7 +3965,7 @@
|
||||
|
||||
interceptForm('enableVersioningForm', {
|
||||
successMessage: 'Versioning enabled',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
updateVersioningBadge(true);
|
||||
updateVersioningCard(true);
|
||||
}
|
||||
@@ -3972,7 +3974,7 @@
|
||||
interceptForm('suspendVersioningForm', {
|
||||
successMessage: 'Versioning suspended',
|
||||
closeModal: 'suspendVersioningModal',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
updateVersioningBadge(false);
|
||||
updateVersioningCard(false);
|
||||
}
|
||||
@@ -3980,21 +3982,21 @@
|
||||
|
||||
interceptForm('encryptionForm', {
|
||||
successMessage: 'Encryption settings saved',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
updateEncryptionCard(data.enabled !== false, data.algorithm || 'AES256');
|
||||
}
|
||||
});
|
||||
|
||||
interceptForm('quotaForm', {
|
||||
successMessage: 'Quota settings saved',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
updateQuotaCard(data.has_quota, data.max_bytes, data.max_objects);
|
||||
}
|
||||
});
|
||||
|
||||
interceptForm('bucketPolicyForm', {
|
||||
successMessage: 'Bucket policy saved',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
var policyModeEl = document.getElementById('policyMode');
|
||||
var policyPresetEl = document.getElementById('policyPreset');
|
||||
var preset = policyModeEl && policyModeEl.value === 'delete' ? 'private' :
|
||||
@@ -4005,11 +4007,11 @@
|
||||
|
||||
var deletePolicyForm = document.getElementById('deletePolicyForm');
|
||||
if (deletePolicyForm) {
|
||||
deletePolicyForm.addEventListener('submit', function(e) {
|
||||
deletePolicyForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
window.UICore.submitFormAjax(deletePolicyForm, {
|
||||
successMessage: 'Bucket policy deleted',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
var modal = bootstrap.Modal.getInstance(document.getElementById('deletePolicyModal'));
|
||||
if (modal) modal.hide();
|
||||
updatePolicyCard(false, 'private');
|
||||
@@ -4022,13 +4024,13 @@
|
||||
|
||||
var disableEncBtn = document.getElementById('disableEncryptionBtn');
|
||||
if (disableEncBtn) {
|
||||
disableEncBtn.addEventListener('click', function() {
|
||||
disableEncBtn.addEventListener('click', function () {
|
||||
var form = document.getElementById('encryptionForm');
|
||||
if (!form) return;
|
||||
document.getElementById('encryptionAction').value = 'disable';
|
||||
window.UICore.submitFormAjax(form, {
|
||||
successMessage: 'Encryption disabled',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
document.getElementById('encryptionAction').value = 'enable';
|
||||
updateEncryptionCard(false, null);
|
||||
}
|
||||
@@ -4038,13 +4040,13 @@
|
||||
|
||||
var removeQuotaBtn = document.getElementById('removeQuotaBtn');
|
||||
if (removeQuotaBtn) {
|
||||
removeQuotaBtn.addEventListener('click', function() {
|
||||
removeQuotaBtn.addEventListener('click', function () {
|
||||
var form = document.getElementById('quotaForm');
|
||||
if (!form) return;
|
||||
document.getElementById('quotaAction').value = 'remove';
|
||||
window.UICore.submitFormAjax(form, {
|
||||
successMessage: 'Quota removed',
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
document.getElementById('quotaAction').value = 'set';
|
||||
updateQuotaCard(false, null, null);
|
||||
}
|
||||
@@ -4058,8 +4060,8 @@
|
||||
fetch(window.location.pathname + '?tab=replication', {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
})
|
||||
.then(function(resp) { return resp.text(); })
|
||||
.then(function(html) {
|
||||
.then(function (resp) { return resp.text(); })
|
||||
.then(function (html) {
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(html, 'text/html');
|
||||
var newPane = doc.getElementById('replication-pane');
|
||||
@@ -4069,20 +4071,20 @@
|
||||
initReplicationStats();
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
.catch(function (err) {
|
||||
console.error('Failed to reload replication pane:', err);
|
||||
});
|
||||
}
|
||||
|
||||
function initReplicationForms() {
|
||||
document.querySelectorAll('form[action*="replication"]').forEach(function(form) {
|
||||
document.querySelectorAll('form[action*="replication"]').forEach(function (form) {
|
||||
if (form.dataset.ajaxBound) return;
|
||||
form.dataset.ajaxBound = 'true';
|
||||
var actionInput = form.querySelector('input[name="action"]');
|
||||
if (!actionInput) return;
|
||||
var action = actionInput.value;
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
var msg = action === 'pause' ? 'Replication paused' :
|
||||
action === 'resume' ? 'Replication resumed' :
|
||||
@@ -4090,7 +4092,7 @@
|
||||
action === 'create' ? 'Replication configured' : 'Operation completed';
|
||||
window.UICore.submitFormAjax(form, {
|
||||
successMessage: msg,
|
||||
onSuccess: function(data) {
|
||||
onSuccess: function (data) {
|
||||
var modal = bootstrap.Modal.getInstance(document.getElementById('disableReplicationModal'));
|
||||
if (modal) modal.hide();
|
||||
reloadReplicationPane();
|
||||
@@ -4112,14 +4114,14 @@
|
||||
var bytesEl = statsContainer.querySelector('[data-stat="bytes"]');
|
||||
|
||||
fetch(statusEndpoint)
|
||||
.then(function(resp) { return resp.json(); })
|
||||
.then(function(data) {
|
||||
.then(function (resp) { return resp.json(); })
|
||||
.then(function (data) {
|
||||
if (syncedEl) syncedEl.textContent = data.objects_synced || 0;
|
||||
if (pendingEl) pendingEl.textContent = data.objects_pending || 0;
|
||||
if (orphanedEl) orphanedEl.textContent = data.objects_orphaned || 0;
|
||||
if (bytesEl) bytesEl.textContent = formatBytes(data.bytes_synced || 0);
|
||||
})
|
||||
.catch(function(err) {
|
||||
.catch(function (err) {
|
||||
console.error('Failed to load replication stats:', err);
|
||||
});
|
||||
}
|
||||
@@ -4129,10 +4131,10 @@
|
||||
|
||||
var deleteBucketForm = document.getElementById('deleteBucketForm');
|
||||
if (deleteBucketForm) {
|
||||
deleteBucketForm.addEventListener('submit', function(e) {
|
||||
deleteBucketForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
window.UICore.submitFormAjax(deleteBucketForm, {
|
||||
onSuccess: function() {
|
||||
onSuccess: function () {
|
||||
sessionStorage.setItem('flashMessage', JSON.stringify({ title: 'Bucket deleted', variant: 'success' }));
|
||||
window.location.href = window.BucketDetailConfig?.endpoints?.bucketsOverview || '/ui/buckets';
|
||||
}
|
||||
|
||||
@@ -1560,12 +1560,13 @@
|
||||
<th>Status</th>
|
||||
<th>Expiration</th>
|
||||
<th>Noncurrent</th>
|
||||
<th>Abort MPU</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="lifecycle-rules-body">
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted py-4">
|
||||
<td colspan="7" class="text-center text-muted py-4">
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
Loading...
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user