Support multiple deploy template list URLs with cross-list dedupe
CI / test (push) Successful in 9m55s

Add Settings UI to configure many Portainer-format template catalogs (not just one), merge them on fetch, drop duplicates by image/stack/title, and show source counts on the Deploy view.
This commit is contained in:
2026-07-10 22:19:46 -04:00
parent 60816ed78b
commit 2d8b24ec0e
7 changed files with 603 additions and 25 deletions
+21 -7
View File
@@ -1,6 +1,7 @@
// Import dependencies first (ES6 imports must be at top)
import { closeAllModals, showStatusIndicator, hideStatusIndicator, updateStatusIndicator, showAlert } from './uiUtils.js';
import notificationManager from './notifications.js';
import { fetchMergedTemplates, getTemplateListUrls } from '../client/templateLists.js';
// DOM Elements - Lazy loaded (initialized when modal opens)
let templateList = null;
@@ -62,22 +63,35 @@ function initTemplateDeployer() {
setupFormSubmitListener();
}
// Fetch templates from the URL
// Fetch templates from all configured list URLs (multi-source, de-duplicated)
async function fetchTemplates() {
// Ensure template deployer is initialized before fetching
initTemplateDeployer();
try {
const response = await fetch('https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
templates = data.templates || []; // Update global templates
const urls = getTemplateListUrls();
const result = await fetchMergedTemplates(urls);
templates = result.templates || [];
displayTemplateList(templates);
const st = result.stats || {};
if (st.duplicates > 0) {
console.log(
`[INFO] Templates: ${st.unique} unique from ${st.sources} list(s), removed ${st.duplicates} duplicate(s)`
);
}
if (st.errors?.length && !templates.length) {
showAlert('danger', `Failed to load templates from ${st.errors.length} list(s).`);
} else if (st.errors?.length) {
showAlert(
'warning',
`Loaded ${templates.length} templates; ${st.errors.length} list(s) failed to fetch.`
);
}
return result;
} catch (error) {
console.error('[ERROR] Failed to fetch templates:', error.message);
showAlert('danger', 'Failed to load templates.');
return null;
}
}