(force = false)
| 63 | }; |
| 64 | |
| 65 | async function loadCredentials(force = false) { |
| 66 | if (!force && credentialsCache) { displayCredentials(credentialsCache); return; } |
| 67 | try { |
| 68 | const data = await fetchAPI('/api/credentials'); |
| 69 | // Flatten into unified array |
| 70 | const flat = []; |
| 71 | Object.entries(data).forEach(([svc, entries]) => { |
| 72 | (entries || []).forEach(e => flat.push({ ...e, service: svc })); |
| 73 | }); |
| 74 | credentialsCache = flat; |
| 75 | displayCredentials(flat); |
| 76 | } catch(err) { |
| 77 | const tbody = document.getElementById('cred-table-body'); |
| 78 | if (tbody) tbody.innerHTML = |
| 79 | `<tr><td colspan="6" class="py-8 text-center text-red-400">Error loading credentials: ${escapeHtml(err.message)}</td></tr>`; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | function onCredSearch(val) { credIPSearch = val.trim().toLowerCase(); displayCredentials(credentialsCache); } |
| 84 |
no test coverage detected