mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-25 19:54:15 +00:00
improvements to how scanning works, and helmchart ui
This commit is contained in:
+75
-11
@@ -438,6 +438,11 @@ async function deleteUntaggedManifests(repository) {
|
||||
const confirmBtn = document.getElementById('confirm-untagged-delete-btn');
|
||||
const originalText = confirmBtn.textContent;
|
||||
|
||||
const restoreButton = () => {
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.textContent = originalText;
|
||||
};
|
||||
|
||||
try {
|
||||
confirmBtn.disabled = true;
|
||||
confirmBtn.textContent = 'Deleting...';
|
||||
@@ -449,26 +454,40 @@ async function deleteUntaggedManifests(repository) {
|
||||
body: JSON.stringify({ repo: repository }),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const raw = await response.text();
|
||||
let data = null;
|
||||
try {
|
||||
data = raw ? JSON.parse(raw) : null;
|
||||
} catch (_) {
|
||||
// Non-JSON body (e.g., upstream proxy error page) — fall through.
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
const deleted = (data && data.deleted) || 0;
|
||||
const failed = (data && data.failed) || 0;
|
||||
closeDialog(document.getElementById('untagged-delete-modal'));
|
||||
showToast(`Deleted ${data.deleted} untagged manifest(s)`, 'success');
|
||||
if (data.deleted > 0) {
|
||||
location.reload();
|
||||
if (failed > 0) {
|
||||
showToast(`Deleted ${deleted} of ${deleted + failed} untagged manifest(s); ${failed} failed`, 'error');
|
||||
} else if (deleted > 0) {
|
||||
showToast(`Deleted ${deleted} untagged manifest(s)`, 'success');
|
||||
} else {
|
||||
showToast('No untagged manifests to delete', 'info');
|
||||
}
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.textContent = originalText;
|
||||
if (deleted > 0) {
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
restoreButton();
|
||||
} else {
|
||||
showToast(`Failed to delete untagged manifests: ${data.error || 'Unknown error'}`, 'error');
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.textContent = originalText;
|
||||
const errMsg = (data && data.error) || raw || `HTTP ${response.status}`;
|
||||
const partial = data && data.deleted ? ` (${data.deleted} succeeded before failure)` : '';
|
||||
showToast(`Failed to delete untagged manifests: ${errMsg}${partial}`, 'error');
|
||||
restoreButton();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error deleting untagged manifests:', err);
|
||||
showToast(`Error: ${err.message}`, 'error');
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.textContent = originalText;
|
||||
restoreButton();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,6 +632,51 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (sel) sel.addEventListener('change', () => updatePullCommand(sel.value));
|
||||
});
|
||||
|
||||
// Helm install/pull switcher. Persisted via localStorage (no profile setting
|
||||
// — too minor to round-trip through the API). Uses event delegation +
|
||||
// htmx:afterSettle so it survives the repo page's HTMX tag swaps; binding
|
||||
// on DOMContentLoaded alone would only catch the first render.
|
||||
function helmCmdSwitcher_render(mode) {
|
||||
const container = document.getElementById('helm-cmd-container');
|
||||
if (!container) return;
|
||||
const registryURL = container.dataset.registryUrl;
|
||||
const ownerHandle = container.dataset.ownerHandle;
|
||||
const repoName = container.dataset.repoName;
|
||||
const tag = container.dataset.tag || '';
|
||||
const ociRef = 'oci://' + registryURL + '/' + ownerHandle + '/' + repoName;
|
||||
const versionFlag = tag ? ' --version ' + tag : '';
|
||||
const cmd = mode === 'pull'
|
||||
? 'helm pull ' + ociRef + versionFlag
|
||||
: 'helm install ' + repoName + ' ' + ociRef + versionFlag;
|
||||
const display = document.getElementById('helm-cmd-display');
|
||||
if (!display) return;
|
||||
const code = display.querySelector('code');
|
||||
if (code) code.textContent = cmd;
|
||||
const btn = display.querySelector('[data-cmd]');
|
||||
if (btn) btn.dataset.cmd = cmd;
|
||||
}
|
||||
|
||||
function helmCmdSwitcher_apply() {
|
||||
const sel = document.getElementById('helm-cmd-switcher');
|
||||
if (!sel) return;
|
||||
const saved = lsGet('helm-cmd');
|
||||
if (saved === 'install' || saved === 'pull') {
|
||||
sel.value = saved;
|
||||
}
|
||||
helmCmdSwitcher_render(sel.value);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', helmCmdSwitcher_apply);
|
||||
document.body.addEventListener('htmx:afterSettle', helmCmdSwitcher_apply);
|
||||
|
||||
// Delegated change handler: works on the initial render AND on any switcher
|
||||
// HTMX swaps into the DOM later (repo page tag switch).
|
||||
document.addEventListener('change', (e) => {
|
||||
if (!e.target || e.target.id !== 'helm-cmd-switcher') return;
|
||||
lsSet('helm-cmd', e.target.value);
|
||||
helmCmdSwitcher_render(e.target.value);
|
||||
});
|
||||
|
||||
// Install page: platform tab switcher. Each .platform-tab has data-platform
|
||||
// pointing at a sibling panel (#<platform>-content). No-op off the install page.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
@@ -202,7 +202,7 @@ document.body.addEventListener('htmx:beforeSwap', () => {
|
||||
function initTabController() {
|
||||
if (!document.getElementById('tag-content')) return;
|
||||
|
||||
const validTabs = ['overview', 'layers', 'vulns', 'sbom', 'artifacts'];
|
||||
const validTabs = ['overview', 'layers', 'vulns', 'sbom', 'artifacts', 'chart'];
|
||||
// State per target id: 'loading' while a request is in-flight, 'loaded'
|
||||
// on success. On error we clear the entry so the retry button can
|
||||
// trigger a fresh fetch; without a separate 'loading' marker, a failing
|
||||
@@ -328,6 +328,7 @@ function initTabController() {
|
||||
if (tabId === 'layers') { const u = contentUrl('layers'); if (u) lazyLoad('layers-content', u); }
|
||||
if (tabId === 'vulns') { const u = contentUrl('vulns'); if (u) lazyLoad('vulns-content', u); }
|
||||
if (tabId === 'sbom') { const u = contentUrl('sbom'); if (u) lazyLoad('sbom-content', u); }
|
||||
if (tabId === 'chart') { const u = contentUrl('chart'); if (u) lazyLoad('chart-content', u); }
|
||||
};
|
||||
|
||||
function initTabs() {
|
||||
@@ -338,6 +339,7 @@ function initTabController() {
|
||||
['layers-tab-btn', 'layers-content', () => contentUrl('layers')],
|
||||
['vulns-tab-btn', 'vulns-content', () => contentUrl('vulns')],
|
||||
['sbom-tab-btn', 'sbom-content', () => contentUrl('sbom')],
|
||||
['chart-tab-btn', 'chart-content', () => contentUrl('chart')],
|
||||
];
|
||||
prefetch.forEach(([btnId, targetId, urlFn]) => {
|
||||
const btn = document.getElementById(btnId);
|
||||
@@ -386,7 +388,7 @@ function initTabController() {
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' ||
|
||||
e.target.tagName === 'SELECT' || e.target.isContentEditable) return;
|
||||
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
||||
const map = { o: 'overview', l: 'layers', v: 'vulns', s: 'sbom', a: 'artifacts' };
|
||||
const map = { o: 'overview', l: 'layers', v: 'vulns', s: 'sbom', a: 'artifacts', c: 'chart' };
|
||||
const tab = map[e.key.toLowerCase()];
|
||||
if (tab && validTabs.indexOf(tab) !== -1) window.switchRepoTab(tab);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user