Loading admin/static/app.js +93 −0 Changes for admin/static/app.js: 93 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -28,6 +28,16 @@ const ProxyPlusAdmin = (() => { tr.lastElementChild.appendChild(btn); } // Appends a "Bearbeiten" button into a row's last (empty) <td>. All create // endpoints upsert by id/key (see ConfigStore::put*), so editing just means: // show the create form again, pre-filled from this row, then resubmit. function addEditButton(tr, onClick) { const btn = document.createElement("button"); btn.textContent = "Bearbeiten"; btn.onclick = onClick; tr.lastElementChild.appendChild(btn); } async function login() { const username = document.getElementById("login-username").value; const password = document.getElementById("login-password").value; Loading Loading @@ -84,11 +94,25 @@ const ProxyPlusAdmin = (() => { : "") : "keine"; tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.path_prefix}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`; addEditButton(tr, () => editRoute(r)); addDeleteButton(tr, () => deleteRoute(r.id)); body.appendChild(tr); } } function editRoute(r) { showNewRouteForm(); document.getElementById("route-id").value = r.id; document.getElementById("route-id").readOnly = true; document.getElementById("route-domain").value = r.domain; document.getElementById("route-path").value = r.path_prefix; document.getElementById("route-upstream").value = r.upstream; document.getElementById("route-upstream-pool").value = r.upstream_pool; document.getElementById("route-permission").value = r.required_permission; document.getElementById("route-tls-verify").checked = r.tls_verify_upstream; document.getElementById("route-tls-pins").value = (r.tls_pinned_fingerprints_sha256 || []).join(", "); } async function deleteRoute(id) { if (!confirm("Route '" + id + "' wirklich löschen?")) return; try { Loading @@ -106,11 +130,22 @@ const ProxyPlusAdmin = (() => { for (const r of relays) { const tr = document.createElement("tr"); tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.smarthost}</td><td>${r.smarthost_pool}</td><td>${r.owner_group_id}</td><td></td>`; addEditButton(tr, () => editRelay(r)); addDeleteButton(tr, () => deleteRelay(r.id)); body.appendChild(tr); } } function editRelay(r) { showNewRelayForm(); document.getElementById("relay-id").value = r.id; document.getElementById("relay-id").readOnly = true; document.getElementById("relay-domain").value = r.domain; document.getElementById("relay-smarthost").value = r.smarthost; document.getElementById("relay-smarthost-pool").value = r.smarthost_pool; document.getElementById("relay-group").value = r.owner_group_id; } async function deleteRelay(id) { if (!confirm("Relay '" + id + "' wirklich löschen?")) return; try { Loading @@ -131,11 +166,23 @@ const ProxyPlusAdmin = (() => { .join(", "); const tr = document.createElement("tr"); tr.innerHTML = `<td>${p.id}</td><td>${p.kind}</td><td>${p.algorithm}</td><td>${targets}</td><td></td>`; addEditButton(tr, () => editPool(p)); addDeleteButton(tr, () => deletePool(p.id)); body.appendChild(tr); } } function editPool(p) { showNewPoolForm(); document.getElementById("pool-id").value = p.id; document.getElementById("pool-id").readOnly = true; document.getElementById("pool-kind").value = p.kind; document.getElementById("pool-algorithm").value = p.algorithm; document.getElementById("pool-targets").value = (p.targets || []) .map((t) => `${t.address}:${t.weight}`) .join(", "); } async function deletePool(id) { if (!confirm("Pool '" + id + "' wirklich löschen?")) return; try { Loading @@ -153,11 +200,38 @@ const ProxyPlusAdmin = (() => { for (const p of profiles) { const tr = document.createElement("tr"); tr.innerHTML = `<td>${p.profile}</td><td>${(p.domains || []).join(", ")}</td><td>${p.acme_enabled ? "ja" : "nein"}</td><td>${p.self_signed ? "ja" : "nein"}</td><td>${p.has_manual_cert ? "ja" : "nein"}</td><td>${p.acme_email}</td><td>${p.ca_plugin}</td><td></td>`; addEditButton(tr, () => editTlsProfile(p)); addDeleteButton(tr, () => deleteTlsProfile(p.profile)); body.appendChild(tr); } } function editTlsProfile(p) { showNewTlsProfileForm(); document.getElementById("tls-profile-id").value = p.profile; document.getElementById("tls-profile-id").readOnly = true; document.getElementById("tls-profile-domains").value = (p.domains || []).join(", "); document.getElementById("tls-profile-acme").checked = p.acme_enabled; document.getElementById("tls-profile-email").value = p.acme_email || ""; document.getElementById("tls-profile-directory").value = p.acme_directory_url || ""; document.getElementById("tls-profile-ca-plugin").value = p.ca_plugin || ""; document.getElementById("tls-profile-selfsigned").checked = p.self_signed; // GET never returns cert/key PEM (it's private key material) and POST fully // replaces the record, so leaving these blank on save would silently erase // an existing manual certificate -- warn rather than let that happen quietly. const certField = document.getElementById("tls-profile-cert-pem"); const keyField = document.getElementById("tls-profile-key-pem"); certField.value = ""; keyField.value = ""; const hint = p.has_manual_cert ? "Vorhandenes Zertifikat -- leer lassen entfernt es beim Speichern! Zum Behalten erneut einfuegen." : "Eigenes Zertifikat (PEM) -- nur im Cluster-Modus, sonst Datei direkt unter <data>/tls/<profil>.crt ablegen"; certField.placeholder = hint; keyField.placeholder = p.has_manual_cert ? "Vorhandener Schluessel -- leer lassen entfernt ihn beim Speichern! Zum Behalten erneut einfuegen." : "Eigener privater Schluessel (PEM) -- wird vor der Speicherung mit dem Cluster-Schluessel versiegelt"; } async function deleteTlsProfile(profile) { if (!confirm("TLS-Profil '" + profile + "' wirklich löschen?")) return; try { Loading @@ -175,11 +249,21 @@ const ProxyPlusAdmin = (() => { for (const k of keys) { const tr = document.createElement("tr"); tr.innerHTML = `<td>${k.domain}</td><td>${k.selector}</td><td>${k.key_path}</td><td>${k.enabled ? "ja" : "nein"}</td><td></td>`; addEditButton(tr, () => editDkimKey(k)); addDeleteButton(tr, () => deleteDkimKey(k.domain)); body.appendChild(tr); } } function editDkimKey(k) { showNewDkimKeyForm(); document.getElementById("dkim-domain").value = k.domain; document.getElementById("dkim-domain").readOnly = true; document.getElementById("dkim-selector").value = k.selector; document.getElementById("dkim-keypath").value = k.key_path; document.getElementById("dkim-enabled").checked = k.enabled; } async function deleteDkimKey(domain) { if (!confirm("DKIM-Key fuer '" + domain + "' wirklich löschen?")) return; try { Loading @@ -202,6 +286,7 @@ const ProxyPlusAdmin = (() => { } function showNewRouteForm() { document.getElementById("route-id").readOnly = false; document.getElementById("new-route-form").hidden = false; } Loading Loading @@ -229,6 +314,7 @@ const ProxyPlusAdmin = (() => { } function showNewRelayForm() { document.getElementById("relay-id").readOnly = false; document.getElementById("new-relay-form").hidden = false; } Loading @@ -249,6 +335,11 @@ const ProxyPlusAdmin = (() => { } function showNewTlsProfileForm() { document.getElementById("tls-profile-id").readOnly = false; document.getElementById("tls-profile-cert-pem").placeholder = "Eigenes Zertifikat (PEM) -- nur im Cluster-Modus, sonst Datei direkt unter <data>/tls/<profil>.crt ablegen"; document.getElementById("tls-profile-key-pem").placeholder = "Eigener privater Schluessel (PEM) -- wird vor der Speicherung mit dem Cluster-Schluessel versiegelt"; document.getElementById("new-tls-profile-form").hidden = false; } Loading Loading @@ -277,6 +368,7 @@ const ProxyPlusAdmin = (() => { } function showNewDkimKeyForm() { document.getElementById("dkim-domain").readOnly = false; document.getElementById("new-dkim-key-form").hidden = false; } Loading @@ -296,6 +388,7 @@ const ProxyPlusAdmin = (() => { } function showNewPoolForm() { document.getElementById("pool-id").readOnly = false; document.getElementById("new-pool-form").hidden = false; } Loading Loading
admin/static/app.js +93 −0 Changes for admin/static/app.js: 93 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -28,6 +28,16 @@ const ProxyPlusAdmin = (() => { tr.lastElementChild.appendChild(btn); } // Appends a "Bearbeiten" button into a row's last (empty) <td>. All create // endpoints upsert by id/key (see ConfigStore::put*), so editing just means: // show the create form again, pre-filled from this row, then resubmit. function addEditButton(tr, onClick) { const btn = document.createElement("button"); btn.textContent = "Bearbeiten"; btn.onclick = onClick; tr.lastElementChild.appendChild(btn); } async function login() { const username = document.getElementById("login-username").value; const password = document.getElementById("login-password").value; Loading Loading @@ -84,11 +94,25 @@ const ProxyPlusAdmin = (() => { : "") : "keine"; tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.path_prefix}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`; addEditButton(tr, () => editRoute(r)); addDeleteButton(tr, () => deleteRoute(r.id)); body.appendChild(tr); } } function editRoute(r) { showNewRouteForm(); document.getElementById("route-id").value = r.id; document.getElementById("route-id").readOnly = true; document.getElementById("route-domain").value = r.domain; document.getElementById("route-path").value = r.path_prefix; document.getElementById("route-upstream").value = r.upstream; document.getElementById("route-upstream-pool").value = r.upstream_pool; document.getElementById("route-permission").value = r.required_permission; document.getElementById("route-tls-verify").checked = r.tls_verify_upstream; document.getElementById("route-tls-pins").value = (r.tls_pinned_fingerprints_sha256 || []).join(", "); } async function deleteRoute(id) { if (!confirm("Route '" + id + "' wirklich löschen?")) return; try { Loading @@ -106,11 +130,22 @@ const ProxyPlusAdmin = (() => { for (const r of relays) { const tr = document.createElement("tr"); tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.smarthost}</td><td>${r.smarthost_pool}</td><td>${r.owner_group_id}</td><td></td>`; addEditButton(tr, () => editRelay(r)); addDeleteButton(tr, () => deleteRelay(r.id)); body.appendChild(tr); } } function editRelay(r) { showNewRelayForm(); document.getElementById("relay-id").value = r.id; document.getElementById("relay-id").readOnly = true; document.getElementById("relay-domain").value = r.domain; document.getElementById("relay-smarthost").value = r.smarthost; document.getElementById("relay-smarthost-pool").value = r.smarthost_pool; document.getElementById("relay-group").value = r.owner_group_id; } async function deleteRelay(id) { if (!confirm("Relay '" + id + "' wirklich löschen?")) return; try { Loading @@ -131,11 +166,23 @@ const ProxyPlusAdmin = (() => { .join(", "); const tr = document.createElement("tr"); tr.innerHTML = `<td>${p.id}</td><td>${p.kind}</td><td>${p.algorithm}</td><td>${targets}</td><td></td>`; addEditButton(tr, () => editPool(p)); addDeleteButton(tr, () => deletePool(p.id)); body.appendChild(tr); } } function editPool(p) { showNewPoolForm(); document.getElementById("pool-id").value = p.id; document.getElementById("pool-id").readOnly = true; document.getElementById("pool-kind").value = p.kind; document.getElementById("pool-algorithm").value = p.algorithm; document.getElementById("pool-targets").value = (p.targets || []) .map((t) => `${t.address}:${t.weight}`) .join(", "); } async function deletePool(id) { if (!confirm("Pool '" + id + "' wirklich löschen?")) return; try { Loading @@ -153,11 +200,38 @@ const ProxyPlusAdmin = (() => { for (const p of profiles) { const tr = document.createElement("tr"); tr.innerHTML = `<td>${p.profile}</td><td>${(p.domains || []).join(", ")}</td><td>${p.acme_enabled ? "ja" : "nein"}</td><td>${p.self_signed ? "ja" : "nein"}</td><td>${p.has_manual_cert ? "ja" : "nein"}</td><td>${p.acme_email}</td><td>${p.ca_plugin}</td><td></td>`; addEditButton(tr, () => editTlsProfile(p)); addDeleteButton(tr, () => deleteTlsProfile(p.profile)); body.appendChild(tr); } } function editTlsProfile(p) { showNewTlsProfileForm(); document.getElementById("tls-profile-id").value = p.profile; document.getElementById("tls-profile-id").readOnly = true; document.getElementById("tls-profile-domains").value = (p.domains || []).join(", "); document.getElementById("tls-profile-acme").checked = p.acme_enabled; document.getElementById("tls-profile-email").value = p.acme_email || ""; document.getElementById("tls-profile-directory").value = p.acme_directory_url || ""; document.getElementById("tls-profile-ca-plugin").value = p.ca_plugin || ""; document.getElementById("tls-profile-selfsigned").checked = p.self_signed; // GET never returns cert/key PEM (it's private key material) and POST fully // replaces the record, so leaving these blank on save would silently erase // an existing manual certificate -- warn rather than let that happen quietly. const certField = document.getElementById("tls-profile-cert-pem"); const keyField = document.getElementById("tls-profile-key-pem"); certField.value = ""; keyField.value = ""; const hint = p.has_manual_cert ? "Vorhandenes Zertifikat -- leer lassen entfernt es beim Speichern! Zum Behalten erneut einfuegen." : "Eigenes Zertifikat (PEM) -- nur im Cluster-Modus, sonst Datei direkt unter <data>/tls/<profil>.crt ablegen"; certField.placeholder = hint; keyField.placeholder = p.has_manual_cert ? "Vorhandener Schluessel -- leer lassen entfernt ihn beim Speichern! Zum Behalten erneut einfuegen." : "Eigener privater Schluessel (PEM) -- wird vor der Speicherung mit dem Cluster-Schluessel versiegelt"; } async function deleteTlsProfile(profile) { if (!confirm("TLS-Profil '" + profile + "' wirklich löschen?")) return; try { Loading @@ -175,11 +249,21 @@ const ProxyPlusAdmin = (() => { for (const k of keys) { const tr = document.createElement("tr"); tr.innerHTML = `<td>${k.domain}</td><td>${k.selector}</td><td>${k.key_path}</td><td>${k.enabled ? "ja" : "nein"}</td><td></td>`; addEditButton(tr, () => editDkimKey(k)); addDeleteButton(tr, () => deleteDkimKey(k.domain)); body.appendChild(tr); } } function editDkimKey(k) { showNewDkimKeyForm(); document.getElementById("dkim-domain").value = k.domain; document.getElementById("dkim-domain").readOnly = true; document.getElementById("dkim-selector").value = k.selector; document.getElementById("dkim-keypath").value = k.key_path; document.getElementById("dkim-enabled").checked = k.enabled; } async function deleteDkimKey(domain) { if (!confirm("DKIM-Key fuer '" + domain + "' wirklich löschen?")) return; try { Loading @@ -202,6 +286,7 @@ const ProxyPlusAdmin = (() => { } function showNewRouteForm() { document.getElementById("route-id").readOnly = false; document.getElementById("new-route-form").hidden = false; } Loading Loading @@ -229,6 +314,7 @@ const ProxyPlusAdmin = (() => { } function showNewRelayForm() { document.getElementById("relay-id").readOnly = false; document.getElementById("new-relay-form").hidden = false; } Loading @@ -249,6 +335,11 @@ const ProxyPlusAdmin = (() => { } function showNewTlsProfileForm() { document.getElementById("tls-profile-id").readOnly = false; document.getElementById("tls-profile-cert-pem").placeholder = "Eigenes Zertifikat (PEM) -- nur im Cluster-Modus, sonst Datei direkt unter <data>/tls/<profil>.crt ablegen"; document.getElementById("tls-profile-key-pem").placeholder = "Eigener privater Schluessel (PEM) -- wird vor der Speicherung mit dem Cluster-Schluessel versiegelt"; document.getElementById("new-tls-profile-form").hidden = false; } Loading Loading @@ -277,6 +368,7 @@ const ProxyPlusAdmin = (() => { } function showNewDkimKeyForm() { document.getElementById("dkim-domain").readOnly = false; document.getElementById("new-dkim-key-form").hidden = false; } Loading @@ -296,6 +388,7 @@ const ProxyPlusAdmin = (() => { } function showNewPoolForm() { document.getElementById("pool-id").readOnly = false; document.getElementById("new-pool-form").hidden = false; } Loading