Webapp: Updates pages, image page tabs with review-before-save, BPRT sync
Updates group: - Dell drivers and BIOS: check Dell's catalogs against all three driver maps and models.txt, download with a shared speed cap, repack, preview, apply with backups and rollback. - iPXE boot files: check GitHub for the signed Secure Boot set, verify the sha256 and each file's signer, install atomically, roll back. Includes the by-hand procedure. - Blancco: upload a release, stage it, test-boot it from the menu, activate, roll back. - BPRT sync: validate a batch uploaded from the MediaCreator Lite cache (scripts/windows/Sync-BPRT.ps1 or a zip), show the diff, swap with the site overlay files kept. Images: one sidebar entry per image with Overview, Drivers, Unattend and OS and Packages tabs. Every save shows a diff first, keeps the file's format, makes a backup and can be undone. Driver lint runs inline. Enrollment: upload standard/engineer MCL packages, one per tenant/purpose, with archive and restore. Hardware coverage counts engineer as Win11.
This commit is contained in:
114
webapp/static/image-pages.js
Normal file
114
webapp/static/image-pages.js
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* image-pages.js - helpers shared by the GE Aerospace image tabs.
|
||||
* Loaded by templates/_image_header.html. Exposes window.PxeImage:
|
||||
*
|
||||
* PxeImage.postJSON(url, body) POST JSON with the CSRF header;
|
||||
* resolves {ok, status, data}.
|
||||
* PxeImage.renderDiff(el, files) draw [{name, changed, diff, reformat}]
|
||||
* (services/control_files.public) into el.
|
||||
* PxeImage.reviewAndSave(opts) preview -> diff modal -> save -> reload.
|
||||
* opts: previewUrl, saveUrl, body (object), title (optional),
|
||||
* onSaved(data) (optional; default reloads the page).
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function csrfToken() {
|
||||
var m = document.querySelector('meta[name="csrf-token"]');
|
||||
return m ? m.getAttribute('content') : '';
|
||||
}
|
||||
|
||||
function postJSON(url, body) {
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken() },
|
||||
body: JSON.stringify(body || {}),
|
||||
credentials: 'same-origin'
|
||||
}).then(function (resp) {
|
||||
return resp.json().catch(function () { return { error: 'HTTP ' + resp.status }; })
|
||||
.then(function (data) { return { ok: resp.ok, status: resp.status, data: data }; });
|
||||
});
|
||||
}
|
||||
|
||||
function el(tag, cls, text) {
|
||||
var e = document.createElement(tag);
|
||||
if (cls) e.className = cls;
|
||||
if (text !== undefined) e.textContent = text;
|
||||
return e;
|
||||
}
|
||||
|
||||
function renderDiff(container, files) {
|
||||
container.innerHTML = '';
|
||||
var changed = (files || []).filter(function (f) { return f.changed; });
|
||||
if (!changed.length) {
|
||||
container.appendChild(el('div', 'alert alert-info mb-0', 'No changes: the files already hold exactly this.'));
|
||||
return 0;
|
||||
}
|
||||
changed.forEach(function (f) {
|
||||
var box = el('div', 'diff-file');
|
||||
var head = el('div', 'diff-file-head');
|
||||
head.appendChild(el('i', 'bi bi-file-earmark-diff'));
|
||||
head.appendChild(el('span', 'mono', f.name));
|
||||
if (!f.existed) head.appendChild(el('span', 'badge badge-info', 'new file'));
|
||||
if (f.reformat) {
|
||||
var w = el('span', 'badge badge-warning', 'whitespace also changes');
|
||||
w.title = 'The current file is not laid out the way it will be written; ' +
|
||||
'unchanged lines may show as changed.';
|
||||
head.appendChild(w);
|
||||
}
|
||||
box.appendChild(head);
|
||||
var pre = el('pre', 'diff-view');
|
||||
String(f.diff || '').split('\n').forEach(function (line, i, all) {
|
||||
if (i === all.length - 1 && line === '') return;
|
||||
var c = '';
|
||||
if (line.indexOf('+++') === 0 || line.indexOf('---') === 0) c = 'd-meta';
|
||||
else if (line.indexOf('@@') === 0) c = 'd-hunk';
|
||||
else if (line.charAt(0) === '+') c = 'd-add';
|
||||
else if (line.charAt(0) === '-') c = 'd-del';
|
||||
pre.appendChild(el('span', c, line || ' '));
|
||||
});
|
||||
box.appendChild(pre);
|
||||
container.appendChild(box);
|
||||
});
|
||||
return changed.length;
|
||||
}
|
||||
|
||||
function reviewAndSave(opts) {
|
||||
var modalEl = document.getElementById('imageReviewModal');
|
||||
var body = document.getElementById('imageReviewBody');
|
||||
var err = document.getElementById('imageReviewError');
|
||||
var confirmBtn = document.getElementById('imageReviewConfirm');
|
||||
var title = document.getElementById('imageReviewTitle');
|
||||
if (!modalEl || !window.bootstrap) return;
|
||||
title.textContent = opts.title || 'Review changes';
|
||||
body.innerHTML = '<div class="text-muted py-3">Building the preview...</div>';
|
||||
err.classList.add('d-none');
|
||||
confirmBtn.disabled = true;
|
||||
var modal = bootstrap.Modal.getOrCreateInstance(modalEl);
|
||||
modal.show();
|
||||
|
||||
function fail(msg) {
|
||||
err.textContent = msg;
|
||||
err.classList.remove('d-none');
|
||||
}
|
||||
|
||||
postJSON(opts.previewUrl, opts.body).then(function (r) {
|
||||
if (!r.ok) { body.innerHTML = ''; fail(r.data.error || 'Preview failed.'); return; }
|
||||
var n = renderDiff(body, r.data.files);
|
||||
var expect = {};
|
||||
(r.data.files || []).forEach(function (f) { expect[f.name] = f.sha_before; });
|
||||
confirmBtn.disabled = n === 0;
|
||||
confirmBtn.onclick = function () {
|
||||
confirmBtn.disabled = true;
|
||||
var payload = Object.assign({}, opts.body, { expect: expect });
|
||||
postJSON(opts.saveUrl, payload).then(function (s) {
|
||||
if (!s.ok) { fail(s.data.error || 'Save failed.'); confirmBtn.disabled = false; return; }
|
||||
if (opts.onSaved) opts.onSaved(s.data);
|
||||
else window.location.reload();
|
||||
}).catch(function (e) { fail('Save failed: ' + e); confirmBtn.disabled = false; });
|
||||
};
|
||||
}).catch(function (e) { body.innerHTML = ''; fail('Preview failed: ' + e); });
|
||||
}
|
||||
|
||||
window.PxeImage = { postJSON: postJSON, renderDiff: renderDiff, reviewAndSave: reviewAndSave };
|
||||
})();
|
||||
Reference in New Issue
Block a user