www/caddy: Implement reusable grid template (#4454)

See: https://github.com/opnsense/core/commit/a7a99fcdfe972dacc1a6beada7607e73a1689d05
This commit is contained in:
Monviech
2025-01-10 09:30:22 +01:00
committed by GitHub
parent 04fb480492
commit dbe43dc186
11 changed files with 243 additions and 268 deletions
@@ -1,7 +1,7 @@
<?php
/**
* Copyright (C) 2023-2024 Cedrik Pischem
* Copyright (C) 2023-2025 Cedrik Pischem
*
* All rights reserved.
*
@@ -37,7 +37,11 @@ class Layer4Controller extends IndexController
public function indexAction()
{
$this->view->pick('OPNsense/Caddy/layer4');
$this->view->formDialogLayer4 = $this->getForm("dialogLayer4");
$this->view->formDialogLayer4Openvpn = $this->getForm("dialogLayer4Openvpn");
$this->view->formDialogLayer4 = $this->getForm('dialogLayer4');
$this->view->formGridLayer4 = $this->getFormGrid('dialogLayer4', null, 'ConfChangeMessage');
$this->view->formDialogLayer4Openvpn = $this->getForm('dialogLayer4Openvpn');
$this->view->formGridLayer4Openvpn = $this->getFormGrid('dialogLayer4Openvpn', null, 'ConfChangeMessage');
}
}
@@ -1,8 +1,7 @@
<?php
/**
* Copyright (C) 2023-2024 Cedrik Pischem
* Copyright (C) 2015 Deciso B.V.
* Copyright (C) 2023-2025 Cedrik Pischem
*
* All rights reserved.
*
@@ -38,11 +37,23 @@ class ReverseProxyController extends IndexController
public function indexAction()
{
$this->view->pick('OPNsense/Caddy/reverse_proxy');
$this->view->formDialogReverseProxy = $this->getForm("dialogReverseProxy");
$this->view->formGridReverseProxy = $this->getFormGrid('dialogReverseProxy', null, 'ConfChangeMessage');
$this->view->formDialogSubdomain = $this->getForm("dialogSubdomain");
$this->view->formGridSubdomain = $this->getFormGrid('dialogSubdomain', null, 'ConfChangeMessage');
$this->view->formDialogHandle = $this->getForm("dialogHandle");
$this->view->formGridHandle = $this->getFormGrid('dialogHandle', null, 'ConfChangeMessage');
$this->view->formDialogAccessList = $this->getForm("dialogAccessList");
$this->view->formGridAccessList = $this->getFormGrid('dialogAccessList', null, 'ConfChangeMessage');
$this->view->formDialogBasicAuth = $this->getForm("dialogBasicAuth");
$this->view->formGridBasicAuth = $this->getFormGrid('dialogBasicAuth', null, 'ConfChangeMessage');
$this->view->formDialogHeader = $this->getForm("dialogHeader");
$this->view->formGridHeader = $this->getFormGrid('dialogHeader', null, 'ConfChangeMessage');
}
}
@@ -18,6 +18,10 @@
<label>Invert List</label>
<type>checkbox</type>
<help><![CDATA[If checked, the access list logic will be inverted (i.e., the listed IPs will be blocked instead of allowed).]]></help>
<grid_view>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<id>accesslist.HttpResponseCode</id>
@@ -26,6 +30,9 @@
<hint>abort</hint>
<help><![CDATA[Set a custom HTTP response code that should be returned to the requesting client when the access list does not match. If empty, the connection is aborted with no response.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>accesslist.HttpResponseMessage</id>
@@ -33,6 +40,9 @@
<type>text</type>
<help><![CDATA[Set a custom HTTP response message in addition to the HTTP response code. If empty, the default HTTP response message for the chosen HTTP response code will be used.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>accesslist.description</id>
@@ -4,6 +4,11 @@
<label>Enabled</label>
<type>checkbox</type>
<help><![CDATA[Enable this handler.]]></help>
<grid_view>
<width>6em</width>
<type>boolean</type>
<formatter>rowtoggle</formatter>
</grid_view>
</field>
<field>
<id>handle.description</id>
@@ -38,6 +43,9 @@
<type>dropdown</type>
<help><![CDATA[Choose a handling type: "handle" (default) will keep the path in all requests. "handle_path" will strip the path from all requests.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.HandlePath</id>
@@ -46,6 +54,9 @@
<hint>any</hint>
<help><![CDATA[Enter a path to handle. Choose a pattern like "/*" or "/example/*". Leave empty to handle any paths (recommended). Any request matching this pattern will be handled by the chosen directive.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<type>header</type>
@@ -58,6 +69,9 @@
<type>dropdown</type>
<help><![CDATA[Select an Access List to restrict access to this handler. If unset, any local or remote client is allowed access.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.ForwardAuth</id>
@@ -65,6 +79,11 @@
<type>checkbox</type>
<help><![CDATA[Enable or disable Forward Auth. Requires an "Auth Provider" in "General Settings". Headers are set automatically to the standard of the chosen provider. Enabling this option will additionally generate the forward_auth directive in front of the reverse_proxy directive inside the scope of this handler.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<type>header</type>
@@ -87,6 +106,9 @@
<style>style_reverse_proxy</style>
<help><![CDATA[The default versions are highly recommended. Choose a HTTP version for the upstream destination. HTTP/3 (HTTP over QUIC) requires HTTPS, and only establishes connections to webservers that also support HTTP/3.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.header</id>
@@ -97,6 +119,9 @@
<style>style_reverse_proxy selectpicker</style>
<help><![CDATA[Select one or multiple headers. Caddy sets "X-Forwarded-For", "X-Forwarded-Proto" and "X-Forwarded-Host" by default, adding them here is not needed. Setting a wrong configuration can be a security risk or break functionality.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.HttpKeepalive</id>
@@ -106,6 +131,9 @@
<style>style_reverse_proxy</style>
<help><![CDATA[Leave empty to use default. Keepalive is either 0 (off) or a duration value that specifies how long to keep connections open (timeout) in seconds.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.HttpTls</id>
@@ -133,6 +161,9 @@
<type>text</type>
<help><![CDATA[When directive is "reverse_proxy", enter a path prefix like "/guacamole" that should be prepended to the upstream request. This is useful for destinations that have a virtual directory as their base path. When directive is "redir", add the path the request should be redirected to; leaving it empty will append {uri}.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.HttpTlsInsecureSkipVerify</id>
@@ -140,6 +171,11 @@
<type>checkbox</type>
<style>style_tls style_reverse_proxy</style>
<help><![CDATA[Caddy uses HTTP by default to connect to the Upstream. If the Upstream is only reachable via HTTPS, this option disables the TLS handshake verification. This makes the connection insecure and vulnerable to man-in-the-middle attacks. In private networks the risk is low, though do not use in production if possible. It is advised to either use plain HTTP, or proper TLS handling by using the options in "Trust".]]></help>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<id>handle.HttpTlsTrustedCaCerts</id>
@@ -147,6 +183,9 @@
<type>dropdown</type>
<style>style_tls style_reverse_proxy</style>
<help><![CDATA[Choose a CA or self-signed certificate to trust from "System - Trust - Authorities". Useful if the upstream destination only accepts TLS connections and offers a self signed certificate. Adding that certificate here will allow for the encrypted connection to succeed.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.HttpTlsServerName</id>
@@ -154,6 +193,9 @@
<type>text</type>
<style>style_tls style_reverse_proxy</style>
<help><![CDATA[Enter a hostname or IP address that matches the SAN "Subject Alternative Name" of the offered upstream certificate. This will change the SNI "Server Name Indication" of Caddy. Setting an IP address as "Upstream Domain", enabling "TLS" and selecting a "TLS Trust Pool", would make the SAN of the offered upstream certificate not match with the SNI of Caddy, since it will be an IP address instead of a hostname. Setting the hostname of the certificate here, fixes this issue. Please note that only SAN certificates are supported; CN "Common Name" will not work.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.HttpNtlm</id>
@@ -161,6 +203,11 @@
<type>checkbox</type>
<style>style_tls style_reverse_proxy</style>
<help><![CDATA[Enable or disable NTLM. Needed to reverse proxy an Exchange Server. Warning: NTLM has been deprecated by Microsoft. This option will stay for as long as the optional http.reverse_proxy.transport.http_ntlm module can be compiled without errors.]]></help>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<type>header</type>
@@ -174,6 +221,9 @@
<style>style_reverse_proxy</style>
<help><![CDATA[lb_policy is the name of the load balancing policy, along with any options. For policies that involve hashing, the highest-random-weight (HRW) algorithm is used to ensure that a client or request with the same hash key is mapped to the same upstream, even if the list of upstreams change.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.lb_retries</id>
@@ -183,6 +233,9 @@
<hint>off</hint>
<help><![CDATA[lb_retries is how many times to retry selecting available backends for each request if the next available host is down. If lb_try_duration is also configured, then retries may stop early if the duration is reached. In other words, the retry duration takes precedence over the retry count.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.lb_try_duration</id>
@@ -192,6 +245,9 @@
<hint>0</hint>
<help><![CDATA[lb_try_duration is a duration value that defines how long to try selecting available backends for each request if the next available host is down. Clients will wait for up to this long while the load balancer tries to find an available upstream host. A reasonable starting point might be 5s since the HTTP transport's default dial timeout is 3s, so that should allow for at least one retry if the first selected upstream cannot be reached.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.lb_try_interval</id>
@@ -201,6 +257,9 @@
<hint>250</hint>
<help><![CDATA[lb_try_interval is a duration value that defines how long to wait between selecting the next host from the pool. Only relevant when a request to an upstream host fails. Be aware that setting this to 0 with a non-zero lb_try_duration can cause the CPU to spin if all backends are down and latency is very low.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.PassiveHealthFailDuration</id>
@@ -210,6 +269,9 @@
<hint>off</hint>
<help><![CDATA[fail_duration enables a passive health check when multiple destinations in "Upstream Domain" are set. It is a value that defines how long to remember a failed request. A duration of 1 or more seconds enables passive health checking. A reasonable starting point might be 30s to balance error rates with responsiveness when bringing an unhealthy upstream back online.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.PassiveHealthMaxFails</id>
@@ -219,6 +281,9 @@
<hint>1</hint>
<help><![CDATA[max_fails is the maximum number of failed requests within fail_duration that are needed before considering an upstream to be down.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.PassiveHealthUnhealthyStatus</id>
@@ -228,6 +293,9 @@
<hint>off</hint>
<help><![CDATA[unhealthy_status counts a request as failed if the response comes back with one of these status codes. Can be a 3-digit status code or a status code class ending in xx, for example: 404 or 5xx.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.PassiveHealthUnhealthyLatency</id>
@@ -237,6 +305,9 @@
<hint>off</hint>
<help><![CDATA[unhealthy_latency is a duration value in ms that counts a request as failed if it takes this long to get a response.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>handle.PassiveHealthUnhealthyRequestCount</id>
@@ -246,5 +317,8 @@
<hint>off</hint>
<help><![CDATA[unhealthy_request_count is the permissible number of simultaneous requests to a backend before marking it as down. In other words, if a particular backend is currently handling this many requests, then it is considered "overloaded" and other backends will be preferred instead. This should be a reasonably large number; configuring this means that the proxy will have a limit of unhealthy_request_count × upstreams_count total simultaneous requests, and any requests after that point will result in an error due to no upstreams being available.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
</form>
@@ -16,12 +16,18 @@
<label>Header Value</label>
<type>text</type>
<help><![CDATA[Enter a value for the selected header. One of the most common options is "{upstream_hostport}". It is also possible to use a regular expression to search for a specific value in a header. For example: "^prefix-([A-Za-z0-9]*)$" which uses the regular expression language RE2 included in Go.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>header.HeaderReplace</id>
<label>Header Replace</label>
<type>text</type>
<help><![CDATA[If a regular expression is used to search for a Header Value, the replacement string can be set here. For example: "replaced-$1-suffix" which expands the replacement string, allowing the use of captured values, "$1" being the first capture group.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>header.description</id>
@@ -4,6 +4,11 @@
<label>Enabled</label>
<type>checkbox</type>
<help><![CDATA[Enable this Layer4 route.]]></help>
<grid_view>
<width>6em</width>
<type>boolean</type>
<formatter>rowtoggle</formatter>
</grid_view>
</field>
<field>
<id>layer4.Sequence</id>
@@ -26,6 +31,9 @@
<label>Routing Type</label>
<type>dropdown</type>
<help><![CDATA[Choose either "listener_wrappers" for multiplexing protocols on the default HTTP and HTTPS ports on OSI Layer 7, or "global" for raw TCP/UDP traffic routing on a custom "Local port" on OSI Layer 4 with optional OSI Layer 7 protocol matching.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>layer4.Protocol</id>
@@ -40,6 +48,9 @@
<type>text</type>
<style>style_type</style>
<help><![CDATA[Choose a custom local port to listen on.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<type>header</type>
@@ -65,6 +76,9 @@
<type>dropdown</type>
<style>style_matchers matchers_openvpn</style>
<help><![CDATA[Select the mode matching the OpenVPN Server or Client.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>layer4.FromOpenvpnStaticKey</id>
@@ -74,6 +88,9 @@
<hint>Any</hint>
<size>5</size>
<help><![CDATA[Select a Static Key to match. Multiple Static Keys are only supported for tls-crypt2_client mode.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>layer4.InvertMatchers</id>
@@ -81,6 +98,11 @@
<type>checkbox</type>
<help><![CDATA[Invert the sense of the matcher. E.g., if the protocol is TLS, inverting will match all traffic that is not TLS. When domains have been chosen, these will be equally inverted.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<id>layer4.TerminateTls</id>
@@ -88,6 +110,11 @@
<type>checkbox</type>
<style>style_matchers matchers_domain</style>
<help><![CDATA[Terminate TLS before routing to the upstream. Since this requires a certificate, ensure there is a domain configured in "Reverse Proxy" that matches the SNI of "Domain". The best matching SAN or wildcard certificate will be used automatically for this route.]]></help>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<type>header</type>
@@ -113,6 +140,9 @@
<type>dropdown</type>
<help><![CDATA[Add the HA Proxy Protocol header. Either version 1 or 2 can be chosen. The default is off, since it is only needed when the upstream can use the Proxy Protocol header.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<type>header</type>
@@ -126,6 +156,9 @@
<style>style_reverse_proxy</style>
<help><![CDATA[lb_policy is the name of the load balancing policy, along with any options. For policies that involve hashing, the highest-random-weight (HRW) algorithm is used to ensure that a client or request with the same hash key is mapped to the same upstream, even if the list of upstreams change.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>layer4.PassiveHealthFailDuration</id>
@@ -135,6 +168,9 @@
<hint>off</hint>
<help><![CDATA[fail_duration enables a passive health check when multiple destinations in "Upstream Domain" are set. It is a value that defines how long to remember a failed request. A duration of 1 or more seconds enables passive health checking. A reasonable starting point might be 30s to balance error rates with responsiveness when bringing an unhealthy upstream back online.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>layer4.PassiveHealthMaxFails</id>
@@ -144,6 +180,9 @@
<hint>1</hint>
<help><![CDATA[max_fails is the maximum number of failed requests within fail_duration that are needed before considering an upstream to be down.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<type>header</type>
@@ -156,5 +195,8 @@
<style>tokenize</style>
<allownew>true</allownew>
<help><![CDATA[Enter one or multiple IP addresses and networks. Leaving this empty will allow any remote client to connect. If populated and the remote IP address of a client matches, the connection to the "Upstream Domain" is allowed, otherwise the connection is dropped. Due to restrictions with this matcher, this list can not be inverted.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
</form>
@@ -9,5 +9,8 @@
<label>Static Key</label>
<type>textbox</type>
<help>Paste an OpenVPN Static key.</help>
<grid_view>
<ignore>true</ignore>
</grid_view>
</field>
</fields>
@@ -4,6 +4,11 @@
<label>Enabled</label>
<type>checkbox</type>
<help><![CDATA[Enable this domain.]]></help>
<grid_view>
<width>6em</width>
<type>boolean</type>
<formatter>rowtoggle</formatter>
</grid_view>
</field>
<field>
<id>reverse.description</id>
@@ -39,6 +44,9 @@
<type>dropdown</type>
<style>style_tls</style>
<help><![CDATA[Choose ACME to get automatic certificates with the built in ACME client; no additional plugin required. The "HTTP-01", "TLS-ALPN-01" or "DNS-01" challenge will be used to get automatic "Let's Encrypt" or "ZeroSSL" certificates. Alternatively, choose a custom certificate from "System - Trust - Certificates" for this domain. Make sure the full chain has been imported.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>reverse.AcmePassthrough</id>
@@ -46,6 +54,9 @@
<type>text</type>
<help><![CDATA[Enter a domain name or IP address. The HTTP-01 challenge will be redirected to that destination. This enables an ACME Client behind Caddy to serve "/.well-known/acme-challenge/" on port 80. Caddy will reverse proxy the HTTP-01 challenge for this domain, and will still issue a certificate using the TLS-ALPN-01 challenge or DNS-01 challenge for itself. This option can be used for High Availability when using Caddy with a master and backup OPNsense.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>reverse.DnsChallenge</id>
@@ -53,12 +64,22 @@
<type>checkbox</type>
<style>style_tls</style>
<help><![CDATA[Enable the DNS-01 Challenge for this domain. Requires a "DNS Provider" in "General Settings". This is only needed for wildcard domains, or when the default challenges can not be used due to restrictive firewall policies.]]></help>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<id>reverse.DynDns</id>
<label>Dynamic DNS</label>
<type>checkbox</type>
<help><![CDATA[Enable or disable Dynamic DNS. Requires a "DNS Provider" in "General Settings". The DNS records of this domain will be automatically updated. A wildcard domain will create a "*.example.com" record. A base domain will create a "example.com" or "opn.example.com" record. Some providers need subdomains to set records for domains like "opn.example.com"; in that case use the checkbox in the subdomains tab.]]></help>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<type>header</type>
@@ -69,6 +90,9 @@
<label>Access List</label>
<type>dropdown</type>
<help><![CDATA[Select an Access List to restrict access to this domain. If unset, any local or remote client is allowed access.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>reverse.basicauth</id>
@@ -76,11 +100,19 @@
<type>select_multiple</type>
<size>5</size>
<help><![CDATA[Select Users to restrict access to this domain. Basic Auth matches after Access Lists. If unset, any user is allowed access. When using CrowdSec, authorization failures will result in automatic bans.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>reverse.AccessLog</id>
<label>HTTP Access Log</label>
<type>checkbox</type>
<help><![CDATA[Enable HTTP request logging for this domain and its subdomains. This option is mostly used for troubleshooting, audits and CrowdSec. It will log every single request.]]></help>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
</form>
@@ -4,6 +4,11 @@
<label>Enabled</label>
<type>checkbox</type>
<help><![CDATA[Enable this subdomain.]]></help>
<grid_view>
<width>6em</width>
<type>boolean</type>
<formatter>rowtoggle</formatter>
</grid_view>
</field>
<field>
<id>subdomain.description</id>
@@ -33,6 +38,11 @@
<label>Dynamic DNS</label>
<type>checkbox</type>
<help><![CDATA[Enable or disable Dynamic DNS. Requires a "DNS Provider" in "General Settings". The DNS records of this subdomain will be automatically updated.]]></help>
<grid_view>
<visible>false</visible>
<type>boolean</type>
<formatter>boolean</formatter>
</grid_view>
</field>
<field>
<id>subdomain.AcmePassthrough</id>
@@ -40,6 +50,9 @@
<type>text</type>
<help><![CDATA[Enter a domain name or IP address. The HTTP-01 challenge will be redirected to that destination. This enables an ACME Client behind Caddy to serve "/.well-known/acme-challenge/" on port 80. Caddy will reverse proxy the HTTP-01 challenge for this subdomain.]]></help>
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<type>header</type>
@@ -50,6 +63,9 @@
<label>Access List</label>
<type>dropdown</type>
<help><![CDATA[Select an Access List to restrict access to this subdomain. If unset, any local or remote client is allowed access.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>subdomain.basicauth</id>
@@ -57,5 +73,8 @@
<type>select_multiple</type>
<size>5</size>
<help><![CDATA[Select Users to restrict access to this subdomain. Basic Auth matches after Access Lists. If unset, any user is allowed access.]]></help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
</form>
@@ -1,5 +1,5 @@
{#
# Copyright (c) 2024 Cedrik Pischem
# Copyright (c) 2024-2025 Cedrik Pischem
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -27,7 +27,7 @@
<script>
$(document).ready(function() {
// Bootgrid Setup
$("#Layer4Grid").UIBootgrid({
$("#{{formGridLayer4['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchLayer4/',
get:'/api/caddy/ReverseProxy/getLayer4/',
set:'/api/caddy/ReverseProxy/setLayer4/',
@@ -36,7 +36,7 @@
toggle:'/api/caddy/ReverseProxy/toggleLayer4/',
});
$("#Layer4OpenvpnGrid").UIBootgrid({
$("#{{formGridLayer4Openvpn['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchLayer4Openvpn/',
get:'/api/caddy/ReverseProxy/getLayer4Openvpn/',
set:'/api/caddy/ReverseProxy/setLayer4Openvpn/',
@@ -137,44 +137,7 @@
<div style="padding-left: 16px;">
<h1 class="custom-header">{{ lang._('Layer4 Routes') }}</h1>
<div style="display: block;">
<table id="Layer4Grid" class="table table-condensed table-hover table-striped" data-editDialog="DialogLayer4" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="boolean" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="Sequence" data-type="string">{{ lang._('Sequence') }}</th>
<th data-column-id="Type" data-type="string" data-visible="false">{{ lang._('Routing Type') }}</th>
<th data-column-id="Protocol" data-type="string">{{ lang._('Protocol') }}</th>
<th data-column-id="FromPort" data-type="string" data-visible="false">{{ lang._('Local Port') }}</th>
<th data-column-id="Matchers" data-type="string">{{ lang._('Matchers') }}</th>
<th data-column-id="InvertMatchers" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('Invert Matchers') }}</th>
<th data-column-id="TerminateTls" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('Terminate TLS') }}</th>
<th data-column-id="FromDomain" data-type="string">{{ lang._('Domain') }}</th>
<th data-column-id="FromOpenvpnModes" data-type="string" data-visible="false">{{ lang._('OpenVPN Modes') }}</th>
<th data-column-id="FromOpenvpnStaticKey" data-type="string" data-visible="false">{{ lang._('OpenVPN Static Key') }}</th>
<th data-column-id="ToDomain" data-type="string">{{ lang._('Upstream Domain') }}</th>
<th data-column-id="ToPort" data-type="string">{{ lang._('Upstream Port') }}</th>
<th data-column-id="RemoteIp" data-type="string" data-visible="false">{{ lang._('Remote IP') }}</th>
<th data-column-id="lb_policy" data-type="string" data-visible="false">{{ lang._('Load Balance Policy') }}</th>
<th data-column-id="PassiveHealthFailDuration" data-type="string" data-visible="false">{{ lang._('Passive Health Fail Duration') }}</th>
<th data-column-id="PassiveHealthMaxFails" data-type="string" data-visible="false">{{ lang._('Passive Health Max Fails') }}</th>
<th data-column-id="ProxyProtocol" data-type="string" data-visible="false">{{ lang._('Proxy Protocol') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addLayer4Btn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
{{ partial('layout_partials/base_bootgrid_table', formGridLayer4)}}
</div>
</div>
</div>
@@ -185,26 +148,7 @@
<!-- OpenVPN Matcher -->
<h1 class="custom-header">{{ lang._('OpenVPN Static Keys') }}</h1>
<div style="display: block;">
<table id="Layer4OpenvpnGrid" class="table table-condensed table-hover table-striped" data-editDialog="DialogLayer4Openvpn" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addLayer4OpenvpnBtn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
{{ partial('layout_partials/base_bootgrid_table', formGridLayer4Openvpn)}}
</div>
</div>
</div>
@@ -226,12 +170,12 @@
<!-- Message Area for error/success messages -->
<div id="messageArea" class="alert alert-info" style="display: none;"></div>
<!-- Message Area to hint user to apply changes when data is changed in bootgrids -->
<div id="ConfigurationChangeMessage" class="alert alert-info" style="display: none;">
<div id="ConfChangeMessage" class="alert alert-info" style="display: none;">
{{ lang._('Please do not forget to apply the configuration.') }}
</div>
</div>
</div>
</section>
{{ partial("layout_partials/base_dialog",['fields':formDialogLayer4,'id':'DialogLayer4','label':lang._('Edit Layer4 Route')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogLayer4Openvpn,'id':'DialogLayer4Openvpn','label':lang._('Edit OpenVPN Static Key')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogLayer4,'id':formGridLayer4['edit_dialog_id'],'label':lang._('Edit Layer4 Route')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogLayer4Openvpn,'id':formGridLayer4Openvpn['edit_dialog_id'],'label':lang._('Edit OpenVPN Static Key')])}}
@@ -1,5 +1,5 @@
{#
# Copyright (c) 2023-2024 Cedrik Pischem
# Copyright (c) 2023-2025 Cedrik Pischem
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -27,7 +27,7 @@
<script>
$(document).ready(function() {
// Bootgrid Setup
$("#reverseProxyGrid").UIBootgrid({
$("#{{formGridReverseProxy['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchReverseProxy/',
get:'/api/caddy/ReverseProxy/getReverseProxy/',
set:'/api/caddy/ReverseProxy/setReverseProxy/',
@@ -40,7 +40,7 @@
}
});
$("#reverseSubdomainGrid").UIBootgrid({
$("#{{formGridSubdomain['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchSubdomain/',
get:'/api/caddy/ReverseProxy/getSubdomain/',
set:'/api/caddy/ReverseProxy/setSubdomain/',
@@ -53,7 +53,7 @@
}
});
$("#reverseHandleGrid").UIBootgrid({
$("#{{formGridHandle['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchHandle/',
get:'/api/caddy/ReverseProxy/getHandle/',
set:'/api/caddy/ReverseProxy/setHandle/',
@@ -65,7 +65,7 @@
}
});
$("#accessListGrid").UIBootgrid({
$("#{{formGridAccessList['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchAccessList/',
get:'/api/caddy/ReverseProxy/getAccessList/',
set:'/api/caddy/ReverseProxy/setAccessList/',
@@ -76,7 +76,7 @@
}
});
$("#basicAuthGrid").UIBootgrid({
$("#{{formGridBasicAuth['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchBasicAuth/',
get:'/api/caddy/ReverseProxy/getBasicAuth/',
set:'/api/caddy/ReverseProxy/setBasicAuth/',
@@ -87,7 +87,7 @@
}
});
$("#reverseHeaderGrid").UIBootgrid({
$("#{{formGridHeader['table_id']}}").UIBootgrid({
search:'/api/caddy/ReverseProxy/searchHeader/',
get:'/api/caddy/ReverseProxy/getHeader/',
set:'/api/caddy/ReverseProxy/setHeader/',
@@ -138,11 +138,11 @@
select.append($('<option>').val(item.id).text(item.domainPort));
});
} else {
select.html('<option value="">{{ lang._('Failed to load data') }}</option>');
select.html(`<option value="">{{ lang._('Failed to load data') }}</option>`);
}
select.selectpicker('refresh');
}).fail(function() {
$('#reverseFilter').html('<option value="">{{ lang._('Failed to load data') }}</option>').selectpicker('refresh');
$('#reverseFilter').html(`<option value="">{{ lang._('Failed to load data') }}</option>`).selectpicker('refresh');
});
}
@@ -164,9 +164,9 @@
}
function reloadGrids() {
$("#reverseProxyGrid").bootgrid("reload");
$("#reverseSubdomainGrid").bootgrid("reload");
$("#reverseHandleGrid").bootgrid("reload");
$("#{{formGridReverseProxy['table_id']}}").bootgrid("reload");
$("#{{formGridSubdomain['table_id']}}").bootgrid("reload");
$("#{{formGridHandle['table_id']}}").bootgrid("reload");
}
// Hide message area when starting new actions
@@ -345,72 +345,16 @@
<div style="padding-left: 16px;">
<!-- Reverse Proxy -->
<h1 class="custom-header">{{ lang._('Domains') }}</h1>
<div style="display: block;"> <!-- Common container -->
<table id="reverseProxyGrid" class="table table-condensed table-hover table-striped" data-editDialog="DialogReverseProxy" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="boolean" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="DisableTls" data-type="string">{{ lang._('Protocol') }}</th>
<th data-column-id="FromDomain" data-type="string">{{ lang._('Domain') }}</th>
<th data-column-id="FromPort" data-type="string">{{ lang._('Port') }}</th>
<th data-column-id="accesslist" data-type="string" data-visible="false">{{ lang._('Access List') }}</th>
<th data-column-id="basicauth" data-type="string" data-visible="false">{{ lang._('Basic Auth') }}</th>
<th data-column-id="DnsChallenge" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('DNS-01 Challenge') }}</th>
<th data-column-id="DynDns" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('Dynamic DNS') }}</th>
<th data-column-id="AccessLog" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('HTTP Access Log') }}</th>
<th data-column-id="CustomCertificate" data-type="string" data-visible="false">{{ lang._('Certificate') }}</th>
<th data-column-id="AcmePassthrough" data-type="string" data-visible="false">{{ lang._('HTTP-01 Challenge Redirection') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addReverseProxyBtn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div style="display: block;">
{{ partial('layout_partials/base_bootgrid_table', formGridReverseProxy)}}
</div>
</div>
<!-- Subdomains Tab -->
<div style="padding-left: 16px;">
<h1 class="custom-header">{{ lang._('Subdomains') }}</h1>
<div style="display: block;"> <!-- Common container -->
<table id="reverseSubdomainGrid" class="table table-condensed table-hover table-striped" data-editDialog="DialogSubdomain" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="boolean" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="reverse" data-type="string">{{ lang._('Domain') }}</th>
<th data-column-id="FromDomain" data-type="string">{{ lang._('Subdomain') }}</th>
<th data-column-id="accesslist" data-type="string" data-visible="false">{{ lang._('Access List') }}</th>
<th data-column-id="basicauth" data-type="string" data-visible="false">{{ lang._('Basic Auth') }}</th>
<th data-column-id="DynDns" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('Dynamic DNS') }}</th>
<th data-column-id="AcmePassthrough" data-type="string" data-visible="false">{{ lang._('HTTP-01 Challenge Redirection') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addSubdomainBtn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div style="display: block;">
{{ partial('layout_partials/base_bootgrid_table', formGridSubdomain)}}
</div>
</div>
</div>
@@ -419,55 +363,8 @@
<div id="handlesTab" class="tab-pane fade">
<div style="padding-left: 16px;">
<h1 class="custom-header">{{ lang._('Handlers') }}</h1>
<div style="display: block;"> <!-- Common container -->
<table id="reverseHandleGrid" class="table table-condensed table-hover table-striped" data-editDialog="DialogHandle" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="boolean" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="reverse" data-type="string">{{ lang._('Domain') }}</th>
<th data-column-id="subdomain" data-type="string">{{ lang._('Subdomain') }}</th>
<th data-column-id="HandleType" data-type="string" data-visible="false">{{ lang._('Handler') }}</th>
<th data-column-id="HandlePath" data-type="string" data-visible="false">{{ lang._('Path') }}</th>
<th data-column-id="header" data-type="string" data-visible="false">{{ lang._('HTTP Headers') }}</th>
<th data-column-id="HandleDirective" data-type="string">{{ lang._('Directive') }}</th>
<th data-column-id="HttpTls" data-type="string" data-visible="false">{{ lang._('Protocol') }}</th>
<th data-column-id="ToDomain" data-type="string">{{ lang._('Upstream Domain') }}</th>
<th data-column-id="ToPort" data-type="string">{{ lang._('Upstream Port') }}</th>
<th data-column-id="ToPath" data-type="string" data-visible="false">{{ lang._('Upstream Path') }}</th>
<th data-column-id="ForwardAuth" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('Forward Auth') }}</th>
<th data-column-id="accesslist" data-type="string" data-visible="false">{{ lang._('Access List') }}</th>
<th data-column-id="HttpVersion" data-type="string" data-visible="false">{{ lang._('HTTP Version') }}</th>
<th data-column-id="HttpKeepalive" data-type="string" data-visible="false">{{ lang._('HTTP Keepalive') }}</th>
<th data-column-id="HttpTlsTrustedCaCerts" data-type="string" data-visible="false">{{ lang._('TLS Trust Pool') }}</th>
<th data-column-id="HttpTlsServerName" data-type="string" data-visible="false">{{ lang._('TLS Server Name') }}</th>
<th data-column-id="HttpNtlm" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('NTLM') }}</th>
<th data-column-id="HttpTlsInsecureSkipVerify" data-type="boolean" data-formatter="boolean" data-visible="false">{{ lang._('TLS Insecure Skip Verify') }}</th>
<th data-column-id="lb_policy" data-type="string" data-visible="false">{{ lang._('Load Balance Policy') }}</th>
<th data-column-id="lb_retries" data-type="string" data-visible="false">{{ lang._('Load Balance Retries') }}</th>
<th data-column-id="lb_try_duration" data-type="string" data-visible="false">{{ lang._('Load Balance Try Duration') }}</th>
<th data-column-id="lb_try_interval" data-type="string" data-visible="false">{{ lang._('Load Balance Try Interval') }}</th>
<th data-column-id="PassiveHealthFailDuration" data-type="string" data-visible="false">{{ lang._('Passive Health Fail Duration') }}</th>
<th data-column-id="PassiveHealthMaxFails" data-type="string" data-visible="false">{{ lang._('Passive Health Max Fails') }}</th>
<th data-column-id="PassiveHealthUnhealthyStatus" data-type="string" data-visible="false">{{ lang._('Passive Health Unhealthy Status') }}</th>
<th data-column-id="PassiveHealthUnhealthyLatency" data-type="string" data-visible="false">{{ lang._('Passive Health Unhealthy Latency') }}</th>
<th data-column-id="PassiveHealthUnhealthyRequestCount" data-type="string" data-visible="false">{{ lang._('Passive Health Unhealthy Request Count') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addReverseHandleBtn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div style="display: block;">
{{ partial('layout_partials/base_bootgrid_table', formGridHandle)}}
</div>
</div>
</div>
@@ -478,31 +375,7 @@
<div style="padding-left: 16px;">
<h1 class="custom-header">{{ lang._('Access Lists') }}</h1>
<div style="display: block;">
<table id="accessListGrid" class="table table-condensed table-hover table-striped" data-editDialog="DialogAccessList" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="accesslistName" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="clientIps" data-type="string">{{ lang._('Client IPs') }}</th>
<th data-column-id="accesslistInvert" data-type="boolean" data-formatter="boolean">{{ lang._('Invert') }}</th>
<th data-column-id="HttpResponseCode" data-type="string" data-visible="false">{{ lang._('HTTP Code') }}</th>
<th data-column-id="HttpResponseMessage" data-type="string" data-visible="false">{{ lang._('HTTP Message') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addAccessListBtn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
{{ partial('layout_partials/base_bootgrid_table', formGridAccessList)}}
</div>
</div>
@@ -510,27 +383,7 @@
<div style="padding-left: 16px;">
<h1 class="custom-header">{{ lang._('Basic Auth') }}</h1>
<div style="display: block;">
<table id="basicAuthGrid" class="table table-condensed table-hover table-striped" data-editDialog="DialogBasicAuth" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="basicauthuser" data-type="string">{{ lang._('User') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addBasicAuthBtn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
{{ partial('layout_partials/base_bootgrid_table', formGridBasicAuth)}}
</div>
</div>
</div>
@@ -539,31 +392,8 @@
<div id="headerTab" class="tab-pane fade">
<div style="padding-left: 16px;">
<h1 class="custom-header">{{ lang._('Headers') }}</h1>
<div style="display: block;"> <!-- Common container -->
<table id="reverseHeaderGrid" class="table table-condensed table-hover table-striped" data-editDialog="DialogHeader" data-editAlert="ConfigurationChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="HeaderUpDown" data-type="string">{{ lang._('Header') }}</th>
<th data-column-id="HeaderType" data-type="string">{{ lang._('Header Type') }}</th>
<th data-column-id="HeaderValue" data-type="string" data-visible="false">{{ lang._('Header Value') }}</th>
<th data-column-id="HeaderReplace" data-type="string" data-visible="false">{{ lang._('Header Replace') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="addReverseHeaderBtn" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div style="display: block;">
{{ partial('layout_partials/base_bootgrid_table', formGridHeader)}}
</div>
</div>
</div>
@@ -584,16 +414,16 @@
<!-- Message Area for error/success messages -->
<div id="messageArea" class="alert alert-info" style="display: none;"></div>
<!-- Message Area to hint user to apply changes when data is changed in bootgrids -->
<div id="ConfigurationChangeMessage" class="alert alert-info" style="display: none;">
<div id="ConfChangeMessage" class="alert alert-info" style="display: none;">
{{ lang._('Please do not forget to apply the configuration.') }}
</div>
</div>
</div>
</section>
{{ partial("layout_partials/base_dialog",['fields':formDialogReverseProxy,'id':'DialogReverseProxy','label':lang._('Edit Domain')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogSubdomain,'id':'DialogSubdomain','label':lang._('Edit Subdomain')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogHandle,'id':'DialogHandle','label':lang._('Edit Handler')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogAccessList,'id':'DialogAccessList','label':lang._('Edit Access List')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogBasicAuth,'id':'DialogBasicAuth','label':lang._('Edit Basic Auth')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogHeader,'id':'DialogHeader','label':lang._('Edit Header')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogReverseProxy,'id':formGridReverseProxy['edit_dialog_id'],'label':lang._('Edit Domain')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogSubdomain,'id':formGridSubdomain['edit_dialog_id'],'label':lang._('Edit Subdomain')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogHandle,'id':formGridHandle['edit_dialog_id'],'label':lang._('Edit Handler')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogAccessList,'id':formGridAccessList['edit_dialog_id'],'label':lang._('Edit Access List')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogBasicAuth,'id':formGridBasicAuth['edit_dialog_id'],'label':lang._('Edit Basic Auth')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogHeader,'id':formGridHeader['edit_dialog_id'],'label':lang._('Edit Header')])}}