feat(policy): именованные наборы правил с DNS и M:N привязкой к агентам
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m44s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

Правила живут в policy_sets; evaluate мержит назначенные наборы; источник list|CIDR|hostname с кэшем DNS.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-20 22:11:03 +07:00
co-authored by Cursor
parent cad05659b1
commit 0bed4367c7
18 changed files with 1524 additions and 312 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ const overviewNav = [
const opsNav = [
{ to: '/agents', label: 'Агенты', icon: ServerIcon, exact: false },
{ to: '/lists', label: 'Списки IP', icon: ListIcon, exact: false },
{ to: '/rules', label: 'Правила', icon: ShieldIcon, exact: false },
{ to: '/rules', label: 'Наборы правил', icon: ShieldIcon, exact: false },
{ to: '/stats', label: 'Статистика', icon: BarChart3Icon, exact: false },
] as const
@@ -47,8 +47,8 @@ const NAV_ITEMS = [
},
{
to: '/rules',
label: 'Правила',
keywords: ['rules', 'правила', 'policy'],
label: 'Наборы правил',
keywords: ['rules', 'правила', 'policy', 'наборы', 'sets'],
icon: ShieldIcon,
},
{
@@ -22,7 +22,7 @@ const routeTitles: Record<string, string> = {
'/': 'Панель управления',
'/agents': 'Агенты',
'/lists': 'Списки IP',
'/rules': 'Правила',
'/rules': 'Наборы правил',
'/stats': 'Статистика',
'/settings': 'Настройки',
}
@@ -42,6 +42,13 @@ function getBreadcrumbs(
]
}
if (pathname.match(/^\/rules\/[^/]+$/)) {
return [
{ label: 'Наборы правил', href: '/rules' },
{ label: dynamicLabels[pathname] ?? 'Набор', href: pathname },
]
}
const title = routeTitles[pathname]
if (title) {
return [{ label: title, href: pathname }]
@@ -11,7 +11,7 @@ import {
agentsQueryOptions,
dashboardQueryOptions,
listsQueryOptions,
rulesQueryOptions,
policySetsQueryOptions,
} from '@/queries'
type MonitorMetric = {
@@ -80,7 +80,10 @@ export function SystemMonitorPopover() {
const dashQ = useQuery({ ...dashboardQueryOptions(), refetchInterval: 30_000 })
const agentsQ = useQuery({ ...agentsQueryOptions(), refetchInterval: 30_000 })
const listsQ = useQuery({ ...listsQueryOptions(), refetchInterval: 60_000 })
const rulesQ = useQuery({ ...rulesQueryOptions(), refetchInterval: 60_000 })
const setsQ = useQuery({
...policySetsQueryOptions(),
refetchInterval: 60_000,
})
const d = dashQ.data
const agents = agentsQ.data?.items ?? []
@@ -90,7 +93,8 @@ export function SystemMonitorPopover() {
d?.agents_pending ?? agents.filter((a) => a.status === 'pending').length
const onlinePct = approved > 0 ? Math.round((online / approved) * 100) : 0
const listsCount = listsQ.data?.items?.length ?? d?.lists_total ?? 0
const rulesCount = rulesQ.data?.items?.length ?? 0
const rulesCount =
setsQ.data?.items.reduce((n, s) => n + (s.rules_count ?? 0), 0) ?? 0
const apiOk = !dashQ.isError
const metrics = useMemo<MonitorMetric[]>(