From 037eb820f050d2ee44e9c712ed4f6da34df8ad35 Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 13:03:31 +0200 Subject: [PATCH 01/28] feat(ui): add i18n strings for frontend proxies settings Add the action titles for get-trusted-proxies and set-trusted-proxies, the settings_http_routes keys of the new tab and a generic common.add label. --- core/ui/public/i18n/en/translation.json | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/core/ui/public/i18n/en/translation.json b/core/ui/public/i18n/en/translation.json index 8330ac4fc..cc96c3206 100644 --- a/core/ui/public/i18n/en/translation.json +++ b/core/ui/public/i18n/en/translation.json @@ -18,6 +18,7 @@ "clear_search": "Clear search", "settings": "Settings", "cancel": "Cancel", + "add": "Add", "node": "Node", "nodes_c": "Node | Nodes", "version": "Version", @@ -402,6 +403,8 @@ "set-route": "Set route", "delete-route": "Delete route", "list-routes": "List routes", + "get-trusted-proxies": "Retrieve frontend proxies", + "set-trusted-proxies": "Configure frontend proxies", "set-certificate": "Set certificate", "set-default-certificate": "Request certificate", "delete-certificate": "Delete certificate", @@ -844,7 +847,27 @@ "show_logs": "Show logs", "traefik_will_be_restarted": "Traefik will be restarted", "delete_route_with_certificate_message": "Deleting this route will briefly disconnect HTTP clients connected to {node}.", - "revoke_certificate_message": "Revoking the route certificate will briefly disconnect HTTP clients connected to {node}." + "revoke_certificate_message": "Revoking the route certificate will briefly disconnect HTTP clients connected to {node}.", + "routes": "Routes", + "frontend_proxies": "Frontend proxies", + "frontend_proxies_description": "If Traefik runs behind another HTTP proxy, such as an external load balancer or a CDN, configure here the proxy addresses to trust: the real client IP address is read from the X-Forwarded-For header only for requests coming from these addresses.", + "proxies": "Frontend proxies", + "depth": "Trust depth", + "add_frontend_proxy": "Add frontend proxy", + "edit_frontend_proxy_node": "Edit frontend proxies of {node}", + "delete_frontend_proxy_node": "Delete frontend proxies of {node}", + "delete_frontend_proxy_description": "Delete the frontend proxies of {node}? The real client IP address won't be read from the X-Forwarded-For header anymore, and the trust depth will be reset to 0.", + "no_frontend_proxy": "No frontend proxy", + "no_frontend_proxy_description": "There is no node configured to run behind a frontend HTTP proxy", + "search_frontend_proxy": "Search node", + "frontend_proxy_node_helper": "Select the node running behind a frontend HTTP proxy", + "frontend_proxies_placeholder": "E.g. 192.168.1.10", + "frontend_proxies_helper": "Enter one IPv4 or IPv6 address per line. CIDR ranges are not supported", + "trust_depth": "Trust depth", + "trust_depth_helper": "Number of frontend proxy levels to trust", + "trust_depth_invalid": "Enter a whole number greater than or equal to 0", + "bad_ip_address": "Invalid IP address. Enter one IPv4 or IPv6 address per line, without CIDR prefix", + "set_frontend_proxy_restart_message": "Saving will briefly disconnect HTTP clients connected to {node}." }, "settings_tls_certificates": { "title": "TLS certificates", From 4d92bcb6b76fa043e6f0a635a60b1594bb5d7c53 Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 13:03:31 +0200 Subject: [PATCH 02/28] feat(ui): add frontend proxy configuration modal Let the operator set the frontend proxy addresses and the trust depth of a node. Accept bare IPv4 and IPv6 addresses only, since the traefik set-trusted-proxies action validates them with ipaddress.ip_address() and rejects CIDR ranges. Always send both proxies and depth, because omitting depth makes the action reset it to 1. --- .../settings/ConfigureFrontendProxyModal.vue | 339 ++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 core/ui/src/components/settings/ConfigureFrontendProxyModal.vue diff --git a/core/ui/src/components/settings/ConfigureFrontendProxyModal.vue b/core/ui/src/components/settings/ConfigureFrontendProxyModal.vue new file mode 100644 index 000000000..3c1550458 --- /dev/null +++ b/core/ui/src/components/settings/ConfigureFrontendProxyModal.vue @@ -0,0 +1,339 @@ + + + + + + From e7232e2ad76252a54532919d34d1dbaafa6a6478 Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 13:03:31 +0200 Subject: [PATCH 03/28] feat(ui): add frontend proxies tab content List the nodes running behind a frontend HTTP proxy, one row per node, by running get-trusted-proxies on every traefik instance. Nodes without any frontend proxy are hidden. Deleting a row sends an empty proxy list, which also resets the trust depth to 0. --- .../settings/HttpFrontendProxies.vue | 421 ++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 core/ui/src/components/settings/HttpFrontendProxies.vue diff --git a/core/ui/src/components/settings/HttpFrontendProxies.vue b/core/ui/src/components/settings/HttpFrontendProxies.vue new file mode 100644 index 000000000..c7caf8c56 --- /dev/null +++ b/core/ui/src/components/settings/HttpFrontendProxies.vue @@ -0,0 +1,421 @@ + + + + + + From eecdc9858fdaa11ed34415d60c82899f367afd2d Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 13:03:31 +0200 Subject: [PATCH 04/28] feat(ui): add frontend proxies tab to HTTP routes page Move the existing route table into a Routes tab and add a Frontend proxies tab. Track the selected tab in the view query parameter and pass the already discovered traefik instances down to the new tab. --- .../src/views/settings/SettingsHttpRoutes.vue | 405 ++++++++++-------- 1 file changed, 225 insertions(+), 180 deletions(-) diff --git a/core/ui/src/views/settings/SettingsHttpRoutes.vue b/core/ui/src/views/settings/SettingsHttpRoutes.vue index 28c3e1898..aae339715 100644 --- a/core/ui/src/views/settings/SettingsHttpRoutes.vue +++ b/core/ui/src/views/settings/SettingsHttpRoutes.vue @@ -40,6 +40,7 @@ - - - - - - - - - {{ $t("settings_http_routes.create_route") }} - - - - - - - - + + + + + + + + + + @@ -327,6 +356,7 @@ import { import { mapState } from "vuex"; import HttpRouteDetailModal from "@/components/settings/HttpRouteDetailModal.vue"; import CreateOrEditHttpRouteModal from "@/components/settings/CreateOrEditHttpRouteModal.vue"; +import HttpFrontendProxies from "@/components/settings/HttpFrontendProxies.vue"; import _cloneDeep from "lodash/cloneDeep"; import WarningAltFilled16 from "@carbon/icons-vue/es/warning--alt--filled/16"; @@ -335,6 +365,7 @@ export default { components: { HttpRouteDetailModal, CreateOrEditHttpRouteModal, + HttpFrontendProxies, WarningAltFilled16, }, mixins: [ @@ -351,6 +382,7 @@ export default { data() { return { q: { + view: "", selectedNodeId: "", }, tablePage: [], @@ -443,7 +475,20 @@ export default { created() { this.listInstalledModules(); }, + mounted() { + // if no view is set in query params, default to routes tab + if (!this.q.view) { + this.q.view = "routes"; + } + }, methods: { + tabSelected(tabNum) { + if (tabNum == 0) { + this.q.view = "routes"; + } else if (tabNum == 1) { + this.q.view = "frontend-proxies"; + } + }, showCreateRouteModal() { this.isEditingRoute = false; this.isShownCreateOrEditRouteModal = true; From c4d3c48ab4fe4fb22e476255f1e0e066879f236b Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 15:06:58 +0200 Subject: [PATCH 05/28] fix(ui): align frontend proxies strings with the design Use the empty state wording of the design, drop the CIDR example and the word CIDR from the address field, since the traefik set-trusted-proxies action rejects CIDR ranges. Add the trust depth tooltip and the node placeholder, and remove the strings of the description paragraph and of the restart warning. --- core/ui/public/i18n/en/translation.json | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/core/ui/public/i18n/en/translation.json b/core/ui/public/i18n/en/translation.json index cc96c3206..cdb96668a 100644 --- a/core/ui/public/i18n/en/translation.json +++ b/core/ui/public/i18n/en/translation.json @@ -850,24 +850,23 @@ "revoke_certificate_message": "Revoking the route certificate will briefly disconnect HTTP clients connected to {node}.", "routes": "Routes", "frontend_proxies": "Frontend proxies", - "frontend_proxies_description": "If Traefik runs behind another HTTP proxy, such as an external load balancer or a CDN, configure here the proxy addresses to trust: the real client IP address is read from the X-Forwarded-For header only for requests coming from these addresses.", "proxies": "Frontend proxies", "depth": "Trust depth", "add_frontend_proxy": "Add frontend proxy", "edit_frontend_proxy_node": "Edit frontend proxies of {node}", "delete_frontend_proxy_node": "Delete frontend proxies of {node}", "delete_frontend_proxy_description": "Delete the frontend proxies of {node}? The real client IP address won't be read from the X-Forwarded-For header anymore, and the trust depth will be reset to 0.", - "no_frontend_proxy": "No frontend proxy", - "no_frontend_proxy_description": "There is no node configured to run behind a frontend HTTP proxy", - "search_frontend_proxy": "Search node", - "frontend_proxy_node_helper": "Select the node running behind a frontend HTTP proxy", - "frontend_proxies_placeholder": "E.g. 192.168.1.10", - "frontend_proxies_helper": "Enter one IPv4 or IPv6 address per line. CIDR ranges are not supported", + "no_frontend_proxy": "No frontend proxy configured", + "no_frontend_proxy_description": "Add frontend proxy addresses to correctly identify client IPs behind reverse proxies or load balancers.", + "filter_frontend_proxies": "Filter", + "choose_node": "Choose node", + "frontend_proxies_placeholder": "e.g. 192.168.1.10\n2001:db8::1", + "frontend_proxies_helper": "Enter one IPv4 or IPv6 address per line", "trust_depth": "Trust depth", - "trust_depth_helper": "Number of frontend proxy levels to trust", + "trust_depth_helper": "Number of frontend proxy levels", + "trust_depth_tooltip": "Number of frontend proxies between the client and Traefik. With a trust depth of 1, the client IP address is read from the last entry of the X-Forwarded-For header.", "trust_depth_invalid": "Enter a whole number greater than or equal to 0", - "bad_ip_address": "Invalid IP address. Enter one IPv4 or IPv6 address per line, without CIDR prefix", - "set_frontend_proxy_restart_message": "Saving will briefly disconnect HTTP clients connected to {node}." + "bad_ip_address": "Invalid IP address. Enter one IPv4 or IPv6 address per line, without CIDR prefix" }, "settings_tls_certificates": { "title": "TLS certificates", From 3207c82d111ff4a960e81f9695c566f165535eb6 Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 15:06:58 +0200 Subject: [PATCH 06/28] feat(ui): add network pictogram Hold the subpaths of the Carbon network--3 icon, which the design uses for the frontend proxies empty state. The library ships no routing pictogram. --- core/ui/src/components/NetworkPictogram.vue | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 core/ui/src/components/NetworkPictogram.vue diff --git a/core/ui/src/components/NetworkPictogram.vue b/core/ui/src/components/NetworkPictogram.vue new file mode 100644 index 000000000..fdfd83523 --- /dev/null +++ b/core/ui/src/components/NetworkPictogram.vue @@ -0,0 +1,24 @@ + + + + + From c749194e655699c12939f984deb20d2be598b1ed Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 15:06:58 +0200 Subject: [PATCH 07/28] fix(ui): align frontend proxy modal with the design Drop the node tooltip and add one to the trust depth, following the label markup of NsTextInput since cv-number-input has no tooltip slot. Widen the trust depth field so its helper text is no longer clipped, and remove the restart warning. --- .../settings/ConfigureFrontendProxyModal.vue | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/core/ui/src/components/settings/ConfigureFrontendProxyModal.vue b/core/ui/src/components/settings/ConfigureFrontendProxyModal.vue index 3c1550458..46ed1051c 100644 --- a/core/ui/src/components/settings/ConfigureFrontendProxyModal.vue +++ b/core/ui/src/components/settings/ConfigureFrontendProxyModal.vue @@ -22,7 +22,7 @@ - - + /> + +
+ + {{ $t("settings_http_routes.trust_depth") }} + + + + +
-
node.value === this.selectedNodeId); }, - selectedNodeLabel() { - return this.selectedNode ? this.selectedNode.label : ""; - }, }, watch: { isShown: function () { @@ -336,4 +334,14 @@ export default { From 833ac63b13077414e4d8f5aaa200b3160d90289b Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Tue, 28 Jul 2026 15:06:58 +0200 Subject: [PATCH 08/28] fix(ui): align frontend proxies tab with the design Add the filter row with the node dropdown and the clear filters link, and turn the table search off in favour of it. Show the add button as primary, inside the empty state when no node is configured and above the table otherwise. Drop the description paragraph and the restart warning. --- .../settings/HttpFrontendProxies.vue | 183 ++++++++++++++---- 1 file changed, 146 insertions(+), 37 deletions(-) diff --git a/core/ui/src/components/settings/HttpFrontendProxies.vue b/core/ui/src/components/settings/HttpFrontendProxies.vue index c7caf8c56..f0f73cf91 100644 --- a/core/ui/src/components/settings/HttpFrontendProxies.vue +++ b/core/ui/src/components/settings/HttpFrontendProxies.vue @@ -8,15 +8,42 @@ -
- {{ $t("settings_http_routes.frontend_proxies_description") }} +
+ + + + + {{ $t("common.clear_filters") }} +
- + + diff --git a/core/ui/src/components/settings/HttpFrontendProxies.vue b/core/ui/src/components/settings/HttpFrontendProxies.vue index 8bb4e6019..633b86f71 100644 --- a/core/ui/src/components/settings/HttpFrontendProxies.vue +++ b/core/ui/src/components/settings/HttpFrontendProxies.vue @@ -290,13 +290,11 @@ import { UtilService, TaskService, IconService } from "@nethserver/ns8-ui-lib"; import _cloneDeep from "lodash/cloneDeep"; import { mapState } from "vuex"; import ConfigureFrontendProxyModal from "@/components/settings/ConfigureFrontendProxyModal.vue"; -import NetworkPictogram from "@/components/NetworkPictogram.vue"; export default { name: "HttpFrontendProxies", components: { ConfigureFrontendProxyModal, - NetworkPictogram, }, mixins: [TaskService, UtilService, IconService], props: { diff --git a/core/ui/yarn.lock b/core/ui/yarn.lock index fae5d8163..169a8b507 100644 --- a/core/ui/yarn.lock +++ b/core/ui/yarn.lock @@ -3289,9 +3289,9 @@ __metadata: languageName: node linkType: hard -"@nethserver/ns8-ui-lib@npm:^1.12.4": - version: 1.12.4 - resolution: "@nethserver/ns8-ui-lib@npm:1.12.4" +"@nethserver/ns8-ui-lib@npm:^2.0.1": + version: 2.0.1 + resolution: "@nethserver/ns8-ui-lib@npm:2.0.1" dependencies: "@rollup/plugin-json": ^4.1.0 core-js: ^3.15.2 @@ -3300,7 +3300,7 @@ __metadata: vue-date-fns: ^2.0.1 peerDependencies: vue: ^2.6.12 - checksum: 319c09ec851a7391c49da77f1849d09a40da199349e5d797d1dd6d09b688a316f6bcc4cacd0018dc7a2348ccda9f155bf25ceb981720e854dde85490f8c28257 + checksum: 1f87dc7ae9994d8c2ebe6348714f900f39096975be4f24af10b2adfa1c84e53b8782ae379d3c71b19873361d46a1f426cb8db23f7d69ae749059f3db138520a6 languageName: node linkType: hard @@ -14771,7 +14771,7 @@ __metadata: "@carbon/icons-vue": ^10.37.0 "@carbon/themes": ^10.34.0 "@carbon/vue": ^2.40.0 - "@nethserver/ns8-ui-lib": ^1.12.4 + "@nethserver/ns8-ui-lib": ^2.0.1 "@storybook/addon-actions": ^6.2.9 "@storybook/addon-essentials": ^6.2.9 "@storybook/addon-links": ^6.2.9