diff --git a/webui/html/index.html b/webui/html/index.html
index 5bf328e..b3c78e2 100644
--- a/webui/html/index.html
+++ b/webui/html/index.html
@@ -49,9 +49,6 @@
-
-
-
-
-
-
-
- Units (รฉรฉn per regel). Deze lijst wordt opgeslagen in je browser (localStorage).
-
-
-
- Opslaan
- Standaard
- Gebruik allowlist op server om te beperken.
-
-
- De server enforceโt jouw allowlist. Als je hier een unit invult die niet toegestaan is, krijg je 403.
-
-
-
-
- Snelle actie op รฉรฉn unit:
-
-
-
- Status
- Start
- Restart
- Stop
-
-
- Tip: gebruik demo1.service , demo2.service , sonarr.service om te testen.
-
-
-
-
-
-
-
-
- Unit
- Laatste status (API output)
- Acties
-
-
-
-
-
-
-
-
-
- Na wijzigen van *.container moet je meestal daemon-reload doen (kan via Systemd-tab of dashboard-knop).
+ Na wijzigen van *.container moet je meestal daemon-reload doen (via de dashboard-knop).
@@ -354,7 +279,7 @@
async function pingApi() {
try {
// simpele ping: pods ophalen
- await api('/pods', 'GET');
+ await api('/pods-dashboard', 'GET');
setApiState(true, 'API: OK');
} catch (e) {
setApiState(false, 'API: fout (' + e.message + ')');
@@ -372,22 +297,16 @@
async function refreshActive() {
try {
if (currentTab === 'containers') await fetchContainers();
- else if (currentTab === 'systemd') await systemdRefresh();
else {
- // dashboard: haal in achtergrond counts + mini systemd
const [pods, containers] = await Promise.all([
api('/pods-dashboard','GET'),
- api('/containers','GET')
+ api('/containers-dashboard','GET')
]);
document.getElementById('countPods').textContent = (pods || []).length;
- // containers list kan array of object zijn; jij gebruikt array
- const cCount = Array.isArray(containers) ? containers.length : (containers?.length || 0);
+
+ const list = Array.isArray(containers) ? containers : (containers?.containers || []);
+ const cCount = list.length;
document.getElementById('countContainers').textContent = cCount;
-
- const units = await getSystemdUnitsFromServer();
- document.getElementById('countSystemd').textContent = units.length;
-
- await systemdMiniRefresh();
}
setApiState(true, 'API: OK');
} catch (e) {
@@ -396,49 +315,14 @@
}
// ---- Pods ----
- async function fetchPods() {
- const pods = await api('/pods-dashboard','GET');
- document.getElementById('countPods').textContent = (pods || []).length;
- const tbody = document.getElementById('podsTbody');
- if (!tbody) return; // Pods-tab verwijderd: alleen countPods updaten, geen rendering
- tbody.innerHTML = (pods || []).map(p => {
- const name = p.Name || p.name || '';
- const status = p.Status || p.status || '';
- const containers = (Array.isArray(p.Containers) ? p.Containers : [])
- .map(c => {
- if (typeof c === 'string') return c; // jouw nieuwe API: list of strings
- const n = c?.Names;
- if (Array.isArray(n)) return n[0] || '';
- if (typeof n === 'string') return n;
- return c?.Name || c?.name || '';
- })
- .filter(Boolean)
- .join(', ');
- return `
-
- ${esc(name)}
- ${badgeFromStatus(status)}
- ${esc(containers || '')}
-
-
- Start
- Restart
- Stop
-
-
-
- `;
- }).join('');
- }
+
async function podAction(action, name) {
try {
const res = await api(`/pods/actions/${encodeURIComponent(action)}/${encodeURIComponent(name)}`, 'POST');
showModal(`Pod ${action}: ${name}`, JSON.stringify(res, null, 2));
- await fetchPods();
- if (currentTab === 'containers') {
- await fetchContainers();
- }
+
+ await fetchContainers(); // refresh pods + containers view in รฉรฉn keer
} catch (e) {
showModal(`Pod ${action} fout`, e.stack || e.message);
}
@@ -511,15 +395,15 @@
${ports || '-'}
- ๐
- ๐
+ ๐
+ ๐
${renderActionsDropdown(menuId, 'containerAction', esc(name))}
`;
}
-
+ let containersC2P = new Map();
function renderContainersGrouped(list, tbody, podStatus) {
const groups = new Map();
for (const c of (list || [])) {
@@ -538,8 +422,6 @@
return _cmpStr(a, b);
});
- const table = tbody.closest('table');
- const colCount = table ? table.querySelectorAll('thead th').length : 9;
containersC2P = new Map();
let html = '';
@@ -589,7 +471,7 @@
html += `
-
+
${collapsed ? 'โถ' : 'โผ'} ${esc(pod)}
@@ -626,7 +508,7 @@
if (cname) containersC2P.set(cname, pod);
const row = renderContainerRow(c).replace(
' {
+ tbody.querySelectorAll(`.pod-item-row[data-pod="${CSS.escape(podEnc)}"]`).forEach(r => {
r.style.display = isNowCollapsed ? 'none' : '';
});
t.innerHTML =
`${isNowCollapsed ? 'โถ' : 'โผ'} ${pod} ` +
- `(${tbody.querySelectorAll(`.pod-item-row[data-pod="${CSS.escape(pod)}"]`).length}) `;
+ `(${tbody.querySelectorAll(`.pod-item-row[data-pod="${CSS.escape(podEnc)}"]`).length}) `;
};
}
@@ -670,11 +553,12 @@
}
const tbody = document.getElementById('containersTbody');
+ if (!tbody) return;
renderContainersGrouped(list, tbody, podStatus);
}
let containersStatsES = null;
- let containersC2P = new Map();
+
function startContainersStatsStream() {
if (containersStatsES) return;
@@ -682,7 +566,13 @@
containersStatsES = new EventSource("/api/containers/stats/stream?interval=1");
containersStatsES.addEventListener("stats", (ev) => {
- const payload = JSON.parse(ev.data);
+ let payload;
+ try {
+ payload = JSON.parse(ev.data);
+ } catch (e) {
+ // Slechte SSE chunk -> negeren zodat de stream niet "stilvalt"
+ return;
+ }
const statsList = payload?.data?.Stats || [];
// totals per pod voor deze SSE tick
const podCpu = new Map(); // podName -> cpuPct sum
@@ -802,49 +692,11 @@
}
}
- // ---- Systemd UI storage ----
- const LS_KEY = 'mvp_systemd_units_v1';
- function loadDefaultUnits() {
- const defaults = ["demo1.service","demo2.service","sonarr.service"];
- document.getElementById('systemdUnits').value = defaults.join("\n");
- saveSystemdUnits();
- }
- function saveSystemdUnits() {
- const raw = document.getElementById('systemdUnits').value || '';
- const units = raw.split('\n').map(x => x.trim()).filter(Boolean);
- localStorage.setItem(LS_KEY, JSON.stringify(units));
- systemdRenderRows(units);
- refreshActive();
- }
- async function getSystemdUnitsFromServer() {
- const data = await api('/systemd/allowlist', 'GET');
- const units = Array.isArray(data.units) ? data.units : [];
- // vul textarea ook
- const ta = document.getElementById('systemdUnits');
- if (ta) ta.value = units.join("\n");
- return units;
- }
- function systemdRenderRows(units) {
- const tbody = document.getElementById('systemdTbody');
- tbody.innerHTML = units.map(u => `
-
- ${esc(u)}
- -
-
-
- Status
- Start
- Restart
- Stop
-
-
-
- `).join('');
- }
-
function cssSafeId(s){
- // simpele safe id: base64-ish
- return btoa(unescape(encodeURIComponent(s))).replaceAll('=','').replaceAll('+','-').replaceAll('/','_');
+ const bytes = new TextEncoder().encode(String(s));
+ let bin = '';
+ bytes.forEach(b => bin += String.fromCharCode(b));
+ return btoa(bin).replaceAll('=','').replaceAll('+','-').replaceAll('/','_');
}
function normalizeContainerName(n) {
@@ -861,11 +713,6 @@
return n + "B";
}
- function encodeUnit(unit) {
- // encodeURIComponent is genoeg voor @ en .
- return encodeURIComponent(unit);
- }
-
async function daemonReload() {
try {
const res = await api('/daemon-reload','POST');
@@ -875,67 +722,6 @@
}
}
- async function systemdAction(action, unit) {
- try {
- const res = await api(`/${encodeURIComponent(action)}/${encodeUnit(unit)}`, 'POST');
- // res.output kan lang zijn
- showModal(`systemctl ${action} ${unit}`, (res.output ?? JSON.stringify(res, null, 2)));
- // update inline status cell
- const cell = document.getElementById('sys-out-' + cssSafeId(unit));
- if (cell) {
- const summary = (res.output || '').split('\n').slice(0,3).join(' / ') || '(geen output)';
- cell.textContent = summary;
- }
- } catch (e) {
- showModal(`systemctl ${action} fout`, e.stack || e.message);
- }
- }
-
- async function systemdActionSingle(action) {
- const unit = (document.getElementById('systemdOne').value || '').trim();
- if (!unit) return showModal('Systemd', 'Vul eerst een unit in (bijv. sonarr.service).');
- await systemdAction(action, unit);
- }
-
- async function systemdRefresh() {
- const units = await getSystemdUnitsFromServer();
- systemdRenderRows(units);
- document.getElementById('countSystemd').textContent = units.length;
-
- for (const u of units) {
- try {
- const res = await api(`/status/${encodeUnit(u)}`, 'POST');
- const cell = document.getElementById('sys-out-' + cssSafeId(u));
- if (cell) {
- const first = (res.output || '').split('\n')[0] || '';
- const activeLine = (res.output || '').split('\n').find(x => x.trim().startsWith('Active:')) || '';
- cell.textContent = (first + ' | ' + activeLine).trim();
- }
- } catch (e) {
- const cell = document.getElementById('sys-out-' + cssSafeId(u));
- if (cell) cell.textContent = 'ERROR: ' + e.message;
- }
- }
- }
-
- async function systemdMiniRefresh() {
- const units = await getSystemdUnitsFromServer();
- const mini = document.getElementById('systemdMini');
- if (!mini) return;
-
- const lines = [];
- for (const u of units.slice(0, 6)) {
- try {
- const res = await api(`/status/${encodeUnit(u)}`, 'POST');
- const activeLine = (res.output || '').split('\n').find(x => x.trim().startsWith('Active:')) || '';
- lines.push(`${u}: ${activeLine.replace('Active:','').trim() || 'unknown'}`);
- } catch (e) {
- lines.push(`${u}: ERROR (${e.message})`);
- }
- }
- mini.innerHTML = `
${esc(lines.join('\n'))} `;
- }
-
// =========================
// Files tab (systemd subtree)
// =========================
@@ -1002,8 +788,8 @@
๐ ${esc(folderLabel)}
- +
- ๐๏ธ
+ +
+ ๐๏ธ
`);
@@ -1020,7 +806,7 @@
const fullUi = uiFolderPath ? `${uiFolderPath}/${f}` : f;
parts.push(`