From 3913456755410861c968880995a240d7b289c8c8 Mon Sep 17 00:00:00 2001 From: Monviech <79600909+Monviech@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:40:35 +0100 Subject: [PATCH] www/caddy: widgets, hide unused certs, improve error handling (#4372) * www/caddy: CaddyCertificate widget, compare certs on disk with configured hostnames and only display relevant ones * www/caddy: Caddy Domain widget improve error handling --- www/caddy/pkg-descr | 2 + .../www/js/widgets/CaddyCertificate.js | 67 ++++++++++--------- .../opnsense/www/js/widgets/CaddyDomain.js | 28 +++++--- .../www/js/widgets/Metadata/Caddy.xml | 1 + 4 files changed, 58 insertions(+), 40 deletions(-) diff --git a/www/caddy/pkg-descr b/www/caddy/pkg-descr index 4f4a41636..378f12b86 100644 --- a/www/caddy/pkg-descr +++ b/www/caddy/pkg-descr @@ -17,6 +17,8 @@ Plugin Changelog * Add: Layer4 TLS Termination * Add: h2c protocol to HTTP Handler +* Cleanup: Caddy Certificate widget hides unused automatic certificates +* Cleanup: Widgets error handling improved * Cleanup: Refactor caddy_certs.php to Trust model 1.7.4 diff --git a/www/caddy/src/opnsense/www/js/widgets/CaddyCertificate.js b/www/caddy/src/opnsense/www/js/widgets/CaddyCertificate.js index ccdf65868..7a3834294 100644 --- a/www/caddy/src/opnsense/www/js/widgets/CaddyCertificate.js +++ b/www/caddy/src/opnsense/www/js/widgets/CaddyCertificate.js @@ -38,8 +38,8 @@ export default class CaddyCertificate extends BaseTableWidget { } getMarkup() { - let $container = $('
'); - let $caddyCertificateTable = this.createTable('caddyCertificateTable', { + const $container = $('
'); + const $caddyCertificateTable = this.createTable('caddyCertificateTable', { headerPosition: 'none' }); @@ -48,49 +48,54 @@ export default class CaddyCertificate extends BaseTableWidget { } async onWidgetTick() { - // Check if Caddy is enabled - const caddyStatus = await this.ajaxCall('/api/caddy/reverse_proxy/get'); - if (!caddyStatus.caddy.general || caddyStatus.caddy.general.enabled === "0") { + const proxyData = await this.ajaxCall('/api/caddy/reverse_proxy/get'); + if (!proxyData.caddy.general || proxyData.caddy.general.enabled === "0") { this.displayError(`${this.translations.unconfigured}`); return; } - // Fetch the certificate details - const response = await this.ajaxCall('/api/caddy/diagnostics/certificate'); - if (response.status !== "success") { + const domains = Object.values(proxyData.caddy.reverseproxy?.reverse || []) + .map(proxy => proxy.FromDomain) + .filter(Boolean); + + const certificates = (await this.ajaxCall('/api/caddy/diagnostics/certificate')).content || []; + + // Display certificate if hostname in config and CN of stored cert on disk match + const matchingCertificates = certificates.filter(cert => domains.includes(cert.hostname)); + + if (matchingCertificates.length === 0) { this.displayError(`${this.translations.nocerts}`); return; } - // Process certificates if the response is successful - this.processCertificates(response.content); + this.clearError(); + this.processCertificates(matchingCertificates); } - // Utility function to display errors within the widget displayError(message) { const $error = $(`
${message}
`); $('#caddyCertificateTable').empty().append($error); } - processCertificates(certificates) { - if (!this.dataChanged('certificates', certificates)) { - return; - } + clearError() { + $('#caddyCertificateTable .error-message').remove(); + } + processCertificates(certificates) { $('.caddy-certificate-tooltip').tooltip('hide'); - let rows = certificates.map(certificate => { - let colorClass = 'text-success'; - if (certificate.remaining_days === 0) { - colorClass = 'text-danger'; - } else if (certificate.remaining_days < 14) { - colorClass = 'text-warning'; - } + const rows = certificates.map(certificate => { + const colorClass = certificate.remaining_days === 0 + ? 'text-danger' + : certificate.remaining_days < 14 + ? 'text-warning' + : 'text-success'; - let statusText = certificate.remaining_days === 0 ? this.translations.expired : - this.translations.valid; + const statusText = certificate.remaining_days === 0 + ? this.translations.expired + : this.translations.valid; - let row = ` + const row = `
@@ -98,19 +103,19 @@ export default class CaddyCertificate extends BaseTableWidget {   ${certificate.hostname}
-
${this.translations.expires} ${certificate.remaining_days} ${this.translations.days}, ${new Date(certificate.expiration_date).toLocaleString()}
+
+ ${this.translations.expires} ${certificate.remaining_days} ${this.translations.days}, + ${new Date(certificate.expiration_date).toLocaleString()} +
`; return { html: row, expirationDate: new Date(certificate.expiration_date) }; }); - // Sort rows by expiration date from lowest to highest rows.sort((a, b) => a.expirationDate - b.expirationDate); - // Extract sorted HTML rows and update table - let sortedRows = rows.map(row => [row.html]); + const sortedRows = rows.map(row => [row.html]); super.updateTable('caddyCertificateTable', sortedRows); - // Initialize tooltips for new elements - $('.caddy-certificate-tooltip').tooltip({container: 'body'}); + $('.caddy-certificate-tooltip').tooltip({ container: 'body' }); } } diff --git a/www/caddy/src/opnsense/www/js/widgets/CaddyDomain.js b/www/caddy/src/opnsense/www/js/widgets/CaddyDomain.js index c544cfe6f..ecfbf0f24 100644 --- a/www/caddy/src/opnsense/www/js/widgets/CaddyDomain.js +++ b/www/caddy/src/opnsense/www/js/widgets/CaddyDomain.js @@ -37,8 +37,8 @@ export default class CaddyDomain extends BaseTableWidget { } getMarkup() { - let $container = $('
'); - let $caddyDomainTable = this.createTable('caddyDomainTable', { + const $container = $('
'); + const $caddyDomainTable = this.createTable('caddyDomainTable', { headerPosition: 'none' }); @@ -55,7 +55,14 @@ export default class CaddyDomain extends BaseTableWidget { } // Process domains if caddy is enabled - let domains = { ...data.caddy.reverseproxy.reverse, ...data.caddy.reverseproxy.subdomain }; + const domains = { ...data.caddy.reverseproxy.reverse, ...data.caddy.reverseproxy.subdomain }; + + if (Object.keys(domains).length === 0) { + this.displayError(`${this.translations.nodomains}`); + return; + } + + this.clearError(); this.processDomains(domains); } @@ -65,6 +72,10 @@ export default class CaddyDomain extends BaseTableWidget { $('#caddyDomainTable').empty().append($error); } + clearError() { + $('#caddyDomainTable .error-message').remove(); + } + processDomains(domains) { if (!this.dataChanged('domains', domains)) { return; @@ -72,19 +83,18 @@ export default class CaddyDomain extends BaseTableWidget { $('.caddy-domain-tooltip').tooltip('hide'); - let rows = []; - // Assuming domains is a combination of both reverse and subdomains + const rows = []; for (const key in domains) { const domain = domains[key]; - let colorClass = domain.enabled === "1" ? 'text-success' : 'text-danger'; - let tooltipText = domain.enabled === "1" ? this.translations.enabled : this.translations.disabled; + const colorClass = domain.enabled === "1" ? 'text-success' : 'text-danger'; + const tooltipText = domain.enabled === "1" ? this.translations.enabled : this.translations.disabled; let domainPort = domain.FromDomain; if (domain.FromPort) { domainPort += `:${domain.FromPort}`; } - let row = $(` + const row = $(`
[row.html])); // Initialize tooltips for interactivity - $('.caddy-domain-tooltip').tooltip({container: 'body'}); + $('.caddy-domain-tooltip').tooltip({ container: 'body' }); } } diff --git a/www/caddy/src/opnsense/www/js/widgets/Metadata/Caddy.xml b/www/caddy/src/opnsense/www/js/widgets/Metadata/Caddy.xml index ac818fd2a..c423f6163 100644 --- a/www/caddy/src/opnsense/www/js/widgets/Metadata/Caddy.xml +++ b/www/caddy/src/opnsense/www/js/widgets/Metadata/Caddy.xml @@ -10,6 +10,7 @@ Enabled Disabled Caddy is disabled or not configured. + Caddy does not manage any domains.