From d89569017d1870d8ff9827fc452610fe7c741568 Mon Sep 17 00:00:00 2001 From: Debian Date: Thu, 28 May 2026 20:36:25 +0200 Subject: [PATCH] feat(firewall): Regel duplizieren + CIDR-Validierung (v1.1.139) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Duplicate-Button (CopyOutlined): klont Regel mit priority+1, disabled=true, Name "(copy)" - CIDR-Felder: Pattern-Validator für IPv4/IPv6-CIDR-Notation Co-Authored-By: Claude Sonnet 4.6 --- VERSION | 2 +- cmd/edgeguard-api/main.go | 2 +- cmd/edgeguard-ctl/main.go | 2 +- cmd/edgeguard-scheduler/main.go | 2 +- management-ui/src/i18n/locales/de/common.json | 3 ++ management-ui/src/i18n/locales/en/common.json | 3 ++ management-ui/src/pages/Firewall/Rules.tsx | 39 +++++++++++++++++-- 7 files changed, 46 insertions(+), 7 deletions(-) diff --git a/VERSION b/VERSION index 93599c6..97cae93 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.138 +1.1.139 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index 6bbb26d..bbf3536 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -60,7 +60,7 @@ import ( usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users" ) -var version = "1.1.138" +var version = "1.1.139" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index c38c290..6e3c408 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -11,7 +11,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" ) -var version = "1.1.138" +var version = "1.1.139" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index 6d4a6ae..d6a5ab8 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -41,7 +41,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" ) -var version = "1.1.138" +var version = "1.1.139" const ( // renewTickInterval — how often we re-evaluate expiring certs. diff --git a/management-ui/src/i18n/locales/de/common.json b/management-ui/src/i18n/locales/de/common.json index fcdf6f2..3bf035c 100644 --- a/management-ui/src/i18n/locales/de/common.json +++ b/management-ui/src/i18n/locales/de/common.json @@ -109,6 +109,9 @@ "cidr": "CIDR", "actions": { "accept": "ACCEPT", "drop": "DROP", "reject": "REJECT" }, "kinds": { "any": "Beliebig", "object": "Objekt", "group": "Gruppe", "cidr": "CIDR" }, + "duplicate": "Regel duplizieren", + "duplicated": "Regel dupliziert (deaktiviert, Priorität +1).", + "cidrInvalid": "Gültiges CIDR eingeben (z.B. 10.0.0.0/24)", "moveUp": "Nach oben (höhere Priorität)", "moveDown": "Nach unten (niedrigere Priorität)", "emptyTitle": "Noch keine eigenen Firewall-Regeln.", diff --git a/management-ui/src/i18n/locales/en/common.json b/management-ui/src/i18n/locales/en/common.json index 5877a6e..57bd4c0 100644 --- a/management-ui/src/i18n/locales/en/common.json +++ b/management-ui/src/i18n/locales/en/common.json @@ -109,6 +109,9 @@ "cidr": "CIDR", "actions": { "accept": "ACCEPT", "drop": "DROP", "reject": "REJECT" }, "kinds": { "any": "Any", "object": "Object", "group": "Group", "cidr": "CIDR" }, + "duplicate": "Duplicate rule", + "duplicated": "Rule duplicated (disabled, priority +1).", + "cidrInvalid": "Enter a valid CIDR (e.g. 10.0.0.0/24)", "moveUp": "Move up (higher priority)", "moveDown": "Move down (lower priority)", "emptyTitle": "No custom firewall rules yet.", diff --git a/management-ui/src/pages/Firewall/Rules.tsx b/management-ui/src/pages/Firewall/Rules.tsx index a503cd0..9425539 100644 --- a/management-ui/src/pages/Firewall/Rules.tsx +++ b/management-ui/src/pages/Firewall/Rules.tsx @@ -7,7 +7,7 @@ import type { ColumnsType } from 'antd/es/table' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { - ArrowDownOutlined, ArrowUpOutlined, EyeOutlined, FireOutlined, PlusOutlined, + ArrowDownOutlined, ArrowUpOutlined, CopyOutlined, EyeOutlined, FireOutlined, PlusOutlined, } from '@ant-design/icons' const { Text } = Typography @@ -233,6 +233,23 @@ export default function RulesTab() { onError: (e: Error) => message.error(e.message), }) + const duplicate = useMutation({ + mutationFn: async (r: FwRule) => { + const { id: _id, created_at: _ca, updated_at: _ua, ...rest } = r as FwRule & { created_at?: unknown; updated_at?: unknown } + await apiClient.post('/firewall/rules', { + ...rest, + name: r.name ? `${r.name} (copy)` : undefined, + priority: r.priority + 1, + enabled: false, + }) + }, + onSuccess: () => { + message.success(t('fw.rule.duplicated')) + void qc.invalidateQueries({ queryKey: ['fw', 'rules'] }) + }, + onError: (e: Error) => message.error(e.message), + }) + const editFromRow = (r: FwRule) => { setEditing(r) form.setFieldsValue({ @@ -376,7 +393,7 @@ export default function RulesTab() { }, }, { - title: '', key: 'actions', width: 100, + title: '', key: 'actions', width: 120, render: (_, row) => ( @@ -384,6 +401,15 @@ export default function RulesTab() { {t('common.edit')} + + @@ -536,7 +562,14 @@ export default function RulesTab() { } if (k === 'cidr') { - return + return }