Loading admin/static/app.js +15 −0 Changes for admin/static/app.js: 15 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -108,6 +108,8 @@ const ProxyPlusAdmin = (() => { } async function createRoute() { document.getElementById("route-error").textContent = ""; try { await api("POST", "/api/v1/http/routes", { id: document.getElementById("route-id").value, path_prefix: document.getElementById("route-path").value, Loading @@ -116,6 +118,9 @@ const ProxyPlusAdmin = (() => { required_permission: document.getElementById("route-permission").value, }); await refreshRoutes(); } catch (e) { document.getElementById("route-error").textContent = e.message; } } function showNewRelayForm() { Loading @@ -123,6 +128,8 @@ const ProxyPlusAdmin = (() => { } async function createRelay() { document.getElementById("relay-error").textContent = ""; try { await api("POST", "/api/v1/smtp/relays", { id: document.getElementById("relay-id").value, domain: document.getElementById("relay-domain").value || "*", Loading @@ -131,6 +138,9 @@ const ProxyPlusAdmin = (() => { owner_group_id: document.getElementById("relay-group").value, }); await refreshRelays(); } catch (e) { document.getElementById("relay-error").textContent = e.message; } } function showNewPoolForm() { Loading @@ -155,6 +165,8 @@ const ProxyPlusAdmin = (() => { } async function createPool() { document.getElementById("pool-error").textContent = ""; try { await api("POST", "/api/v1/pools", { id: document.getElementById("pool-id").value, kind: document.getElementById("pool-kind").value, Loading @@ -162,6 +174,9 @@ const ProxyPlusAdmin = (() => { targets: parseTargets(document.getElementById("pool-targets").value), }); await refreshPools(); } catch (e) { document.getElementById("pool-error").textContent = e.message; } } return { Loading admin/static/index.html +7 −4 Changes for admin/static/index.html: 7 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -29,10 +29,11 @@ <div id="new-route-form" hidden> <input id="route-id" placeholder="ID"> <input id="route-path" placeholder="Pfad-Prefix, z.B. /app"> <input id="route-upstream" placeholder="Upstream (direkt), z.B. http://10.0.0.5:8080"> <input id="route-upstream-pool" placeholder="oder Pool-ID (statt Upstream)"> <input id="route-upstream" placeholder="Upstream (direkt), genau eine URL, z.B. http://10.0.0.5:8080"> <input id="route-upstream-pool" placeholder="oder Pool-ID fuer mehrere Ziele (statt Upstream)"> <input id="route-permission" placeholder="Berechtigung, z.B. http.proxy.use"> <button onclick="ProxyPlusAdmin.createRoute()">Anlegen</button> <div id="route-error" class="error"></div> </div> <h2>SMTP-Relays</h2> Loading @@ -41,10 +42,11 @@ <div id="new-relay-form" hidden> <input id="relay-id" placeholder="ID"> <input id="relay-domain" placeholder="Domain, * fuer alle"> <input id="relay-smarthost" placeholder="Smarthost (direkt), z.B. mail.example.com:587"> <input id="relay-smarthost-pool" placeholder="oder Pool-ID (statt Smarthost)"> <input id="relay-smarthost" placeholder="Smarthost (direkt), genau ein Ziel, z.B. mail.example.com:587"> <input id="relay-smarthost-pool" placeholder="oder Pool-ID fuer mehrere Ziele (statt Smarthost)"> <input id="relay-group" placeholder="Owner-Gruppen-UUID (optional)"> <button onclick="ProxyPlusAdmin.createRelay()">Anlegen</button> <div id="relay-error" class="error"></div> </div> <h2>Load-Balancer-Pools</h2> Loading @@ -62,6 +64,7 @@ </select> <input id="pool-targets" placeholder="Ziele: adresse:gewicht, ... z.B. http://10.0.0.1:8080:1, http://10.0.0.2:8080:1"> <button onclick="ProxyPlusAdmin.createPool()">Anlegen</button> <div id="pool-error" class="error"></div> </div> <button onclick="ProxyPlusAdmin.logout()">Logout</button> Loading src/api/JsonApiServer.cpp +16 −0 Changes for src/api/JsonApiServer.cpp: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -70,6 +70,14 @@ store::LbAlgorithm lbAlgorithmFromString(const std::string &s) { return store::LbAlgorithm::RoundRobin; } // A direct upstream/smarthost field must name exactly one backend. Nothing downstream // validates this: libhttppp::HttpUrl silently stops parsing at the first stray character // after the port and defaults the path to "/", so e.g. "https://a:443,https://b:443" is // accepted without error and quietly turns into a request to "https://a:443/" -- the real // path and every backend but the first are dropped with no diagnostic anywhere. Reject the // comma up front so the mistake (meant for a pool's targets field) surfaces immediately. bool looksLikeSingleTarget(const std::string &s) { return s.find(',') == std::string::npos; } } // namespace JsonApiController::JsonApiController(std::vector<netplus::socket *> serverSockets, Loading Loading @@ -209,6 +217,10 @@ void JsonApiController::registerRoutes() { if (rec.upstream.empty() && rec.upstreamPool.empty()) { return jsonError("either upstream or upstream_pool is required"); } if (!looksLikeSingleTarget(rec.upstream)) { return jsonError("upstream must name exactly one backend URL; " "use upstream_pool for more than one target"); } _configStore.putHttpRoute(rec); JsonGuard resp(json_object_new_object()); json_object_object_add(resp, "status", json_object_new_string("created")); Loading Loading @@ -248,6 +260,10 @@ void JsonApiController::registerRoutes() { if (rec.smarthost.empty() && rec.smarthostPool.empty()) { return jsonError("either smarthost or smarthost_pool is required"); } if (!looksLikeSingleTarget(rec.smarthost)) { return jsonError("smarthost must name exactly one backend host:port; " "use smarthost_pool for more than one target"); } _configStore.putSmtpRelayRule(rec); JsonGuard resp(json_object_new_object()); json_object_object_add(resp, "status", json_object_new_string("created")); Loading Loading
admin/static/app.js +15 −0 Changes for admin/static/app.js: 15 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -108,6 +108,8 @@ const ProxyPlusAdmin = (() => { } async function createRoute() { document.getElementById("route-error").textContent = ""; try { await api("POST", "/api/v1/http/routes", { id: document.getElementById("route-id").value, path_prefix: document.getElementById("route-path").value, Loading @@ -116,6 +118,9 @@ const ProxyPlusAdmin = (() => { required_permission: document.getElementById("route-permission").value, }); await refreshRoutes(); } catch (e) { document.getElementById("route-error").textContent = e.message; } } function showNewRelayForm() { Loading @@ -123,6 +128,8 @@ const ProxyPlusAdmin = (() => { } async function createRelay() { document.getElementById("relay-error").textContent = ""; try { await api("POST", "/api/v1/smtp/relays", { id: document.getElementById("relay-id").value, domain: document.getElementById("relay-domain").value || "*", Loading @@ -131,6 +138,9 @@ const ProxyPlusAdmin = (() => { owner_group_id: document.getElementById("relay-group").value, }); await refreshRelays(); } catch (e) { document.getElementById("relay-error").textContent = e.message; } } function showNewPoolForm() { Loading @@ -155,6 +165,8 @@ const ProxyPlusAdmin = (() => { } async function createPool() { document.getElementById("pool-error").textContent = ""; try { await api("POST", "/api/v1/pools", { id: document.getElementById("pool-id").value, kind: document.getElementById("pool-kind").value, Loading @@ -162,6 +174,9 @@ const ProxyPlusAdmin = (() => { targets: parseTargets(document.getElementById("pool-targets").value), }); await refreshPools(); } catch (e) { document.getElementById("pool-error").textContent = e.message; } } return { Loading
admin/static/index.html +7 −4 Changes for admin/static/index.html: 7 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -29,10 +29,11 @@ <div id="new-route-form" hidden> <input id="route-id" placeholder="ID"> <input id="route-path" placeholder="Pfad-Prefix, z.B. /app"> <input id="route-upstream" placeholder="Upstream (direkt), z.B. http://10.0.0.5:8080"> <input id="route-upstream-pool" placeholder="oder Pool-ID (statt Upstream)"> <input id="route-upstream" placeholder="Upstream (direkt), genau eine URL, z.B. http://10.0.0.5:8080"> <input id="route-upstream-pool" placeholder="oder Pool-ID fuer mehrere Ziele (statt Upstream)"> <input id="route-permission" placeholder="Berechtigung, z.B. http.proxy.use"> <button onclick="ProxyPlusAdmin.createRoute()">Anlegen</button> <div id="route-error" class="error"></div> </div> <h2>SMTP-Relays</h2> Loading @@ -41,10 +42,11 @@ <div id="new-relay-form" hidden> <input id="relay-id" placeholder="ID"> <input id="relay-domain" placeholder="Domain, * fuer alle"> <input id="relay-smarthost" placeholder="Smarthost (direkt), z.B. mail.example.com:587"> <input id="relay-smarthost-pool" placeholder="oder Pool-ID (statt Smarthost)"> <input id="relay-smarthost" placeholder="Smarthost (direkt), genau ein Ziel, z.B. mail.example.com:587"> <input id="relay-smarthost-pool" placeholder="oder Pool-ID fuer mehrere Ziele (statt Smarthost)"> <input id="relay-group" placeholder="Owner-Gruppen-UUID (optional)"> <button onclick="ProxyPlusAdmin.createRelay()">Anlegen</button> <div id="relay-error" class="error"></div> </div> <h2>Load-Balancer-Pools</h2> Loading @@ -62,6 +64,7 @@ </select> <input id="pool-targets" placeholder="Ziele: adresse:gewicht, ... z.B. http://10.0.0.1:8080:1, http://10.0.0.2:8080:1"> <button onclick="ProxyPlusAdmin.createPool()">Anlegen</button> <div id="pool-error" class="error"></div> </div> <button onclick="ProxyPlusAdmin.logout()">Logout</button> Loading
src/api/JsonApiServer.cpp +16 −0 Changes for src/api/JsonApiServer.cpp: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -70,6 +70,14 @@ store::LbAlgorithm lbAlgorithmFromString(const std::string &s) { return store::LbAlgorithm::RoundRobin; } // A direct upstream/smarthost field must name exactly one backend. Nothing downstream // validates this: libhttppp::HttpUrl silently stops parsing at the first stray character // after the port and defaults the path to "/", so e.g. "https://a:443,https://b:443" is // accepted without error and quietly turns into a request to "https://a:443/" -- the real // path and every backend but the first are dropped with no diagnostic anywhere. Reject the // comma up front so the mistake (meant for a pool's targets field) surfaces immediately. bool looksLikeSingleTarget(const std::string &s) { return s.find(',') == std::string::npos; } } // namespace JsonApiController::JsonApiController(std::vector<netplus::socket *> serverSockets, Loading Loading @@ -209,6 +217,10 @@ void JsonApiController::registerRoutes() { if (rec.upstream.empty() && rec.upstreamPool.empty()) { return jsonError("either upstream or upstream_pool is required"); } if (!looksLikeSingleTarget(rec.upstream)) { return jsonError("upstream must name exactly one backend URL; " "use upstream_pool for more than one target"); } _configStore.putHttpRoute(rec); JsonGuard resp(json_object_new_object()); json_object_object_add(resp, "status", json_object_new_string("created")); Loading Loading @@ -248,6 +260,10 @@ void JsonApiController::registerRoutes() { if (rec.smarthost.empty() && rec.smarthostPool.empty()) { return jsonError("either smarthost or smarthost_pool is required"); } if (!looksLikeSingleTarget(rec.smarthost)) { return jsonError("smarthost must name exactly one backend host:port; " "use smarthost_pool for more than one target"); } _configStore.putSmtpRelayRule(rec); JsonGuard resp(json_object_new_object()); json_object_object_add(resp, "status", json_object_new_string("created")); Loading