feat(api, web): enhance policy rule handling with list name integration
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m57s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

- Updated the `sourceMeta` function to accept a database parameter, allowing for dynamic retrieval of list names based on list IDs.
- Modified the `ruleTarget` and `ruleSubtitle` functions to utilize the new list name mapping, improving the readability of policy rules in the UI.
- Introduced memoization for list names in the `PolicySetDetailPage`, optimizing performance and ensuring accurate display of list names in the policy rules sortable component.

These changes enhance the user experience by providing clearer and more informative labels for policy rules, facilitating better understanding and management of policies.
This commit is contained in:
Denozordec
2026-07-23 11:46:51 +07:00
parent fac2ed4d2d
commit d8567205e2
3 changed files with 59 additions and 17 deletions
+12 -7
View File
@@ -82,18 +82,23 @@ function resolveDefaultAction(agentDefaultAction: string | null | undefined): De
return defaultActionFromLegacyMode(agentDefaultAction)
}
function sourceMeta(rule: {
cidr: string | null
listId: string | null
hostname: string | null
}): { kind: 'list' | 'cidr' | 'hostname'; label: string } {
function sourceMeta(
db: Db,
rule: {
cidr: string | null
listId: string | null
hostname: string | null
},
): { kind: 'list' | 'cidr' | 'hostname'; label: string } {
if (rule.cidr?.trim()) {
return { kind: 'cidr', label: rule.cidr.trim() }
}
if (rule.hostname?.trim()) {
return { kind: 'hostname', label: rule.hostname.trim() }
}
return { kind: 'list', label: rule.listId ?? 'list' }
const listId = rule.listId?.trim() || ''
const name = listId ? repos.getIpList(db, listId)?.name : null
return { kind: 'list', label: name || listId || 'list' }
}
/** Evaluate allow/deny sets for an agent from assigned policy sets. */
@@ -125,7 +130,7 @@ export function evaluateAgentPolicy(db: Db, agentId: string): EvaluatedPolicy {
allow.push(...cidrs)
rulesAllow += 1
}
const src = sourceMeta(rule)
const src = sourceMeta(db, rule)
const setName =
assignedSets.find((s) => s.setId === rule.setId)?.name ?? null
chain.push({
@@ -36,17 +36,27 @@ import { apiFetch } from '@/lib/api'
* Docs: https://reui.io/docs/components/base/sortable
*/
function ruleTarget(r: PolicyRule): string {
function ruleTarget(
r: PolicyRule,
listNames?: Map<string, string>,
): string {
if (r.cidr) return r.cidr
if (r.hostname) return r.hostname
if (r.list_id) return `list:${r.list_id.slice(0, 8)}`
if (r.list_id) {
return listNames?.get(r.list_id) ?? `Список ${r.list_id.slice(0, 8)}`
}
return '—'
}
function ruleSubtitle(r: PolicyRule): string | null {
function ruleSubtitle(
r: PolicyRule,
listNames?: Map<string, string>,
): string | null {
const parts: string[] = []
if (r.list_id && (r.cidr || r.hostname)) {
parts.push(`list:${r.list_id.slice(0, 8)}`)
parts.push(
listNames?.get(r.list_id) ?? `Список ${r.list_id.slice(0, 8)}`,
)
}
if (r.comment) parts.push(r.comment)
if (typeof r.resolved_count === 'number' && r.resolved_count > 0) {
@@ -58,6 +68,8 @@ function ruleSubtitle(r: PolicyRule): string | null {
type PolicyRulesSortableProps = {
setId: string
rules: PolicyRule[]
/** list_id → name for human-readable rule labels */
listNames?: Map<string, string>
onDelete: (id: string) => void
onAdd?: () => void
}
@@ -65,6 +77,7 @@ type PolicyRulesSortableProps = {
export function PolicyRulesSortable({
setId,
rules: rulesProp,
listNames,
onDelete,
onAdd,
}: PolicyRulesSortableProps) {
@@ -170,7 +183,7 @@ export function PolicyRulesSortable({
{items.map((r) => {
const enabled = r.enabled !== false
const isDeny = r.action === 'deny'
const subtitle = ruleSubtitle(r)
const subtitle = ruleSubtitle(r, listNames)
return (
<SortableItem
key={r.id}
@@ -201,8 +214,13 @@ export function PolicyRulesSortable({
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
<div className="flex flex-wrap items-center gap-2">
<span className="truncate font-mono text-sm font-medium">
{ruleTarget(r)}
<span
className={cn(
'truncate text-sm font-medium',
r.cidr || r.hostname ? 'font-mono' : undefined,
)}
>
{ruleTarget(r, listNames)}
</span>
<Badge
variant={
+22 -3
View File
@@ -180,6 +180,23 @@ function PolicySetDetailPage() {
const rules = rulesQ.data?.items ?? []
const listNameById = useMemo(() => {
const m = new Map<string, string>()
for (const l of listsQ.data?.items ?? []) {
m.set(l.id, l.name)
}
return m
}, [listsQ.data?.items])
const listSelectItems = useMemo(
() =>
(listsQ.data?.items ?? []).map((l) => ({
value: l.id,
label: l.name,
})),
[listsQ.data?.items],
)
const agentColumns: ColumnDef<Agent>[] = useMemo(
() => [
{
@@ -336,6 +353,7 @@ function PolicySetDetailPage() {
<PolicyRulesSortable
setId={setId}
rules={rules}
listNames={listNameById}
onDelete={(id) => setDeleteRuleId(id)}
onAdd={() => setRuleOpen(true)}
/>
@@ -441,6 +459,7 @@ function PolicySetDetailPage() {
<Field>
<FieldLabel>Список</FieldLabel>
<Select
items={listSelectItems}
value={listId || null}
onValueChange={(v) => setListId(v ?? '')}
>
@@ -448,9 +467,9 @@ function PolicySetDetailPage() {
<SelectValue placeholder="Выберите список" />
</SelectTrigger>
<SelectContent>
{(listsQ.data?.items ?? []).map((l) => (
<SelectItem key={l.id} value={l.id}>
{l.name}
{listSelectItems.map((l) => (
<SelectItem key={l.value} value={l.value}>
{l.label}
</SelectItem>
))}
</SelectContent>