From 7e89ab6709162be9075eca3b2c1f89afbc33e21c Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Tue, 9 Jun 2026 13:09:26 +0000 Subject: [PATCH] =?UTF-8?q?Fix=20proxy=20modals=20never=20saving=20?= =?UTF-8?q?=E2=80=94=20all=20were=20passing=20callbacks=20as=20footerHtml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nova.modal(title, body, footerHtml) expects an HTML string for the third parameter. proxyAddHost, proxyEditHost, proxySwitchLocal, and proxyUninstall were all passing async functions instead, which got stringified as garbage text with no actual buttons rendered. Each modal now gets proper Cancel + action button footer HTML, with the save/action logic wired via addEventListener after modal creation. Co-Authored-By: Claude Sonnet 4.6 --- panel/public/assets/js/admin.js | 54 ++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/panel/public/assets/js/admin.js b/panel/public/assets/js/admin.js index 40671c1..e539d0c 100644 --- a/panel/public/assets/js/admin.js +++ b/panel/public/assets/js/admin.js @@ -2749,7 +2749,7 @@ window.proxySync = async () => { }; window.proxyAddHost = () => { - Nova.modal('Add Proxy Host', ` + const ov = Nova.modal('Add Proxy Host', `
@@ -2759,16 +2759,23 @@ window.proxyAddHost = () => {
- `, async () => { + `, + ` + ` + ); + ov.querySelector('#ph-save-btn').addEventListener('click', async () => { const domain = document.getElementById('ph-domain')?.value?.trim(); const upstream = document.getElementById('ph-upstream')?.value?.trim(); if (!domain || !upstream) { Nova.toast('Domain and upstream required', 'error'); return; } + const btn = ov.querySelector('#ph-save-btn'); + btn.disabled = true; btn.textContent = 'Adding…'; const r = await Nova.api('proxy', 'hosts', { method: 'POST', body: { domain, upstream, ssl_enabled: document.getElementById('ph-ssl')?.checked ? 1 : 0 } }); Nova.toast(r?.success ? 'Host added' : (r?.message || 'Failed'), r?.success ? 'success' : 'error'); - if (r?.success) Nova.loadPage('nginx-proxy', window._novaPages); + if (r?.success) { ov.remove(); Nova.loadPage('nginx-proxy', window._novaPages); } + else { btn.disabled = false; btn.textContent = 'Add Host'; } }); }; @@ -2777,7 +2784,7 @@ window.proxyEditHost = async (id) => { const hosts = hostsR?.data || (Array.isArray(hostsR) ? hostsR : []); const h = hosts.find(x => x.id == id); if (!h) return; - Nova.modal('Edit Proxy Host', ` + const ov = Nova.modal('Edit Proxy Host', `
@@ -2787,7 +2794,13 @@ window.proxyEditHost = async (id) => {
Leave blank to use auto-generated config
- `, async () => { + `, + ` + ` + ); + ov.querySelector('#phe-save-btn').addEventListener('click', async () => { + const btn = ov.querySelector('#phe-save-btn'); + btn.disabled = true; btn.textContent = 'Saving…'; const r = await Nova.api('proxy', 'host', { method: 'PUT', body: { id, @@ -2798,7 +2811,8 @@ window.proxyEditHost = async (id) => { } }); Nova.toast(r?.success ? 'Updated' : (r?.message || 'Failed'), r?.success ? 'success' : 'error'); - if (r?.success) Nova.loadPage('nginx-proxy', window._novaPages); + if (r?.success) { ov.remove(); Nova.loadPage('nginx-proxy', window._novaPages); } + else { btn.disabled = false; btn.textContent = 'Save Changes'; } }); }; @@ -2892,7 +2906,7 @@ window.proxySetupInstructions = async () => { }; window.proxySwitchLocal = () => { - Nova.modal('Enable Local Nginx Proxy', ` + const slOv = Nova.modal('Enable Local Nginx Proxy', `

Nginx will be installed on this server and take over ports 80/443. Apache moves to an internal port and keeps serving all PHP sites — end users see no change.

What will happen:
@@ -2909,7 +2923,12 @@ window.proxySwitchLocal = () => {
- `, () => { + `, + ` + ` + ); + slOv.querySelector('#sl-switch-btn').addEventListener('click', () => { + slOv.remove(); const port = parseInt(document.getElementById('sl-port')?.value) || 8090; const ov = Nova.modal('Switching to Local Proxy Mode', `

Moving Apache to port ${port} and starting nginx on 80/443…

@@ -2947,7 +2966,7 @@ window.proxySwitchLocal = () => { }).catch(e => { log.textContent += '\n— Connection error: ' + e.message + '\n'; }); ov.querySelector('.modal-close')?.addEventListener('click', () => { done = true; }); - }, { confirmLabel: 'Switch Now' }); + }); }; window.proxyDisableLocal = () => { @@ -3017,18 +3036,25 @@ window.proxyRunSetup = () => { }; window.proxyUninstall = () => { - Nova.modal('Uninstall Nginx Proxy', ` + const ov = Nova.modal('Uninstall Nginx Proxy', `

Choose what to remove from the remote proxy VM:


- `, async () => { - const full = document.querySelector('input[name="uninst"]:checked')?.value === 'full'; + `, + ` + ` + ); + ov.querySelector('#uninst-btn').addEventListener('click', async () => { + const btn = ov.querySelector('#uninst-btn'); + btn.disabled = true; btn.textContent = 'Removing…'; + const full = ov.querySelector('input[name="uninst"]:checked')?.value === 'full'; const r = await Nova.api('proxy', 'uninstall', { method: 'DELETE', body: { remove_nginx: full } }); Nova.toast(r?.data?.result || r?.message || 'Done', r?.success ? 'success' : 'error'); - if (r?.success) Nova.loadPage('nginx-proxy', window._novaPages); - }, { confirmLabel: 'Uninstall', danger: true }); + if (r?.success) { ov.remove(); Nova.loadPage('nginx-proxy', window._novaPages); } + else { btn.disabled = false; btn.textContent = 'Uninstall'; } + }); }; window.proxySettings = async () => {