diff --git a/VERSION b/VERSION index c8b4742..01c08cf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.67 +1.0.68 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index f873893..601225a 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -52,7 +52,7 @@ import ( wgsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/wireguard" ) -var version = "1.0.67" +var version = "1.0.68" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index 50193e5..8298191 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -9,7 +9,7 @@ import ( "os" ) -var version = "1.0.67" +var version = "1.0.68" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index d5caefa..50addfc 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -25,7 +25,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" ) -var version = "1.0.67" +var version = "1.0.68" const ( // renewTickInterval — how often we re-evaluate expiring certs. diff --git a/management-ui/src/App.tsx b/management-ui/src/App.tsx index 835ae38..a1ea82c 100644 --- a/management-ui/src/App.tsx +++ b/management-ui/src/App.tsx @@ -25,6 +25,7 @@ const ForwardProxyPage = lazy(() => import('./pages/ForwardProxy')) const DNSPage = lazy(() => import('./pages/DNS')) const NTPPage = lazy(() => import('./pages/NTP')) const ClusterPage = lazy(() => import('./pages/Cluster')) +const FirewallLivePage = lazy(() => import('./pages/FirewallLive')) const LogsPage = lazy(() => import('./pages/Logs')) const BackupsPage = lazy(() => import('./pages/Backups')) const LicensePage = lazy(() => import('./pages/License')) @@ -106,6 +107,7 @@ export default function App() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/management-ui/src/components/Layout/AppLayout.tsx b/management-ui/src/components/Layout/AppLayout.tsx index 48982ee..6fc9603 100644 --- a/management-ui/src/components/Layout/AppLayout.tsx +++ b/management-ui/src/components/Layout/AppLayout.tsx @@ -16,6 +16,8 @@ const PAGE_TITLES: Record = { '/routing-rules': 'nav.routing', '/networks': 'nav.networks', '/ip-addresses': 'nav.ipAddresses', + '/firewall/live': 'nav.firewallLive', + '/firewall': 'nav.firewall', '/cluster': 'nav.cluster', '/logs': 'nav.logs', '/backups': 'nav.backups', diff --git a/management-ui/src/components/Layout/Sidebar.tsx b/management-ui/src/components/Layout/Sidebar.tsx index 2d3531a..d640367 100644 --- a/management-ui/src/components/Layout/Sidebar.tsx +++ b/management-ui/src/components/Layout/Sidebar.tsx @@ -7,6 +7,7 @@ import { ClusterOutlined, CrownOutlined, DashboardOutlined, + EyeOutlined, FileSearchOutlined, DatabaseOutlined, FireOutlined, @@ -27,6 +28,7 @@ interface NavItem { path: string labelKey: string icon: ReactNode + child?: boolean // visuell eingerückt unter dem Parent-Item } interface NavSection { @@ -62,6 +64,7 @@ const NAV: NavSection[] = [ labelKey: 'nav.section.security', items: [ { path: '/firewall', labelKey: 'nav.firewall', icon: }, + { path: '/firewall/live', labelKey: 'nav.firewallLive', icon: , child: true }, { path: '/vpn/wireguard', labelKey: 'nav.wireguard', icon: }, { path: '/forward-proxy', labelKey: 'nav.forwardProxy', icon: }, ], @@ -78,7 +81,7 @@ const NAV: NavSection[] = [ }, ] -const VERSION = '1.0.67' +const VERSION = '1.0.68' // Sidebar-Pattern 1:1 aus netcell-webpanel (enconf) übernommen: // - als root, dunkler Gradient + Teal/Blue-Accent @@ -104,13 +107,23 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) { {section.items.map((item) => { - const isActive = location.pathname === item.path - || location.pathname.startsWith(item.path + '/') + // exact-match → der genauere Pfad gewinnt; sonst würde + // /firewall den /firewall/live-Eintrag als „active" + // mitmarkieren. Sibling-Pfade müssen sich gegenseitig + // ausschließen. + const hasMoreSpecificSibling = section.items.some( + (other) => other.path !== item.path && + other.path.startsWith(item.path + '/'), + ) + const isActive = hasMoreSpecificSibling + ? location.pathname === item.path + : location.pathname === item.path + || location.pathname.startsWith(item.path + '/') + const cls = 'sidebar-menu-item' + + (isActive ? ' active' : '') + + (item.child ? ' sidebar-menu-item--child' : '') return ( - + {item.icon} {t(item.labelKey)} diff --git a/management-ui/src/pages/Firewall/index.tsx b/management-ui/src/pages/Firewall/index.tsx index ce45a15..7e3a825 100644 --- a/management-ui/src/pages/Firewall/index.tsx +++ b/management-ui/src/pages/Firewall/index.tsx @@ -10,7 +10,6 @@ import ServiceGroupsTab from './ServiceGroups' import RulesTab from './Rules' import NATRulesTab from './NATRules' import ZonesTab from './Zones' -import LiveLogTab from './LiveLog' export default function FirewallPage() { const { t } = useTranslation() @@ -18,7 +17,6 @@ export default function FirewallPage() { const tabs = [ { key: 'rules', label: t('fw.tabs.rules'), children: }, { key: 'nat', label: t('fw.tabs.nat'), children: }, - { key: 'live', label: t('fw.tabs.live'), children: }, { key: 'zones', label: t('fw.tabs.zones'), children: }, { key: 'addrObj', label: t('fw.tabs.addrObj'), children: }, { key: 'addrGrp', label: t('fw.tabs.addrGrp'), children: }, diff --git a/management-ui/src/pages/Firewall/LiveLog.tsx b/management-ui/src/pages/FirewallLive/index.tsx similarity index 98% rename from management-ui/src/pages/Firewall/LiveLog.tsx rename to management-ui/src/pages/FirewallLive/index.tsx index d78bd1c..69276b2 100644 --- a/management-ui/src/pages/Firewall/LiveLog.tsx +++ b/management-ui/src/pages/FirewallLive/index.tsx @@ -7,12 +7,15 @@ import { AlertOutlined, ClearOutlined, DownloadOutlined, + EyeOutlined, PauseCircleOutlined, PlayCircleOutlined, PoweroffOutlined, } from '@ant-design/icons' import { useTranslation } from 'react-i18next' +import PageHeader from '../../components/PageHeader' + const { Text } = Typography interface Entry { @@ -105,7 +108,7 @@ function toCSV(rows: Entry[]): string { // DISCONNECTED — der WebSocket wird erst beim Klick auf „Start" // aufgebaut. Stop schließt explizit, Filter-Änderungen reconnecten // nur wenn aktiv. -export default function LiveLogTab() { +export default function FirewallLivePage() { const { t } = useTranslation() const [active, setActive] = useState(false) // Start/Stop master switch @@ -287,6 +290,11 @@ export default function LiveLogTab() { return ( + } + title={t('fwlog.title')} + subtitle={t('fwlog.intro')} + /> {!active ? (