mirror of
https://github.com/opnsense/plugins.git
synced 2026-05-28 04:34:15 -04:00
www/caddy: Add buttons to guide users, conditionally hide subdomain tab, simplify forms (#4102)
* www/caddy: Add buttons that guide the user to the right steps to create a reverse proxy, reducing friction. * www/caddy: Cleanup the buttons and event listener. * www/caddy: Move Subdomains to own tab and conditionally hide it when no wildcard domain is configured. Improve the wizard buttons that guide beginners. * www/caddy: Remove stray comma. * www/caddy: Add changelog. Wrap console error into lang function. * www/caddy: Add css to make headers of bootgrids nicer. * www/caddy: Focus the most used forms on the essential options to get a reverse proxy working. Hide advanced options. * www/caddy: Move handle options out of advanced options into own header to make them better discoverable by user. * www/caddy: Move TLS Insecure Skip Verify into plain sight, improving help text. This option is one of the most used ones. * www/caddy: Fixed wrong variable assignement * www/caddy: Hide obscurer options in the main general settings tab, focusing only on the essentials.
This commit is contained in:
parent
32ba9bfe23
commit
8514b05cac
5 changed files with 110 additions and 28 deletions
|
|
@ -30,6 +30,8 @@ Plugin Changelog
|
|||
Add: Run Caddy as "www" user and group, by enabling "Disable Superuser" in General Settings.
|
||||
Cleanup: Validations in general.volt are all appended to their form keys, instead of triggering a Bootstrap Dialog.
|
||||
Change: Description Fields are optional now, reducing the steps needed to create new entries.
|
||||
Add: Shortcuts to add new Domains and Handlers.
|
||||
Change: Subdomain Tab only appears when a wildcard domain has been configured.
|
||||
|
||||
1.6.0
|
||||
|
||||
|
|
|
|||
|
|
@ -17,12 +17,22 @@
|
|||
<type>dropdown</type>
|
||||
<help><![CDATA[Select a subdomain to handle. Make sure to additionaly choose a wildcard domain as "Domain". Leave unset, if not using subdomains.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.description</id>
|
||||
<label>Description</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter a description for this handler.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Handle</label>
|
||||
<collapse>true</collapse>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.HandleType</id>
|
||||
<label>Handle Type</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Choose a handling directive. "handle" (default) will keep the URI of "Handle URI" in all requests. "handle_path" will strip the URI of "Handle URI" from all requests.]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.HandlePath</id>
|
||||
|
|
@ -30,12 +40,6 @@
|
|||
<type>text</type>
|
||||
<help><![CDATA[Enter an URI to handle. Choose a pattern like "/*" or "/example/*". Leave empty to catch all URIs (recommended). Any request matching this pattern will be handled.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.description</id>
|
||||
<label>Description</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Enter a description for this handler.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Access</label>
|
||||
|
|
@ -71,7 +75,7 @@
|
|||
<style>tokenize</style>
|
||||
<allownew>true</allownew>
|
||||
<hint>192.168.1.1</hint>
|
||||
<help><![CDATA[Enter a domain name or IP address of the upstream destination. If multiple are chosen, they will be load balanced with the default random policy. A health check can be set in "Load Balancing"]]></help>
|
||||
<help><![CDATA[Enter a domain name or IP address of the upstream destination. If multiple are chosen, they will be load balanced with the default random policy. A health check can be activated by populating "Upstream Fail Duration".]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.ToPort</id>
|
||||
|
|
@ -87,6 +91,17 @@
|
|||
<help><![CDATA[Enter a path prefix like "/guacamole" that should be prepended to the upstream request because the application demands it.]]></help>
|
||||
<advanced>true</advanced>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.HttpTlsInsecureSkipVerify</id>
|
||||
<label>TLS Insecure Skip Verify</label>
|
||||
<type>checkbox</type>
|
||||
<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>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Health Check</label>
|
||||
<collapse>true</collapse>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.PassiveHealthFailDuration</id>
|
||||
<label>Upstream Fail Duration</label>
|
||||
|
|
@ -128,12 +143,6 @@
|
|||
<type>checkbox</type>
|
||||
<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>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.HttpTlsInsecureSkipVerify</id>
|
||||
<label>TLS Insecure Skip Verify</label>
|
||||
<type>checkbox</type>
|
||||
<help><![CDATA[Disable the TLS handshake verification, making the connection insecure and vulnerable to man-in-the-middle attacks. Do not use in production if possible. When having issues with an upstream that only accepts TLS connections, enabling this option can mitigate them.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>handle.HttpTlsTrustedCaCerts</id>
|
||||
<label>TLS Trust Pool</label>
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@
|
|||
<label>Domain</label>
|
||||
<type>text</type>
|
||||
<hint>example.com</hint>
|
||||
<help><![CDATA[Enter a domain name. For a base domain, use "example.com" or "opn.example.com". Using a base domain enables automatic "Let's Encrypt" and "ZeroSSL" certificates by default. For a wildcard domain, use "*.example.com". Only use wildcard domains with wildcard certificates, which require a DNS Provider.]]></help>
|
||||
<help><![CDATA[Enter a domain name. For a base domain, use "example.com" or "opn.example.com". Using a base domain enables automatic "Let's Encrypt" and "ZeroSSL" certificates by default. For a wildcard domain, use "*.example.com". Only use wildcard domains with wildcard certificates, which require a DNS Provider. Adding a wildcard domain and pressing "Apply" will activate the "Subdomain" Tab, in which subdomains can be configured.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>reverse.FromPort</id>
|
||||
<label>Port</label>
|
||||
<type>text</type>
|
||||
<hint>443</hint>
|
||||
<help><![CDATA[Leave empty to use ports 80 and 443 with automatic redirection from HTTP to HTTPS or choose a custom port. Don't forget to allow these ports with a Firewall rule.]]></help>
|
||||
<help><![CDATA[Leave empty to use ports 80 and 443 with automatic redirection from HTTP to HTTPS or choose a custom port. Don't forget to allow these ports with a Firewall rule. If the default ports have been changed in "General Settings", leaving this empty will use the chosen alternative ports instead.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>reverse.description</id>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
<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>
|
||||
|
|
@ -59,5 +60,6 @@
|
|||
<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>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@
|
|||
// Update only the service control UI for 'caddy'
|
||||
showAlert("{{ lang._('Configuration applied successfully.') }}", "{{ lang._('Apply Success') }}");
|
||||
updateServiceControlUI('caddy');
|
||||
checkAndToggleSubdomainsTab();
|
||||
} else {
|
||||
console.error("{{ lang._('Action was not successful or an error occurred:') }}", data);
|
||||
}
|
||||
|
|
@ -196,7 +197,7 @@
|
|||
|
||||
// Control the visibility of selectpicker for filter by domain
|
||||
function toggleSelectPicker(tab) {
|
||||
if (tab === 'handlesTab' || tab === 'domainsTab') {
|
||||
if (tab === 'handlesTab' || tab === 'domainsTab' || tab === 'subdomainsTab') {
|
||||
$('.common-filter').show();
|
||||
} else {
|
||||
$('.common-filter').hide();
|
||||
|
|
@ -213,6 +214,61 @@
|
|||
toggleSelectPicker(currentTab);
|
||||
});
|
||||
|
||||
// Add click event listener for "Add Upstream" button
|
||||
$("#addHandleBtn").on("click", function() {
|
||||
if ($('#maintabs .active a').attr('href') === "#handlesTab") {
|
||||
// Directly open the dialog if already in the Handles tab
|
||||
$("#addReverseHandleBtn").click();
|
||||
} else {
|
||||
// Switch to the "Handlers" tab if not already there
|
||||
$('#maintabs a[href="#handlesTab"]').tab('show').one('shown.bs.tab', function(e) {
|
||||
$("#addReverseHandleBtn").click();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add click event listener for "Add Domain" button
|
||||
$("#addDomainBtn").on("click", function() {
|
||||
if ($('#maintabs .active a').attr('href') === "#domainsTab") {
|
||||
$("#addReverseProxyBtn").click();
|
||||
} else {
|
||||
$('#maintabs a[href="#domainsTab"]').tab('show').one('shown.bs.tab', function(e) {
|
||||
$("#addReverseProxyBtn").click();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Check and set the visibility of the Subdomains tab on initial load
|
||||
checkAndToggleSubdomainsTab();
|
||||
|
||||
// Function to check and toggle Subdomains tab
|
||||
function checkAndToggleSubdomainsTab() {
|
||||
$.ajax({
|
||||
url: '/api/caddy/ReverseProxy/getAllReverseDomains',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
let hasWildcard = response.rows.some(domain => domain.domainPort.startsWith('*'));
|
||||
toggleSubdomainsTab(hasWildcard);
|
||||
},
|
||||
error: function() {
|
||||
console.error("{{ lang._('Failed to load domain data from getAllReverseDomains') }}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Function to show or hide the Subdomains tab
|
||||
function toggleSubdomainsTab(visible) {
|
||||
let subdomainsTab = $('#maintabs a[href="#subdomainsTab"]').parent();
|
||||
if (visible) {
|
||||
subdomainsTab.show();
|
||||
} else {
|
||||
subdomainsTab.hide();
|
||||
if (subdomainsTab.hasClass('active')) {
|
||||
$('#maintabs a[href="#domainsTab"]').tab('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -223,20 +279,30 @@
|
|||
margin-right: 5px;
|
||||
padding: 0 15px; // Align with the tables
|
||||
}
|
||||
|
||||
.custom-header {
|
||||
font-weight: 800;
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#domainsTab">{{ lang._('Domains') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#subdomainsTab">{{ lang._('Subdomains') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#handlesTab">{{ lang._('Handlers') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#accessTab">{{ lang._('Access') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#headerTab">{{ lang._('Headers') }}</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content content-box">
|
||||
|
||||
<!-- Selectpicker for filter by domain -->
|
||||
<div class="form-group common-filter">
|
||||
<!-- Container using flexbox -->
|
||||
<div class="form-group common-filter" style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<!-- Button group on the left -->
|
||||
<div>
|
||||
<button id="addDomainBtn" type="button" class="btn btn-secondary">{{ lang._('Step 1: Add Domain') }}</button>
|
||||
<button id="addHandleBtn" type="button" class="btn btn-secondary">{{ lang._('Step 2: Add Upstream') }}</button>
|
||||
</div>
|
||||
<!-- Selectpicker on the right -->
|
||||
<select id="reverseFilter" class="selectpicker form-control" multiple data-live-search="true" data-width="348px" data-size="7" title="{{ lang._('Filter by Domain') }}">
|
||||
<!-- Options will be populated dynamically using JavaScript/Ajax -->
|
||||
</select>
|
||||
|
|
@ -246,7 +312,7 @@
|
|||
<div id="domainsTab" class="tab-pane fade in active">
|
||||
<div style="padding-left: 16px;">
|
||||
<!-- Reverse Proxy -->
|
||||
<h1>{{ lang._('Domains') }}</h1>
|
||||
<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>
|
||||
|
|
@ -281,9 +347,12 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Subdomains Tab -->
|
||||
<div id="subdomainsTab" class="tab-pane fade">
|
||||
<div style="padding-left: 16px;">
|
||||
<!-- Subdomains -->
|
||||
<h1>{{ lang._('Subdomains') }}</h1>
|
||||
<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>
|
||||
|
|
@ -319,7 +388,7 @@
|
|||
<!-- Handle Tab -->
|
||||
<div id="handlesTab" class="tab-pane fade">
|
||||
<div style="padding-left: 16px;">
|
||||
<h1>{{ lang._('Handlers') }}</h1>
|
||||
<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>
|
||||
|
|
@ -367,7 +436,7 @@
|
|||
<div id="accessTab" class="tab-pane fade">
|
||||
<!-- Access Lists Section -->
|
||||
<div style="padding-left: 16px;">
|
||||
<h1>{{ lang._('Access Lists') }}</h1>
|
||||
<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>
|
||||
|
|
@ -399,7 +468,7 @@
|
|||
|
||||
<!-- Basic Auth Section -->
|
||||
<div style="padding-left: 16px;">
|
||||
<h1>{{ lang._('Basic Auth') }}</h1>
|
||||
<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>
|
||||
|
|
@ -429,7 +498,7 @@
|
|||
<!-- Header Tab -->
|
||||
<div id="headerTab" class="tab-pane fade">
|
||||
<div style="padding-left: 16px;">
|
||||
<h1>{{ lang._('Headers') }}</h1>
|
||||
<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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue