From 45a9aec787c0737277e80bae9d21d12b1e188450 Mon Sep 17 00:00:00 2001 From: Jakub Dorfman Date: Tue, 28 Apr 2026 23:20:49 +0200 Subject: [PATCH] feat: add routing tab and fetch routing details for apps in frontend fix: update compose commands to remove orphan containers in panelctl Co-authored-by: Copilot --- frontend/index.html | 50 +++++++++++++++++++++++++++++++++++++++++---- panelctl.sh | 10 ++++----- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 1c1516d..bdffb1a 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -475,6 +475,7 @@ function renderAppCard(app) { +
@@ -505,6 +506,12 @@ function renderAppCard(app) {
Click "Refresh" to load backups.
+
+
+ +
+
Click "Load App Variables" to view routing details & directories.
+
`; @@ -562,6 +569,7 @@ function attachCardListeners() { document.getElementById(`tab-compose-${app}`).classList.toggle("active", tabName === "compose"); document.getElementById(`tab-logs-${app}`).classList.toggle("active", tabName === "logs"); document.getElementById(`tab-backups-${app}`).classList.toggle("active", tabName === "backups"); + document.getElementById(`tab-routing-${app}`).classList.toggle("active", tabName === "routing"); if (tabName === "logs") loadLogs(app); if (tabName === "backups") loadBackups(app); @@ -572,13 +580,22 @@ function attachCardListeners() { document.querySelectorAll("[data-action]").forEach(btn => { btn.onclick = (e) => { e.stopPropagation(); - handleAction(btn.dataset.action, btn.dataset.app); + handleAction(btn.dataset.action, btn.dataset.app, btn); }; }); } // ─── Actions ─── -async function handleAction(action, name) { +async function handleAction(action, name, btnElement) { + const originalWidth = btnElement ? btnElement.offsetWidth : null; + const originalHtml = btnElement ? btnElement.innerHTML : null; + + if (btnElement) { + btnElement.disabled = true; + if (originalWidth) btnElement.style.width = `${originalWidth}px`; + btnElement.innerHTML = ``; + } + try { switch (action) { case "deploy": @@ -628,9 +645,34 @@ async function handleAction(action, name) { setStatus(`Backing up ${name}...`); await api.backup(name); setStatus(`Backup created for ${name}.`); - await loadBackups(name); + case "fetch-routing": + setStatus(`Fetching routing details for ${name}...`); + const appRes = await api.getApp(name); + if (appRes.app) { + const info = appRes.app; + document.getElementById(`routing-${name}`).innerHTML = ` +Primary Domain: ${escHtml(info.APP_DOMAIN || "-")} +All Domains: ${escHtml(info.APP_DOMAINS || "-")} +Upstream Target: ${escHtml(info.APP_UPSTREAM || "-")} +Protected: ${escHtml(info.APP_AUTH_PROTECTED || "-")} +Port: ${escHtml(info.APP_PORT || "-")} + +Volumes Directory: ${escHtml(info.APP_VOLUME_DIR || "-")} +Stack Directory: ${escHtml(info.APP_STACK_DIR || "-")} +Caddy Route Block: ${escHtml(info.APP_ROUTE_FILE || "-")} + `.trim(); + } + setStatus(`Loaded routing for ${name}.`); break; - case "refresh-backups": + } + } catch (err) { + setStatus(`${action} failed for ${name}: ${err.message}`, true); + } finally { + if (btnElement && originalHtml) { + btnElement.disabled = false; + btnElement.innerHTML = originalHtml; + btnElement.style.width = ''; + } await loadBackups(name); break; case "render-route": diff --git a/panelctl.sh b/panelctl.sh index 050cd7b..9334356 100644 --- a/panelctl.sh +++ b/panelctl.sh @@ -344,7 +344,7 @@ cmd_deploy() { cmd_render_route "${name}" - if ! run_compose -f "${APP_COMPOSE_FILE}" up -d 2>&1 | systemd-cat -t panelctl -p info 2>/dev/null; then + if ! run_compose -f "${APP_COMPOSE_FILE}" up -d --remove-orphans 2>&1 | systemd-cat -t panelctl -p info 2>/dev/null; then log err "Deployment failed for app '${name}'" fail "compose up failed" fi @@ -359,9 +359,9 @@ cmd_restart() { log info "Restarting app '${name}'" - run_compose -f "${APP_COMPOSE_FILE}" down || fail "compose down failed" + run_compose -f "${APP_COMPOSE_FILE}" down --remove-orphans || fail "compose down failed" - if ! run_compose -f "${APP_COMPOSE_FILE}" up -d 2>&1; then + if ! run_compose -f "${APP_COMPOSE_FILE}" up -d --remove-orphans 2>&1; then fail "compose up failed during restart" fi @@ -373,7 +373,7 @@ cmd_stop() { validate_name "${name}" load_app "${name}" - run_compose -f "${APP_COMPOSE_FILE}" down || fail "compose down failed" + run_compose -f "${APP_COMPOSE_FILE}" down --remove-orphans || fail "compose down failed" log info "stopped app '${name}'" } @@ -429,7 +429,7 @@ cmd_remove() { validate_name "${name}" load_app "${name}" - run_compose -f "${APP_COMPOSE_FILE}" down 2>/dev/null || true + run_compose -f "${APP_COMPOSE_FILE}" down --remove-orphans 2>/dev/null || true # Remove this app's block from the aggregate routes file. local route_file