mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
www/caddy: Refactor general.volt to base_tabs, Update DNS Providers (#4209)
* Refactor general.volt from layout_partials/base_form to layout_partials/base_tabs * www/caddy: Refactor general.volt from layout_partials/base_form to layout_partials/base_tabs * Add Layer4 DNS matcher, Add DNS Provider Hetzner, Change DNS Providers Router53 max_retries to hosted_zone_id, Restructure general.xml to add Advanced Settings Tab. * Small tweak that message area starts at approximately the same spot as the Apply button * Adjust Makefile and pkg-descr * www/caddy: reverse_proxy.volt, adjust style to fit with general.volt. Make add buttons primary. Hide Wizard buttons in Subdomaintab. Print errors with alert-danger. * www/caddy: Remove useless help text in diagnostics.volt * www/caddy: Refactor views for ajaxGet and ajaxCall. Clean up some style. Add Comments to functions. Declare functions first before calling them.
This commit is contained in:
+2
-3
@@ -1,8 +1,7 @@
|
||||
PLUGIN_NAME= caddy
|
||||
PLUGIN_VERSION= 1.6.3
|
||||
PLUGIN_REVISION= 1
|
||||
PLUGIN_VERSION= 1.7.0
|
||||
PLUGIN_DEPENDS= caddy-custom
|
||||
PLUGIN_COMMENT= Easy to configure Reverse Proxy with Automatic HTTPS and Dynamic DNS
|
||||
PLUGIN_COMMENT= Modern Reverse Proxy with Automatic HTTPS, Dynamic DNS and Layer4 Routing
|
||||
PLUGIN_MAINTAINER= cedrik@pischem.com
|
||||
|
||||
.include "../../Mk/plugins.mk"
|
||||
|
||||
+15
-20
@@ -1,33 +1,28 @@
|
||||
Caddy - The Ultimate Server - makes your sites more secure, more reliable, and more scalable than any other solution.
|
||||
By default, Caddy automatically obtains and renews TLS certificates for all your sites.
|
||||
By default, Caddy automatically obtains and renews TLS certificates (Let's Encrypt and ZeroSSL) for all your sites.
|
||||
It's the most advanced HTTPS server in the world.
|
||||
|
||||
Reverse Proxy HTTP, HTTPS, FastCGI, WebSockets, gRPC, FastCGI (usually PHP), and more!
|
||||
* Reverse Proxy HTTP, HTTPS and WebSockets
|
||||
* Route UDP/TCP traffic with the included Layer4 module: https://github.com/mholt/caddy-l4
|
||||
* Dynamic DNS module included: https://github.com/mholt/caddy-dynamicdns
|
||||
* Large selection of DNS Providers available: https://github.com/caddy-dns
|
||||
|
||||
WWW: https://caddyserver.com/
|
||||
|
||||
Main features of this plugin:
|
||||
|
||||
* Easy to configure and reliable! Reverse Proxy any HTTP/HTTPS or WebSocket application in minutes.
|
||||
* Automatic Let's Encrypt and ZeroSSL certificates with HTTP-01 and TLS-ALPN-01 challenge
|
||||
* DNS-01 challenge and Dynamic DNS with supported DNS Providers built right in
|
||||
* Use custom certificates from OPNsense certificate store
|
||||
* Wildcard Domain and Subdomain support
|
||||
* Access Lists to restrict access based on static networks
|
||||
* Basic Auth to restrict access by username and password
|
||||
* Forward Auth with Authelia
|
||||
* Syslog-ng integration and HTTP Access Log
|
||||
* NTLM Transport
|
||||
* Header manipulation
|
||||
* Simple load balancing with passive health check
|
||||
* Widgets for OPNsense Dashboard (24.7 and later)
|
||||
* Layer4 SNI based routing of TCP/UDP
|
||||
|
||||
DOC: https://docs.opnsense.org/manual/how-tos/caddy.html
|
||||
|
||||
Plugin Changelog
|
||||
================
|
||||
|
||||
1.7.0
|
||||
|
||||
* Add: Layer4 protocols: DNS
|
||||
* Add: DNS Providers: Hetzner
|
||||
* Change: DNS Providers: Route53 field "max_retries" has been renamed to "hosted_zone_id"
|
||||
* Cleanup: Refactor "general.volt" from "layout_partials/base_form" to "layout_partials/base_tabs"
|
||||
* Cleanup: Refactor "general.volt", "reverse_proxy.volt" and "diagnostics.volt" to imported ajaxGet() and ajaxCall()
|
||||
* Cleanup: Adjust style of all views
|
||||
* Cleanup: Restructure "general.xml" to include tabs, add new "Advanced Settings" Tab
|
||||
|
||||
1.6.3
|
||||
|
||||
* Add: Disable Propagation Timeout in DNS Provider Tab. This can help if the DNS Challenge fails due to DNS Propagation being too slow.
|
||||
|
||||
@@ -40,9 +40,5 @@ class GeneralController extends IndexController
|
||||
// Assign the general settings form to the view
|
||||
$this->view->pick('OPNsense/Caddy/general');
|
||||
$this->view->generalForm = $this->getForm("general");
|
||||
$this->view->dnsproviderForm = $this->getForm("dnsprovider");
|
||||
$this->view->dynamicdnsForm = $this->getForm("dynamicdns");
|
||||
$this->view->authproviderForm = $this->getForm("authprovider");
|
||||
$this->view->logsettingsForm = $this->getForm("logsettings");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>caddy.general.AuthProvider</id>
|
||||
<label>Forward Auth Provider</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select a Forward Auth Provider. It can be added inside a "Handler" by enabling the "Forward Auth" checkbox. For Authelia only the basic subdomain example is supported. More information: https://www.authelia.com/integration/proxies/caddy/#basic-examples. For Authentik custom headers are not supported. More information: https://docs.goauthentik.io/docs/providers/proxy/server_caddy]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToDomain</id>
|
||||
<label>Forward Auth Domain</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter the domain name or IP address of the chosen Forward Auth Provider.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToPort</id>
|
||||
<label>Forward Auth Port</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter the listen port of the chosen Forward Auth Provider.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToTls</id>
|
||||
<label>TLS</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Enable or disable HTTP over TLS (HTTPS) to communicate with the Forward Auth Provider.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToUri</id>
|
||||
<label>Forward Auth URI</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter the URI of the authz api endpoint.]]></help>
|
||||
</field>
|
||||
</form>
|
||||
@@ -1,66 +0,0 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsProvider</id>
|
||||
<label>DNS Provider</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the DNS provider for the DNS-01 Challenge and Dynamic DNS. This is mostly needed for Wildcard Certificates, and for Dynamic DNS. To use, enable the checkbox in a "Domain" or "Subdomain". For more information, visit https://github.com/caddy-dns where each module is community maintained.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsApiKey</id>
|
||||
<label>DNS API Standard Field</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[This is the standard field for the API Key. Field can be left empty if optional: Cloudflare "api_token", Duckdns "api_token", DigitalOcean "auth_token", Godaddy "api_token", Gandi "bearer_token", IONOS "api_token", deSEC "token", Route53 "access_key_id", Porkbun "api_key", ACME-DNS "username", Netlify "personal_access_token", Njalla "api_token", Google Cloud DNS "gcp_project", Azure "tenant_id", OVH "endpoint", Namecheap "api_key", PowerDNS "server_url", DDNSS "api_token", Linode "api_token", Tencent Cloud "secret_id", Dinahosting "username", Hexonet "username", Mail-in-a-Box "api_url", DNS Made Easy "api_key", Bunny "access_key", Civo "api_token", Scaleway "secret_key", ACME Proxy "username", INWX "username", Netcup "customer_number", RFC2136 "key_name", Name.com "token", EasyDNS "api_token", Infomaniak "api_token", DirectAdmin "host", Hosttech "api_token", Vultr "api_token"]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Additional Fields</label>
|
||||
<collapse>true</collapse>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsSecretApiKey</id>
|
||||
<label>DNS API Additional Field 1</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Duckdns "override_domain", Route53 "secret_access_key", Porkbun "api_secret_key", ACME-DNS "password", Azure "client_id", OVH "application_key", Namecheap "user", PowerDNS "api_token", DDNSS "username", Linode "api_url", Tencent Cloud "secret_key", Dinahosting "password", Hexonet "password", Mail-in-a-Box "email_address", DNS Made Easy "secret_key", Scaleway "organization_id", ACME Proxy "password", INWX "password", Netcup "api_key", RFC2136 "key_alg", Name.com "server", EasyDNS "api_key", DirectAdmin "user".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField1</id>
|
||||
<label>DNS API Additional Field 2</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "max_retries", ACME-DNS "subdomain", Azure "client_secret", OVH "application_secret", Namecheap "api_endpoint", DDNSS "password", Linode "api_version", Mail-in-a-Box "password", DNS Made Easy "api_endpoint", ACME Proxy "endpoint", INWX "shared_secret", Netcup "api_password", Name.com "user", EasyDNS "api_url", DirectAdmin "login_key", RFC2136 "key".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField2</id>
|
||||
<label>DNS API Additional Field 3</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "profile", ACME-DNS "server_url", Azure "subscription_id", OVH "consumer_key", Namecheap "client_ip", DDNS "password", INWX "endpoint_url", DirectAdmin "insecure_requests", RFC2136 "server".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField3</id>
|
||||
<label>DNS API Additional Field 4</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "region", Azure "resource_group_name".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField4</id>
|
||||
<label>DNS API Additional Field 5</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "session_token".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>DNS Propagation</label>
|
||||
<collapse>true</collapse>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsPropagationResolvers</id>
|
||||
<label>Resolvers</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty to use the system resolvers (default). Resolvers customizes the DNS resolvers used when performing the DNS challenge; these take precedence over system resolvers or any default ones. If set here, the resolvers will propagate to all configured certificate issuers. If the system resolvers use DNS over TLS, setting an external resolver here is required or the DNS challenge will fail.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsPropagationTimeout</id>
|
||||
<label>Disable Propagation Timeout</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Propagation Timeout is a duration value that sets the maximum time to wait for the DNS TXT records to appear when using the DNS challenge. The default is 2 minutes. Disabling this will set propagation_timeout to -1 (checking propagation infinitely) and additionally set a propagation_delay of 30s (wait time before starting propagation checks). This can help when the DNS Challenge continues to fail because the local DNS Server does not know the new DNS TXT records yet in the default timeframe of 2 minutes.]]></help>
|
||||
</field>
|
||||
</form>
|
||||
@@ -1,44 +0,0 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsIpVersions</id>
|
||||
<label>DynDns IP Version</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the DynDns IP Version: "IPv4+IPv6" to set IPv4 A-Records and IPv6 AAAA-Records, "IPv4 only" for only A-Records, "IPv6 only" for only AAAA-Records.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsUpdateOnly</id>
|
||||
<label>DynDns Update Only</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[If enabled, no new DNS records will be created. Only existing records will be updated. This means that the A or AAAA records need to be created manually ahead of time.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsInterval</id>
|
||||
<label>DynDns Check Interval</label>
|
||||
<type>text</type>
|
||||
<hint>1800</hint>
|
||||
<help><![CDATA[Set the interval in seconds to poll for changes in the IP address. Leave empty to use system defaults.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsTtl</id>
|
||||
<label>DynDns TTL</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Set the TTL (Time to Live) for DNS records in seconds. Leave empty to use the default of an already existing TTL (when updating only) or the default of the provider API (when creating new records). If explicitely set, values should be as defined in rfc2181 section 8.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Additional Checks</label>
|
||||
<collapse>true</collapse>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsSimpleHttp</id>
|
||||
<label>DynDns Check Http</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter a URL to test the current IP address of the firewall via the HTTP protocol. This is generally not needed as Caddy uses default providers to test the current IP addresses. If a custom provider is preferred, enter the "https://" link to an IP address testing website.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsInterface</id>
|
||||
<label>DynDns Check Interface</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select an interface to extract the current IP addresses of the firewall. This is generally not needed as Caddy uses default providers to test the current IP addresses. Depending on the specified DynDns IP Version, at most one IPv6 Global Unicast Address and one IPv4 non-RFC1918 Address will be extracted.]]></help>
|
||||
</field>
|
||||
</form>
|
||||
@@ -1,72 +1,234 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>caddy.general.enabled</id>
|
||||
<label>Enabled</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Enable or disable the Caddy web server.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.EnableLayer4</id>
|
||||
<label>(Feature Preview) Enable Layer4</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Enable Layer4 support to stream TCP/UDP. WARNING: This feature is in active developement and the configuration can break or change in the future. The Layer4 app matches before the HTTP app. Deactivating this option will remove all Layer4 functionality completely. More information: https://github.com/mholt/caddy-l4]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.HttpPort</id>
|
||||
<label>HTTP Port</label>
|
||||
<type>text</type>
|
||||
<hint>80</hint>
|
||||
<help><![CDATA[If the default HTTP port is changed to e.g. 8080, then a port forward from port 80 to 8080 is necessary to issue automatic certificates with the HTTP-01 challenge and serve clients the reverse proxied resources.]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.HttpsPort</id>
|
||||
<label>HTTPS Port</label>
|
||||
<type>text</type>
|
||||
<hint>443</hint>
|
||||
<help><![CDATA[If the default HTTPS port is changed to e.g. 8443, then a port forward from port 443 to 8443 is necessary to issue automatic certificates with the TLS-ALPN-01 challenge and serve clients the reverse proxied resources.]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DisableSuperuser</id>
|
||||
<label>Disable Superuser</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Run this service as "www" user and group, instead of "root". This setting increases security, but comes with the hard restriction that the well-known port range can not be used anymore. After enabling and saving this setting, the service has to be totally restarted. For this, please disable Caddy and press Apply. Afterwards enable Caddy and press Apply. This setting is reversible by following the same steps.]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsEmail</id>
|
||||
<label>ACME Email</label>
|
||||
<type>text</type>
|
||||
<hint>info@example.com</hint>
|
||||
<help><![CDATA[Enter the email address for certificate notifications. This is required to receive automatic certificates from "Let's Encrypt" and "ZeroSSL".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsAutoHttps</id>
|
||||
<label>Auto HTTPS</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the Auto HTTPS option. "On (default)" creates automatic certificates using "Let's Encrypt" or "ZeroSSL". "Off" turns all automatic certificate requests off.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.accesslist</id>
|
||||
<label>Trusted Proxies</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select an Access List to set IP ranges of Trusted Proxies. If Caddy is not the first server being connected to by clients (for example, when a "CDN" is in front of Caddy), configure "Trusted Proxies" with a list of IP ranges (CIDRs) from which incoming requests are trusted to have sent good values for these headers. Additionally, set the same Access List to the domains the Trusted Proxies connect to.]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.abort</id>
|
||||
<label>Abort Connections</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Abort all connections that don't have a matching Handle or Access List. This option does not conflict with "Let's Encrypt" or "ZeroSSL". Disable it for troubleshooting purposes, e.g., testing if the "Domain" works and the automatic certificate has been installed. For production use, enabling this option is recommended.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.GracePeriod</id>
|
||||
<label>Grace Period</label>
|
||||
<type>text</type>
|
||||
<hint>10</hint>
|
||||
<help><![CDATA[Defines the grace period for shutting down Caddy during a reload in seconds. During the grace period, no new connections are accepted, idle connections are closed, and active connections are impatiently waited upon to finish their requests. If clients do not finish their requests within the grace period, the server will be forcefully terminated to allow the reload to complete and free up resources. This can influence how long "Apply" of new configurations take, since Caddy waits for all open connections to close.]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<tab id="general-settings" description="General Settings">
|
||||
<field>
|
||||
<id>caddy.general.enabled</id>
|
||||
<label>Enabled</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Enable or disable the Caddy web server.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsEmail</id>
|
||||
<label>ACME Email</label>
|
||||
<type>text</type>
|
||||
<hint>info@example.com</hint>
|
||||
<help><![CDATA[Enter the email address for certificate notifications. This is required to receive automatic certificates from "Let's Encrypt" and "ZeroSSL".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsAutoHttps</id>
|
||||
<label>Auto HTTPS</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the Auto HTTPS option. "On (default)" creates automatic certificates using "Let's Encrypt" or "ZeroSSL". "Off" turns all automatic certificate requests off.]]></help>
|
||||
</field>
|
||||
</tab>
|
||||
<tab id="general-advanced" description="Advanced Settings">
|
||||
<field>
|
||||
<id>caddy.general.EnableLayer4</id>
|
||||
<label>Enable Layer4</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Enable Layer4 support to stream TCP/UDP. This feature is in active developement and the configuration can break or change in the future. The Layer4 app matches before the HTTP app. Deactivating this option will remove all Layer4 functionality completely. More information: https://github.com/mholt/caddy-l4]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DisableSuperuser</id>
|
||||
<label>Disable Superuser</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Run this service as "www" user and group, instead of "root". This setting increases security, but comes with the hard restriction that the well-known port range can not be used anymore. After enabling and saving this setting, the service has to be totally restarted. For this, please disable Caddy and press Apply. Afterwards enable Caddy and press Apply. This setting is reversible by following the same steps.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.HttpPort</id>
|
||||
<label>HTTP Port</label>
|
||||
<type>text</type>
|
||||
<hint>80</hint>
|
||||
<help><![CDATA[If the default HTTP port is changed to e.g. 8080, then a port forward from port 80 to 8080 is necessary to issue automatic certificates with the HTTP-01 challenge and serve clients the reverse proxied resources.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.HttpsPort</id>
|
||||
<label>HTTPS Port</label>
|
||||
<type>text</type>
|
||||
<hint>443</hint>
|
||||
<help><![CDATA[If the default HTTPS port is changed to e.g. 8443, then a port forward from port 443 to 8443 is necessary to issue automatic certificates with the TLS-ALPN-01 challenge and serve clients the reverse proxied resources.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.accesslist</id>
|
||||
<label>Trusted Proxies</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select an Access List to set IP ranges of Trusted Proxies. If Caddy is not the first server being connected to by clients (for example, when a "CDN" is in front of Caddy), configure "Trusted Proxies" with a list of IP ranges (CIDRs) from which incoming requests are trusted to have sent good values for these headers. Additionally, set the same Access List to the domains the Trusted Proxies connect to.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.abort</id>
|
||||
<label>Abort Connections</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Abort all connections that don't have a matching Handle or Access List. This option does not conflict with "Let's Encrypt" or "ZeroSSL". Disable it for troubleshooting purposes, e.g., testing if the "Domain" works and the automatic certificate has been installed. For production use, enabling this option is recommended.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.GracePeriod</id>
|
||||
<label>Grace Period</label>
|
||||
<type>text</type>
|
||||
<hint>10</hint>
|
||||
<help><![CDATA[Defines the grace period for shutting down Caddy during a reload in seconds. During the grace period, no new connections are accepted, idle connections are closed, and active connections are impatiently waited upon to finish their requests. If clients do not finish their requests within the grace period, the server will be forcefully terminated to allow the reload to complete and free up resources. This can influence how long "Apply" of new configurations take, since Caddy waits for all open connections to close.]]></help>
|
||||
</field>
|
||||
</tab>
|
||||
<tab id="general-logsettings" description="Log Settings">
|
||||
<field>
|
||||
<id>caddy.general.LogLevel</id>
|
||||
<label>Log Level</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the minimum global Log Level. "INFO" is the default and should not be changed without a reason, as it displays the ACME Client messages for automatic certificates. This setting does not influence the HTTP Access logs; they always use "INFO", which is their lowest supported Log Level.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.LogCredentials</id>
|
||||
<label>Log Credentials</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Log all cookies and authorization in HTTP request logging. Use this option combined with "HTTP Access Log" in a "Domain". Enable this option only for troubleshooting.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.LogAccessPlain</id>
|
||||
<label>Log HTTP Access in JSON Format</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Log HTTP access in a standard JSON logfile per domain, e.g., for processing by CrowdSec. Use this option combined with "HTTP Access Log" in a "Domain". Enabling this will make the HTTP Access Log disappear from the standard Log File. Logs can be found in the filesystem at "/var/log/caddy/access/".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.LogAccessPlainKeep</id>
|
||||
<label>Keep HTTP Access JSON Logs for (days)</label>
|
||||
<hint>10</hint>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Specify how many days to keep the JSON access logs.]]></help>
|
||||
</field>
|
||||
</tab>
|
||||
<tab id="general-dnsprovider" description="DNS Provider">
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsProvider</id>
|
||||
<label>DNS Provider</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the DNS provider for the DNS-01 Challenge and Dynamic DNS. This is mostly needed for Wildcard Certificates, and for Dynamic DNS. To use, enable the checkbox in a "Domain" or "Subdomain". For more information, visit https://github.com/caddy-dns where each module is community maintained.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsApiKey</id>
|
||||
<label>DNS API Standard Field</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[This is the standard field for the API Key. Field can be left empty if optional: Cloudflare "api_token", Duckdns "api_token", DigitalOcean "auth_token", Godaddy "api_token", Gandi "bearer_token", IONOS "api_token", deSEC "token", Route53 "access_key_id", Porkbun "api_key", ACME-DNS "username", Netlify "personal_access_token", Njalla "api_token", Google Cloud DNS "gcp_project", Azure "tenant_id", OVH "endpoint", Namecheap "api_key", PowerDNS "server_url", DDNSS "api_token", Linode "api_token", Tencent Cloud "secret_id", Dinahosting "username", Hexonet "username", Mail-in-a-Box "api_url", DNS Made Easy "api_key", Bunny "access_key", Civo "api_token", Scaleway "secret_key", ACME Proxy "username", INWX "username", Netcup "customer_number", RFC2136 "key_name", Name.com "token", EasyDNS "api_token", Infomaniak "api_token", DirectAdmin "host", Hosttech "api_token", Vultr "api_token", Hetzner "api_token"]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Additional Fields</label>
|
||||
<collapse>true</collapse>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsSecretApiKey</id>
|
||||
<label>DNS API Additional Field 1</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Duckdns "override_domain", Route53 "secret_access_key", Porkbun "api_secret_key", ACME-DNS "password", Azure "client_id", OVH "application_key", Namecheap "user", PowerDNS "api_token", DDNSS "username", Linode "api_url", Tencent Cloud "secret_key", Dinahosting "password", Hexonet "password", Mail-in-a-Box "email_address", DNS Made Easy "secret_key", Scaleway "organization_id", ACME Proxy "password", INWX "password", Netcup "api_key", RFC2136 "key_alg", Name.com "server", EasyDNS "api_key", DirectAdmin "user".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField1</id>
|
||||
<label>DNS API Additional Field 2</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "hosted_zone_id", ACME-DNS "subdomain", Azure "client_secret", OVH "application_secret", Namecheap "api_endpoint", DDNSS "password", Linode "api_version", Mail-in-a-Box "password", DNS Made Easy "api_endpoint", ACME Proxy "endpoint", INWX "shared_secret", Netcup "api_password", Name.com "user", EasyDNS "api_url", DirectAdmin "login_key", RFC2136 "key".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField2</id>
|
||||
<label>DNS API Additional Field 3</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "profile", ACME-DNS "server_url", Azure "subscription_id", OVH "consumer_key", Namecheap "client_ip", DDNS "password", INWX "endpoint_url", DirectAdmin "insecure_requests", RFC2136 "server".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField3</id>
|
||||
<label>DNS API Additional Field 4</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "region", Azure "resource_group_name".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsOptionalField4</id>
|
||||
<label>DNS API Additional Field 5</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty if your DNS Provider isn't specified here. Field can be left empty if optional: Route53 "session_token".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>DNS Propagation</label>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsPropagationResolvers</id>
|
||||
<label>Resolvers</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Leave empty to use the system resolvers (default). Resolvers customizes the DNS resolvers used when performing the DNS challenge; these take precedence over system resolvers or any default ones. If set here, the resolvers will propagate to all configured certificate issuers. If the system resolvers use DNS over TLS, setting an external resolver here is required or the DNS challenge will fail.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.TlsDnsPropagationTimeout</id>
|
||||
<label>Disable Propagation Timeout</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Propagation Timeout is a duration value that sets the maximum time to wait for the DNS TXT records to appear when using the DNS challenge. The default is 2 minutes. Disabling this will set propagation_timeout to -1 (checking propagation infinitely) and additionally set a propagation_delay of 30s (wait time before starting propagation checks). This can help when the DNS Challenge continues to fail because the local DNS Server does not know the new DNS TXT records yet in the default timeframe of 2 minutes.]]></help>
|
||||
</field>
|
||||
</tab>
|
||||
<tab id="general-dynamicdns" description="Dynamic DNS">
|
||||
<field>
|
||||
<id>caddy.general.DynDnsIpVersions</id>
|
||||
<label>DynDns IP Version</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the DynDns IP Version: "IPv4+IPv6" to set IPv4 A-Records and IPv6 AAAA-Records, "IPv4 only" for only A-Records, "IPv6 only" for only AAAA-Records.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsUpdateOnly</id>
|
||||
<label>DynDns Update Only</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[If enabled, no new DNS records will be created. Only existing records will be updated. This means that the A or AAAA records need to be created manually ahead of time.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsInterval</id>
|
||||
<label>DynDns Check Interval</label>
|
||||
<type>text</type>
|
||||
<hint>1800</hint>
|
||||
<help><![CDATA[Set the interval in seconds to poll for changes in the IP address. Leave empty to use system defaults.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsTtl</id>
|
||||
<label>DynDns TTL</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Set the TTL (Time to Live) for DNS records in seconds. Leave empty to use the default of an already existing TTL (when updating only) or the default of the provider API (when creating new records). If explicitely set, values should be as defined in rfc2181 section 8.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsSimpleHttp</id>
|
||||
<label>DynDns Check Http</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter a URL to test the current IP address of the firewall via the HTTP protocol. This is generally not needed as Caddy uses default providers to test the current IP addresses. If a custom provider is preferred, enter the "https://" link to an IP address testing website.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.DynDnsInterface</id>
|
||||
<label>DynDns Check Interface</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select an interface to extract the current IP addresses of the firewall. This is generally not needed as Caddy uses default providers to test the current IP addresses. Depending on the specified DynDns IP Version, at most one IPv6 Global Unicast Address and one IPv4 non-RFC1918 Address will be extracted.]]></help>
|
||||
</field>
|
||||
</tab>
|
||||
<tab id="general-authprovider" description="Auth Provider">
|
||||
<field>
|
||||
<id>caddy.general.AuthProvider</id>
|
||||
<label>Forward Auth Provider</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select a Forward Auth Provider. It can be added inside a "Handler" by enabling the "Forward Auth" checkbox. For Authelia only the basic subdomain example is supported. More information: https://www.authelia.com/integration/proxies/caddy/#basic-examples. For Authentik custom headers are not supported. More information: https://docs.goauthentik.io/docs/providers/proxy/server_caddy]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToDomain</id>
|
||||
<label>Forward Auth Domain</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter the domain name or IP address of the chosen Forward Auth Provider.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToPort</id>
|
||||
<label>Forward Auth Port</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter the listen port of the chosen Forward Auth Provider.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToTls</id>
|
||||
<label>TLS</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Enable or disable HTTP over TLS (HTTPS) to communicate with the Forward Auth Provider.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.AuthToUri</id>
|
||||
<label>Forward Auth URI</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter the URI of the authz api endpoint.]]></help>
|
||||
</field>
|
||||
</tab>
|
||||
<activetab>general-settings</activetab>
|
||||
</form>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>caddy.general.LogLevel</id>
|
||||
<label>Log Level</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Select the minimum global Log Level. "INFO" is the default and should not be changed without a reason, as it displays the ACME Client messages for automatic certificates. This setting does not influence the HTTP Access logs; they always use "INFO", which is their lowest supported Log Level.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.LogCredentials</id>
|
||||
<label>Log Credentials</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Log all cookies and authorization in HTTP request logging. Use this option combined with "HTTP Access Log" in a "Domain". Enable this option only for troubleshooting.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.LogAccessPlain</id>
|
||||
<label>Log HTTP Access in JSON Format</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Log HTTP access in a standard JSON logfile per domain, e.g., for processing by CrowdSec. Use this option combined with "HTTP Access Log" in a "Domain". Enabling this will make the HTTP Access Log disappear from the standard Log File. Logs can be found in the filesystem at "/var/log/caddy/access/".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>caddy.general.LogAccessPlainKeep</id>
|
||||
<label>Keep HTTP Access JSON Logs for (days)</label>
|
||||
<hint>10</hint>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Specify how many days to keep the JSON access logs.]]></help>
|
||||
</field>
|
||||
</form>
|
||||
@@ -61,6 +61,7 @@
|
||||
<directadmin>DirectAdmin</directadmin>
|
||||
<hosttech>Hosttech</hosttech>
|
||||
<vultr>Vultr</vultr>
|
||||
<hetzner>Hetzner</hetzner>
|
||||
</OptionValues>
|
||||
</TlsDnsProvider>
|
||||
<TlsDnsApiKey type="TextField"/>
|
||||
@@ -409,6 +410,7 @@
|
||||
<Required>Y</Required>
|
||||
<Default>tlssni</Default>
|
||||
<OptionValues>
|
||||
<dns>DNS</dns>
|
||||
<httphost>HTTP (Host Header)</httphost>
|
||||
<postgres>Postgres</postgres>
|
||||
<proxy_protocol>Proxy Protocol</proxy_protocol>
|
||||
|
||||
@@ -35,29 +35,24 @@
|
||||
* @param {string} displaySelector - jQuery selector for the element where data should be displayed.
|
||||
*/
|
||||
function fetchAndDisplay(url, displaySelector) {
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "GET",
|
||||
success: function(response) {
|
||||
if (response.status === "success") {
|
||||
let formattedContent;
|
||||
if (typeof response.content === 'object') {
|
||||
// If the content is an object, stringify and format it
|
||||
formattedContent = JSON.stringify(response.content, null, 2);
|
||||
} else {
|
||||
// If the content is plain text (as with the Caddyfile), just use it directly
|
||||
formattedContent = response.content;
|
||||
}
|
||||
$(displaySelector).text(formattedContent);
|
||||
ajaxGet(url, null, function(response, status) {
|
||||
if (status === "success" && response.status === "success") {
|
||||
let formattedContent;
|
||||
if (typeof response.content === 'object') {
|
||||
// If the content is an object, stringify and format it
|
||||
formattedContent = JSON.stringify(response.content, null, 2);
|
||||
} else {
|
||||
// If the response status is not 'success', display an error message
|
||||
$(displaySelector).text("{{ lang._('Failed to load content: ') }}" + response.message || "{{ lang._('Unknown error') }}");
|
||||
// If the content is plain text (as with the Caddyfile), just use it directly
|
||||
formattedContent = response.content;
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// Handle errors from the AJAX request itself
|
||||
$(displaySelector).text("{{ lang._('AJAX error accessing the API: ') }}" + error);
|
||||
$(displaySelector).text(formattedContent);
|
||||
} else {
|
||||
// If the response status is not 'success', display an error message
|
||||
$(displaySelector).text("{{ lang._('Failed to load content: ') }}" + (response.message || "{{ lang._('Unknown error') }}"));
|
||||
}
|
||||
}).fail(function(xhr, status, error) {
|
||||
// Handle errors from the AJAX request itself
|
||||
$(displaySelector).text("{{ lang._('AJAX error accessing the API: ') }}" + error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,20 +118,14 @@
|
||||
|
||||
// Event handler for the Validate Caddyfile button
|
||||
$('#validateCaddyfile').click(function() {
|
||||
$.ajax({
|
||||
url: '/api/caddy/service/validate',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (data && data['status'].toLowerCase() === 'ok') {
|
||||
showDialogAlert(BootstrapDialog.TYPE_SUCCESS, "{{ lang._('Validation Successful') }}", data['message']);
|
||||
} else {
|
||||
showDialogAlert(BootstrapDialog.TYPE_WARNING, "{{ lang._('Validation Error') }}", data['message']); // Show error message from the API
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
showDialogAlert(BootstrapDialog.TYPE_DANGER, "{{ lang._('Validation Request Failed') }}", error); // Show AJAX error
|
||||
ajaxGet('/api/caddy/service/validate', null, function(data, status) {
|
||||
if (status === "success" && data && data['status'].toLowerCase() === 'ok') {
|
||||
showDialogAlert(BootstrapDialog.TYPE_SUCCESS, "{{ lang._('Validation Successful') }}", data['message']);
|
||||
} else {
|
||||
showDialogAlert(BootstrapDialog.TYPE_WARNING, "{{ lang._('Validation Error') }}", data['message']); // Show error message from the API
|
||||
}
|
||||
}).fail(function(xhr, status, error) {
|
||||
showDialogAlert(BootstrapDialog.TYPE_DANGER, "{{ lang._('Validation Request Failed') }}", error); // Show AJAX error
|
||||
});
|
||||
});
|
||||
|
||||
@@ -144,23 +133,24 @@
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.nav-tabs a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.custom-style .content-box {
|
||||
padding: 20px; /* Adds padding around the contents of each tab */
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.custom-style .display-area {
|
||||
overflow-y: scroll;
|
||||
/* Dynamic height management using clamp for varying screen sizes */
|
||||
height: clamp(50px, 50vh, 4000px);
|
||||
margin-bottom: 20px; /* Adds bottom margin to separate from the help text */
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.custom-style .help-text {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.4;
|
||||
/* Adjusting text size for readability on various displays */
|
||||
font-size: clamp(12px, 1.5vw, 16px);
|
||||
.custom-style .content-box .btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -176,7 +166,6 @@
|
||||
<div id="caddyfileTab" class="tab-pane fade in active">
|
||||
<div class="content-box">
|
||||
<pre id="caddyfileDisplay" class="display-area"></pre>
|
||||
<p class="help-text">{{ lang._("This is the generated configuration located at %sCaddyfile%s. It's the main configuration file to get support with. The validation button triggers a manual check for any configuration errors, which is the same check that is triggered by the Apply buttons automatically.") | format('<code>/usr/local/etc/caddy/', '</code>') }}</p>
|
||||
<button class="btn btn-primary download-btn" id="downloadCaddyfile" type="button">{{ lang._('Download') }}</button>
|
||||
<button class="btn btn-secondary" id="validateCaddyfile" type="button">{{ lang._('Validate Caddyfile') }}</button>
|
||||
<br/><br/>
|
||||
@@ -186,7 +175,6 @@
|
||||
<div id="jsonConfigTab" class="tab-pane fade">
|
||||
<div class="content-box">
|
||||
<pre id="jsonDisplay" class="display-area"></pre>
|
||||
<p class="help-text">{{ lang._("Shows the running Caddy configuration located in %sautosave.json%s. It is automatically adapted from the Caddyfile and also includes any custom imported configurations from %scaddy.d%s.") | format('<code>/var/db/caddy/config/caddy/', '</code>', '<code>/usr/local/etc/caddy/', '</code>') }}</p>
|
||||
<button class="btn btn-primary download-btn" id="downloadJSONConfig" type="button">{{ lang._('Download') }}</button>
|
||||
<br/><br/>
|
||||
</div>
|
||||
|
||||
@@ -24,254 +24,107 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#}
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
const data_get_map = {'frm_GeneralSettings':"/api/caddy/General/get"};
|
||||
mapDataToFormUI(data_get_map).done(function(data){
|
||||
|
||||
// Function to initialize form elements within a tab dynamically
|
||||
function initializeFormElements(tabContent) {
|
||||
$(tabContent).find('.selectpicker').selectpicker('refresh');
|
||||
}
|
||||
|
||||
// Initialize the first tab
|
||||
initializeFormElements('#generalTab');
|
||||
|
||||
// Handle tab changes
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
|
||||
let targetTab = $(e.target).attr('href'); // Activated tab
|
||||
initializeFormElements(targetTab);
|
||||
});
|
||||
|
||||
// Function to show alerts in the HTML message area
|
||||
function showAlert(message, type = "error") {
|
||||
let alertClass = type === "error" ? "alert-danger" : "alert-success";
|
||||
let messageArea = $("#messageArea");
|
||||
|
||||
// Stop any current animation, clear the queue, and immediately hide the element
|
||||
messageArea.stop(true, true).hide();
|
||||
|
||||
// Now set the class and message
|
||||
messageArea.removeClass("alert-success alert-danger").addClass(alertClass).html(message);
|
||||
|
||||
// Use fadeIn to make the message appear smoothly, then fadeOut after a delay
|
||||
messageArea.fadeIn(500).delay(15000).fadeOut(500, function() {
|
||||
// Clear the message after fading out to ensure it's clean for the next message
|
||||
$(this).html('');
|
||||
});
|
||||
}
|
||||
|
||||
// Hide message area when starting new actions
|
||||
$('input, select, textarea').on('change', function() {
|
||||
$("#messageArea").hide();
|
||||
});
|
||||
|
||||
// These fields do not need the validation workaround, they get their validation messages from core.
|
||||
let validationExceptions = [
|
||||
"caddy.general.enabled",
|
||||
"caddy.general.EnableLayer4",
|
||||
"caddy.general.DisableSuperuser",
|
||||
"caddy.general.HttpPort",
|
||||
"caddy.general.HttpsPort",
|
||||
"caddy.general.TlsEmail",
|
||||
"caddy.general.TlsAutoHttps",
|
||||
"caddy.general.accesslist",
|
||||
"caddy.general.abort",
|
||||
"caddy.general.GracePeriod"
|
||||
];
|
||||
|
||||
// For all other fields that are in different tabs than the main form, append the validation message.
|
||||
// Note: This is a workaround and generally not needed. Do not copy this.
|
||||
function displayValidationErrors(errors) {
|
||||
$(".error-message").remove(); // Clear previous error messages
|
||||
$(".error-input").removeClass("error-input");
|
||||
$(".error-label").removeClass("error-label");
|
||||
|
||||
for (let key in errors) {
|
||||
if (errors.hasOwnProperty(key) && !validationExceptions.includes(key)) {
|
||||
let jquerySafeKey = key.replace(/\./g, '\\.'); // Escape dots for jQuery ID selector
|
||||
let field = $('#' + jquerySafeKey);
|
||||
let label = $('#control_label_' + jquerySafeKey);
|
||||
let helpBlock = $('#help_block_' + jquerySafeKey);
|
||||
|
||||
if (field.length !== 0) {
|
||||
field.addClass('error-input');
|
||||
label.addClass('error-label');
|
||||
let errorMessage = $('<div class="error-message">' + errors[key] + '</div>');
|
||||
helpBlock.html(errorMessage); // append error message into help block
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('input, select, textarea').on('change', function() {
|
||||
// Remove error styles when the user corrects the input
|
||||
if($(this).hasClass('error-input')) {
|
||||
$(this).removeClass('error-input');
|
||||
let id = this.id.replace(/\./g, '\\.');
|
||||
let label = $('#control_label_' + id);
|
||||
label.removeClass('error-label');
|
||||
let helpBlock = $('#help_block_' + id);
|
||||
helpBlock.empty();
|
||||
}
|
||||
});
|
||||
|
||||
// Reconfigure the Caddy service, additional form save and validation with a validation API is made beforehand
|
||||
$("#reconfigureAct").SimpleActionButton({
|
||||
onPreAction: function() {
|
||||
const dfObj = $.Deferred();
|
||||
|
||||
// Save the form before continuing
|
||||
saveFormToEndpoint("/api/caddy/general/set", 'frm_GeneralSettings',
|
||||
function() { // callback_ok: What to do when save is successful
|
||||
// After successful save, proceed with validation
|
||||
$.ajax({
|
||||
url: "/api/caddy/service/validate",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (data && data['status'].toLowerCase() === 'ok') {
|
||||
dfObj.resolve(); // Configuration is valid
|
||||
} else {
|
||||
showAlert(data['message'], "{{ lang._('Validation Error') }}");
|
||||
dfObj.reject(); // Configuration is invalid
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
showAlert("{{ lang._('Validation request failed: ') }}" + error, "{{ lang._('Validation Error') }}");
|
||||
dfObj.reject(); // AJAX request failed
|
||||
}
|
||||
});
|
||||
},
|
||||
true, // disable_dialog: This has to be set explicitely so there actually is a callback_fail to catch the validation error.
|
||||
function(errorData) { // callback_fail: What to do when save fails
|
||||
if (errorData.validations) {
|
||||
displayValidationErrors(errorData.validations);
|
||||
} else {
|
||||
showAlert("{{ lang._('Configuration save failed: ') }}" + (errorData.message || "{{ lang._('Validation Error') }}"), "{{ lang._('Error') }}");
|
||||
}
|
||||
dfObj.reject(); // Reject the deferred object to stop the reconfigure action
|
||||
}
|
||||
);
|
||||
|
||||
return dfObj.promise();
|
||||
},
|
||||
onAction: function(data, status) {
|
||||
if (status === "success" && data && data['status'].toLowerCase() === 'ok') {
|
||||
// Configuration is valid and applied, possibly refresh UI or notify user
|
||||
showAlert("{{ lang._('Configuration applied successfully.') }}", "{{ lang._('Apply Success') }}");
|
||||
updateServiceControlUI('caddy');
|
||||
} else {
|
||||
// Handle errors or unsuccessful application
|
||||
showAlert("{{ lang._('An error occurred while applying the configuration.') }}", "{{ lang._('Error') }}");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#saveSettings").SimpleActionButton({
|
||||
onAction: function() {
|
||||
const dfObj = $.Deferred();
|
||||
|
||||
// Save the form before continuing
|
||||
saveFormToEndpoint("/api/caddy/general/set", 'frm_GeneralSettings',
|
||||
function() { // callback_ok: What to do when save is successful
|
||||
showAlert("{{ lang._('Configuration saved successfully. Please do not forget to apply the configuration.') }}", "{{ lang._('Save Success') }}");
|
||||
dfObj.resolve();
|
||||
},
|
||||
true, // disable_dialog
|
||||
function(errorData) { // callback_fail: What to do when save fails
|
||||
if (errorData.validations) {
|
||||
displayValidationErrors(errorData.validations);
|
||||
} else {
|
||||
showAlert("{{ lang._('Configuration save failed: ') }}" + (errorData.message || "{{ lang._('Validation Error') }}"), "{{ lang._('Error') }}");
|
||||
}
|
||||
dfObj.reject();
|
||||
}
|
||||
);
|
||||
|
||||
return dfObj.promise();
|
||||
},
|
||||
});
|
||||
|
||||
// Initialize the service control UI for 'caddy'
|
||||
// Initial setup
|
||||
mapDataToFormUI({'frm_general': "/api/caddy/general/get"}).done(function() {
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
updateServiceControlUI('caddy');
|
||||
});
|
||||
|
||||
/**
|
||||
* Displays an alert message to the user.
|
||||
*
|
||||
* @param {string} message - The message to display.
|
||||
* @param {string} [type="error"] - The type of alert (error or success).
|
||||
*/
|
||||
function showAlert(message, type = "error") {
|
||||
const alertClass = type === "error" ? "alert-danger" : "alert-success";
|
||||
const messageArea = $("#messageArea");
|
||||
messageArea.stop(true, true).hide();
|
||||
messageArea.removeClass("alert-success alert-danger").addClass(alertClass).html(message);
|
||||
messageArea.fadeIn(500).delay(15000).fadeOut(500, function() {
|
||||
$(this).html('');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the spinner icon.
|
||||
* @param {string} generalId - The ID of the general form.
|
||||
* @param {string} action - The action to perform ("start" or "stop").
|
||||
*/
|
||||
function setSpinner(generalId, action) {
|
||||
const $progressIcon = $("#" + generalId + "_progress");
|
||||
if (action === 'start') {
|
||||
$progressIcon.addClass("fa fa-spinner fa-pulse");
|
||||
} else if (action === 'stop') {
|
||||
$progressIcon.removeClass("fa fa-spinner fa-pulse");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures the service.
|
||||
* @param {string} generalId - The ID of the general form (used for controlling the spinner).
|
||||
*/
|
||||
function reconfigureService(generalId) {
|
||||
ajaxCall("/api/caddy/service/reconfigure", {}, function(data, status) {
|
||||
if (status !== "success" || data.status !== 'ok') {
|
||||
showAlert("{{ lang._('Error applying configuration: ') }}" + JSON.stringify(data), "error");
|
||||
} else {
|
||||
showAlert("{{ lang._('Configuration applied successfully.') }}", "success");
|
||||
updateServiceControlUI('caddy');
|
||||
}
|
||||
setSpinner(generalId, 'stop');
|
||||
}).fail(function() {
|
||||
handleError("error", "{{ lang._('Reconfiguration request failed.') }}");
|
||||
setSpinner(generalId, 'stop');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Centralizes error handling.
|
||||
* @param {string} type - The type of error.
|
||||
* @param {string} message - The error message.
|
||||
*/
|
||||
function handleError(type, message) {
|
||||
showAlert(message, type);
|
||||
}
|
||||
|
||||
// Event binding for saving forms
|
||||
$('[id*="save_general-"]').on('click', function() {
|
||||
const generalId = this.form.id;
|
||||
setSpinner(generalId, 'start');
|
||||
saveFormToEndpoint("/api/caddy/general/set", generalId, function() {
|
||||
// Validate Caddyfile after saving the form
|
||||
ajaxGet("/api/caddy/service/validate", {}, function(data, status) {
|
||||
if (status === "success" && data && data.status.toLowerCase() === 'ok') {
|
||||
reconfigureService(generalId);
|
||||
} else {
|
||||
handleError("error", data.message || "{{ lang._('Validation Error') }}");
|
||||
setSpinner(generalId, 'stop');
|
||||
}
|
||||
}).fail(function() {
|
||||
handleError("error", "{{ lang._('Validation request failed.') }}");
|
||||
setSpinner(generalId, 'stop');
|
||||
});
|
||||
}, true, function(errorData) {
|
||||
handleError("error", errorData.message || "{{ lang._('Validation Error') }}");
|
||||
setSpinner(generalId, 'stop');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.error-message {
|
||||
color: #E55451;
|
||||
margin-left: 10px; /* Adjust spacing to the right of the input field */
|
||||
}
|
||||
|
||||
.form-control.error-input {
|
||||
border: 1px solid #E55451;
|
||||
padding: 2px 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.error-label {
|
||||
color: #E55451;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#generalTab">{{ lang._('General') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#dnsProviderTab">{{ lang._('DNS Provider') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#dynamicDnsTab">{{ lang._('Dynamic DNS') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#authProviderTab">{{ lang._('Auth Provider') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#logSettingsTab">{{ lang._('Log Settings') }}</a></li>
|
||||
<!-- General Tab -->
|
||||
<ul id="generalTabsHeader" class="nav nav-tabs" role="tablist">
|
||||
{{ partial("layout_partials/base_tabs_header", ['formData': generalForm]) }}
|
||||
</ul>
|
||||
|
||||
<!-- Tab Content
|
||||
Note: Do not copy this tab layout, it uses the same form id "frm_GeneralSettings". It works, but has problems with validation messages.
|
||||
To fix this issue a custom displayValidationErrors function is used, that appends the messages to all keys. -->
|
||||
<div class="tab-content content-box">
|
||||
<!-- General Tab -->
|
||||
<div id="generalTab" class="tab-pane fade in active">
|
||||
{{ partial("layout_partials/base_form", ['fields': generalForm, 'action': '/ui/caddy/general', 'id': 'frm_GeneralSettings']) }}
|
||||
</div>
|
||||
<!-- DNS Provider Tab -->
|
||||
<div id="dnsProviderTab" class="tab-pane fade">
|
||||
{{ partial("layout_partials/base_form", ['fields': dnsproviderForm, 'action': '/ui/caddy/general', 'id': 'frm_GeneralSettings']) }}
|
||||
</div>
|
||||
<!-- Dynamic DNS Tab -->
|
||||
<div id="dynamicDnsTab" class="tab-pane fade">
|
||||
{{ partial("layout_partials/base_form", ['fields': dynamicdnsForm, 'action': '/ui/caddy/general', 'id': 'frm_GeneralSettings']) }}
|
||||
</div>
|
||||
<!-- Auth Provider Tab -->
|
||||
<div id="authProviderTab" class="tab-pane fade">
|
||||
{{ partial("layout_partials/base_form", ['fields': authproviderForm, 'action': '/ui/caddy/general', 'id': 'frm_GeneralSettings']) }}
|
||||
</div>
|
||||
<!-- Log Settings Tab -->
|
||||
<div id="logSettingsTab" class="tab-pane fade">
|
||||
{{ partial("layout_partials/base_form", ['fields': logsettingsForm, 'action': '/ui/caddy/general', 'id': 'frm_GeneralSettings']) }}
|
||||
<div id="generalTabsContent" class="content-box tab-content">
|
||||
{{ partial("layout_partials/base_tabs_content", ['formData': generalForm]) }}
|
||||
<!-- Message Area for error/success messages -->
|
||||
<div style="max-width: 98%; margin: 10px auto;">
|
||||
<div id="messageArea" class="alert alert-info" style="display: none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="page-content-main">
|
||||
<div class="content-box">
|
||||
<div class="col-md-12">
|
||||
<br/>
|
||||
<!-- Reconfigure Button with Pre-Action -->
|
||||
<button class="btn btn-primary" id="reconfigureAct"
|
||||
data-endpoint="/api/caddy/service/reconfigure"
|
||||
data-label="{{ lang._('Apply') }}"
|
||||
data-error-title="{{ lang._('Error reconfiguring Caddy') }}"
|
||||
type="button"
|
||||
></button>
|
||||
<button class="btn btn-primary" id="saveSettings"
|
||||
data-endpoint="/api/caddy/general/set"
|
||||
data-label="{{ lang._('Save') }}"
|
||||
type="button"
|
||||
style="margin-left: 2px;"
|
||||
>{{ lang._('Save') }}</button>
|
||||
<br/><br/>
|
||||
<!-- Message Area for error/success messages -->
|
||||
<div id="messageArea" class="alert alert-info" style="display: none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -26,16 +26,6 @@
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// Function to handle the search filter request modification
|
||||
function addDomainFilterToRequest(request) {
|
||||
let selectedDomains = $('#reverseFilter').val();
|
||||
if (selectedDomains && selectedDomains.length > 0) {
|
||||
request['reverseUuids'] = selectedDomains.join(',');
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
// Bootgrid Setup
|
||||
$("#reverseProxyGrid").UIBootgrid({
|
||||
search:'/api/caddy/ReverseProxy/searchReverseProxy/',
|
||||
@@ -106,24 +96,124 @@
|
||||
del:'/api/caddy/ReverseProxy/delHeader/',
|
||||
});
|
||||
|
||||
// Function to show alerts in the HTML message area
|
||||
/**
|
||||
* Modifies the search request to include domain filter.
|
||||
*
|
||||
* @param {Object} request - The original request object.
|
||||
* @returns {Object} The modified request object with domain filter.
|
||||
*/
|
||||
function addDomainFilterToRequest(request) {
|
||||
let selectedDomains = $('#reverseFilter').val();
|
||||
if (selectedDomains && selectedDomains.length > 0) {
|
||||
request['reverseUuids'] = selectedDomains.join(',');
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an alert message to the user.
|
||||
*
|
||||
* @param {string} message - The message to display.
|
||||
* @param {string} [type="error"] - The type of alert (error or success).
|
||||
*/
|
||||
function showAlert(message, type = "error") {
|
||||
let alertClass = type === "error" ? "alert-danger" : "alert-success";
|
||||
let messageArea = $("#messageArea");
|
||||
const alertClass = type === "error" ? "alert-danger" : "alert-success";
|
||||
const messageArea = $("#messageArea");
|
||||
|
||||
// Stop any current animation, clear the queue, and immediately hide the element
|
||||
messageArea.stop(true, true).hide();
|
||||
|
||||
// Now set the class and message
|
||||
messageArea.removeClass("alert-success alert-danger").addClass(alertClass).html(message);
|
||||
|
||||
// Use fadeIn to make the message appear smoothly, then fadeOut after a delay
|
||||
messageArea.fadeIn(500).delay(15000).fadeOut(500, function() {
|
||||
// Clear the message after fading out to ensure it's clean for the next message
|
||||
$(this).html('');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads domain filters from the server and populates the filter dropdown.
|
||||
*/
|
||||
function loadDomainFilters() {
|
||||
ajaxGet('/api/caddy/ReverseProxy/getAllReverseDomains', null, function(data, status) {
|
||||
let select = $('#reverseFilter');
|
||||
select.empty(); // Clear current options
|
||||
if (status === "success" && data && data.rows) {
|
||||
data.rows.forEach(function(item) {
|
||||
select.append($('<option>').val(item.id).text(item.domainPort));
|
||||
});
|
||||
} else {
|
||||
select.html('<option value="">{{ lang._('Failed to load data') }}</option>');
|
||||
}
|
||||
select.selectpicker('refresh'); // Refresh selectpicker to update the UI
|
||||
}).fail(function() {
|
||||
$('#reverseFilter').html('<option value="">{{ lang._('Failed to load data') }}</option>').selectpicker('refresh');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the visibility of the selectpicker for domain filtering.
|
||||
*
|
||||
* @param {string} tab - The currently active tab.
|
||||
*/
|
||||
function toggleSelectPicker(tab) {
|
||||
if (tab === 'handlesTab' || tab === 'domainsTab' || tab === 'subdomainsTab') {
|
||||
$('.common-filter').show();
|
||||
} else {
|
||||
$('.common-filter').hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the visibility of add buttons based on the active tab.
|
||||
*
|
||||
* @param {string} tab - The currently active tab.
|
||||
*/
|
||||
function toggleButtonVisibility(tab) {
|
||||
if (tab === 'handlesTab' || tab === 'domainsTab') {
|
||||
$("#addDomainBtn").show();
|
||||
$("#addHandleBtn").show();
|
||||
} else {
|
||||
$("#addDomainBtn").hide();
|
||||
$("#addHandleBtn").hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes tabs by fetching data and setting visibility.
|
||||
*/
|
||||
function initializeTabs() {
|
||||
ajaxGet('/api/caddy/reverse_proxy/get', null, function(response, status) {
|
||||
if (status === "success" && response) {
|
||||
// Check for wildcards in domains to toggle Subdomains tab
|
||||
const hasWildcard = Object.values(response.caddy.reverseproxy.reverse).some(entry => entry.FromDomain.startsWith('*'));
|
||||
toggleTabVisibility('#tab-subdomains', hasWildcard);
|
||||
|
||||
// Check if Layer 4 is enabled to toggle the Layer 4 tab
|
||||
const enableLayer4 = response.caddy.general.EnableLayer4 === '1';
|
||||
toggleTabVisibility('#tab-layer4', enableLayer4);
|
||||
} else {
|
||||
showAlert("{{ lang._('Failed to load data from /api/caddy/reverse_proxy/get') }}", "error");
|
||||
}
|
||||
}).fail(function() {
|
||||
showAlert("{{ lang._('Failed to load data from /api/caddy/reverse_proxy/get') }}", "error");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of a specific tab.
|
||||
*
|
||||
* @param {string} tabSelector - The jQuery selector for the tab.
|
||||
* @param {boolean} visible - Whether the tab should be visible.
|
||||
*/
|
||||
function toggleTabVisibility(tabSelector, visible) {
|
||||
let tab = $(tabSelector);
|
||||
if (visible) {
|
||||
tab.show();
|
||||
} else {
|
||||
tab.hide();
|
||||
if (tab.hasClass('active')) {
|
||||
$('#tab-domains a').tab('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide message area when starting new actions
|
||||
$('input, select, textarea').on('change', function() {
|
||||
$("#messageArea").hide();
|
||||
@@ -135,25 +225,19 @@
|
||||
const dfObj = new $.Deferred();
|
||||
|
||||
// Perform configuration validation
|
||||
$.ajax({
|
||||
url: "/api/caddy/service/validate",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
if (data && data['status'].toLowerCase() === 'ok') {
|
||||
// If configuration is valid, resolve the Deferred object to proceed
|
||||
dfObj.resolve();
|
||||
} else {
|
||||
// If configuration is invalid, show alert and reject the Deferred object
|
||||
showAlert(data['message'], "{{ lang._('Validation Failed') }}");
|
||||
dfObj.reject();
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// On AJAX error, show alert and reject the Deferred object
|
||||
showAlert("{{ lang._('Validation request failed: ') }}" + error, "{{ lang._('Error') }}");
|
||||
ajaxGet("/api/caddy/service/validate", null, function(data, status) {
|
||||
if (status === "success" && data && data['status'].toLowerCase() === 'ok') {
|
||||
// If configuration is valid, resolve the Deferred object to proceed
|
||||
dfObj.resolve();
|
||||
} else {
|
||||
// If configuration is invalid, show alert and reject the Deferred object
|
||||
showAlert(data['message'], "error");
|
||||
dfObj.reject();
|
||||
}
|
||||
}).fail(function(xhr, status, error) {
|
||||
// On AJAX error, show alert and reject the Deferred object
|
||||
showAlert("{{ lang._('Validation request failed: ') }}" + error, "error");
|
||||
dfObj.reject();
|
||||
});
|
||||
|
||||
return dfObj.promise();
|
||||
@@ -165,39 +249,13 @@
|
||||
// Update the service control UI for 'caddy'
|
||||
updateServiceControlUI('caddy');
|
||||
// Update the Tab visibility
|
||||
initializeTabs()
|
||||
initializeTabs();
|
||||
} else {
|
||||
console.error("{{ lang._('Action was not successful or an error occurred:') }}", data);
|
||||
showAlert("{{ lang._('Action was not successful or an error occurred.') }}", "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize the service control UI for 'caddy'
|
||||
updateServiceControlUI('caddy');
|
||||
|
||||
// Filter function for domains
|
||||
function loadDomainFilters() {
|
||||
$.ajax({
|
||||
url: '/api/caddy/ReverseProxy/getAllReverseDomains', // custom API endpoint to get uuid and domainport combinations
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
let select = $('#reverseFilter');
|
||||
select.empty(); // Clear current options
|
||||
if (data && data.rows) {
|
||||
data.rows.forEach(function(item) {
|
||||
select.append($('<option>').val(item.id).text(item.domainPort));
|
||||
});
|
||||
}
|
||||
select.selectpicker('refresh'); // Refresh selectpicker to update the UI
|
||||
},
|
||||
error: function() {
|
||||
$('#reverseFilter').html('<option value="">{{ lang._('Failed to load data') }}</option>').selectpicker('refresh');
|
||||
}
|
||||
});
|
||||
}
|
||||
loadDomainFilters();
|
||||
|
||||
// Reload Bootgrid on filter change
|
||||
$('#reverseFilter').on('changed.bs.select', function() {
|
||||
$("#reverseProxyGrid").bootgrid("reload");
|
||||
@@ -205,23 +263,16 @@
|
||||
$("#reverseHandleGrid").bootgrid("reload");
|
||||
});
|
||||
|
||||
// Control the visibility of selectpicker for filter by domain
|
||||
function toggleSelectPicker(tab) {
|
||||
if (tab === 'handlesTab' || tab === 'domainsTab' || tab === 'subdomainsTab') {
|
||||
$('.common-filter').show();
|
||||
} else {
|
||||
$('.common-filter').hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize visibility based on the active tab on page load
|
||||
let activeTab = $('#maintabs .active a').attr('href').replace('#', '');
|
||||
toggleSelectPicker(activeTab);
|
||||
toggleButtonVisibility(activeTab);
|
||||
|
||||
// Change event when switching tabs
|
||||
$('#maintabs a').on('click', function (e) {
|
||||
let currentTab = $(this).attr('href').replace('#', '');
|
||||
toggleSelectPicker(currentTab);
|
||||
toggleButtonVisibility(currentTab);
|
||||
});
|
||||
|
||||
// Add click event listener for "Add HTTP Handler" button
|
||||
@@ -248,50 +299,17 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Perform an API call to get data and check tabs' visibility on initial load
|
||||
function initializeTabs() {
|
||||
$.ajax({
|
||||
url: '/api/caddy/reverse_proxy/get',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
// Check for wildcards in domains to toggle Subdomains tab
|
||||
const hasWildcard = Object.values(response.caddy.reverseproxy.reverse).some(entry => entry.FromDomain.startsWith('*'));
|
||||
toggleTabVisibility('#tab-subdomains', hasWildcard);
|
||||
|
||||
// Check if Layer 4 is enabled to toggle the Layer 4 tab
|
||||
const enableLayer4 = response.caddy.general.EnableLayer4 === '1';
|
||||
toggleTabVisibility('#tab-layer4', enableLayer4);
|
||||
},
|
||||
error: function() {
|
||||
console.error("{{ lang._('Failed to load data from /api/caddy/reverse_proxy/get') }}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Generic function to show or hide a tab and switch to another tab if the current one is hidden
|
||||
function toggleTabVisibility(tabSelector, visible) {
|
||||
let tab = $(tabSelector);
|
||||
if (visible) {
|
||||
tab.show();
|
||||
} else {
|
||||
tab.hide();
|
||||
// Switch to 'Domains' tab if the currently active tab is being hidden
|
||||
if (tab.hasClass('active')) {
|
||||
$('#tab-domains a').tab('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize tabs on load
|
||||
// Initialize tabs, service control and filter selectpicker
|
||||
initializeTabs();
|
||||
updateServiceControlUI('caddy');
|
||||
loadDomainFilters();
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.common-filter {
|
||||
text-align: right;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
margin-right: 5px;
|
||||
padding: 0 15px; // Align with the tables
|
||||
@@ -302,6 +320,10 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.nav-tabs a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
@@ -358,7 +380,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="addReverseProxyBtn" data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<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>
|
||||
@@ -394,7 +416,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="addSubdomainBtn" data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<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>
|
||||
@@ -441,7 +463,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="addReverseHandleBtn" data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<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>
|
||||
@@ -476,7 +498,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="addAccessListBtn" data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<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>
|
||||
@@ -504,7 +526,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="addBasicAuthBtn" data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<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>
|
||||
@@ -537,7 +559,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="addReverseHeaderBtn" data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<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>
|
||||
@@ -574,7 +596,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="addReverseLayer4Btn" data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<button id="addReverseLayer4Btn" 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>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
{% endif %}
|
||||
{% if dnsSecretApiKey %}secret_access_key {{ dnsSecretApiKey }}
|
||||
{% endif %}
|
||||
{% if dnsOptionalField1 %}max_retries {{ dnsOptionalField1 }}
|
||||
{% if dnsOptionalField1 %}hosted_zone_id {{ dnsOptionalField1 }}
|
||||
{% endif %}
|
||||
{% if dnsOptionalField2 %}profile {{ dnsOptionalField2 }}
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user