import { Space, Tag, Tabs } from 'antd' import { CheckCircleOutlined, CloseCircleOutlined, FireOutlined, SafetyOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' import { useQuery } from '@tanstack/react-query' import apiClient, { isEnvelope } from '../../api/client' import PageHeader from '../../components/PageHeader' import AddressObjectsTab from './AddressObjects' import AddressGroupsTab from './AddressGroups' import ServicesTab from './Services' import ServiceGroupsTab from './ServiceGroups' import RulesTab from './Rules' import NATRulesTab from './NATRules' import SystemRulesCard from './SystemRules' import ZonesTab from './Zones' import type { FwRule, FwZone, NATRule } from './types' interface ServiceStatus { label: string; unit: string; active: boolean; state: string } async function listRulesCount(): Promise { try { const r = await apiClient.get('/firewall/rules') if (!isEnvelope(r.data)) return [] return (r.data.data as { rules?: FwRule[] }).rules ?? [] } catch { return [] } } async function listNATCount(): Promise { try { const r = await apiClient.get('/firewall/nat-rules') if (!isEnvelope(r.data)) return [] return (r.data.data as { nat_rules?: NATRule[] }).nat_rules ?? [] } catch { return [] } } async function listZonesCount(): Promise { try { const r = await apiClient.get('/firewall/zones') if (!isEnvelope(r.data)) return [] return (r.data.data as { zones?: FwZone[] }).zones ?? [] } catch { return [] } } function FirewallKPIStrip({ nftablesActive }: { nftablesActive: boolean | undefined }) { const { t } = useTranslation() const { data: rules } = useQuery({ queryKey: ['fw', 'rules'], queryFn: listRulesCount, staleTime: 30_000 }) const { data: natRules } = useQuery({ queryKey: ['fw', 'nat-rules'], queryFn: listNATCount, staleTime: 30_000 }) const { data: zones } = useQuery({ queryKey: ['fw', 'zones'], queryFn: listZonesCount, staleTime: 30_000 }) const totalRules = rules?.length ?? 0 const activeRules = rules?.filter(r => r.enabled).length ?? 0 const totalNAT = natRules?.length ?? 0 const totalZones = zones?.length ?? 0 return (
{t('fw.kpi.policyRules')}
{totalRules}
{activeRules} {t('fw.kpi.active')} · {totalRules - activeRules} {t('fw.kpi.disabled')}
{t('fw.kpi.natRules')}
{totalNAT}
{t('fw.kpi.natHint')}
{t('fw.kpi.zones')}
{totalZones}
{t('fw.kpi.zonesHint')}
{t('fw.kpi.defaultPolicy')}
INPUT DROP
{nftablesActive === true && ● {t('fw.kpi.nftActive')}} {nftablesActive === false && ● {t('fw.kpi.nftInactive')}} {nftablesActive === undefined && ···}
) } export default function FirewallPage() { const { t } = useTranslation() const { data: services } = useQuery({ queryKey: ['system', 'services'], queryFn: async () => { const r = await apiClient.get('/system/services') return isEnvelope(r.data) ? (r.data.data as { services: ServiceStatus[] }).services : [] }, refetchInterval: 30_000, }) const nftables = services?.find(s => s.unit === 'nftables.service' || s.unit === 'nftables') const tabs = [ { key: 'rules', label: {t('fw.tabs.rules')}, children: }, { key: 'nat', label: t('fw.tabs.nat'), 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: }, { key: 'services', label: t('fw.tabs.services'), children: }, { key: 'svcGrp', label: t('fw.tabs.svcGrp'), children: }, { key: 'system', label: t('fw.tabs.system'), children: }, ] return (
} title={t('fw.title')} subtitle={t('fw.intro')} extra={nftables && ( : } color={nftables.active ? 'green' : 'red'} > nftables {nftables.state} )} />
) }