feat(api, web): enhance policy rule handling with list name integration
- 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:
@@ -82,18 +82,23 @@ function resolveDefaultAction(agentDefaultAction: string | null | undefined): De
|
|||||||
return defaultActionFromLegacyMode(agentDefaultAction)
|
return defaultActionFromLegacyMode(agentDefaultAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sourceMeta(rule: {
|
function sourceMeta(
|
||||||
|
db: Db,
|
||||||
|
rule: {
|
||||||
cidr: string | null
|
cidr: string | null
|
||||||
listId: string | null
|
listId: string | null
|
||||||
hostname: string | null
|
hostname: string | null
|
||||||
}): { kind: 'list' | 'cidr' | 'hostname'; label: string } {
|
},
|
||||||
|
): { kind: 'list' | 'cidr' | 'hostname'; label: string } {
|
||||||
if (rule.cidr?.trim()) {
|
if (rule.cidr?.trim()) {
|
||||||
return { kind: 'cidr', label: rule.cidr.trim() }
|
return { kind: 'cidr', label: rule.cidr.trim() }
|
||||||
}
|
}
|
||||||
if (rule.hostname?.trim()) {
|
if (rule.hostname?.trim()) {
|
||||||
return { kind: 'hostname', label: 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. */
|
/** 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)
|
allow.push(...cidrs)
|
||||||
rulesAllow += 1
|
rulesAllow += 1
|
||||||
}
|
}
|
||||||
const src = sourceMeta(rule)
|
const src = sourceMeta(db, rule)
|
||||||
const setName =
|
const setName =
|
||||||
assignedSets.find((s) => s.setId === rule.setId)?.name ?? null
|
assignedSets.find((s) => s.setId === rule.setId)?.name ?? null
|
||||||
chain.push({
|
chain.push({
|
||||||
|
|||||||
@@ -36,17 +36,27 @@ import { apiFetch } from '@/lib/api'
|
|||||||
* Docs: https://reui.io/docs/components/base/sortable
|
* 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.cidr) return r.cidr
|
||||||
if (r.hostname) return r.hostname
|
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 '—'
|
return '—'
|
||||||
}
|
}
|
||||||
|
|
||||||
function ruleSubtitle(r: PolicyRule): string | null {
|
function ruleSubtitle(
|
||||||
|
r: PolicyRule,
|
||||||
|
listNames?: Map<string, string>,
|
||||||
|
): string | null {
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
if (r.list_id && (r.cidr || r.hostname)) {
|
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 (r.comment) parts.push(r.comment)
|
||||||
if (typeof r.resolved_count === 'number' && r.resolved_count > 0) {
|
if (typeof r.resolved_count === 'number' && r.resolved_count > 0) {
|
||||||
@@ -58,6 +68,8 @@ function ruleSubtitle(r: PolicyRule): string | null {
|
|||||||
type PolicyRulesSortableProps = {
|
type PolicyRulesSortableProps = {
|
||||||
setId: string
|
setId: string
|
||||||
rules: PolicyRule[]
|
rules: PolicyRule[]
|
||||||
|
/** list_id → name for human-readable rule labels */
|
||||||
|
listNames?: Map<string, string>
|
||||||
onDelete: (id: string) => void
|
onDelete: (id: string) => void
|
||||||
onAdd?: () => void
|
onAdd?: () => void
|
||||||
}
|
}
|
||||||
@@ -65,6 +77,7 @@ type PolicyRulesSortableProps = {
|
|||||||
export function PolicyRulesSortable({
|
export function PolicyRulesSortable({
|
||||||
setId,
|
setId,
|
||||||
rules: rulesProp,
|
rules: rulesProp,
|
||||||
|
listNames,
|
||||||
onDelete,
|
onDelete,
|
||||||
onAdd,
|
onAdd,
|
||||||
}: PolicyRulesSortableProps) {
|
}: PolicyRulesSortableProps) {
|
||||||
@@ -170,7 +183,7 @@ export function PolicyRulesSortable({
|
|||||||
{items.map((r) => {
|
{items.map((r) => {
|
||||||
const enabled = r.enabled !== false
|
const enabled = r.enabled !== false
|
||||||
const isDeny = r.action === 'deny'
|
const isDeny = r.action === 'deny'
|
||||||
const subtitle = ruleSubtitle(r)
|
const subtitle = ruleSubtitle(r, listNames)
|
||||||
return (
|
return (
|
||||||
<SortableItem
|
<SortableItem
|
||||||
key={r.id}
|
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 min-w-0 flex-1 flex-col gap-0.5">
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<span className="truncate font-mono text-sm font-medium">
|
<span
|
||||||
{ruleTarget(r)}
|
className={cn(
|
||||||
|
'truncate text-sm font-medium',
|
||||||
|
r.cidr || r.hostname ? 'font-mono' : undefined,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{ruleTarget(r, listNames)}
|
||||||
</span>
|
</span>
|
||||||
<Badge
|
<Badge
|
||||||
variant={
|
variant={
|
||||||
|
|||||||
@@ -180,6 +180,23 @@ function PolicySetDetailPage() {
|
|||||||
|
|
||||||
const rules = rulesQ.data?.items ?? []
|
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(
|
const agentColumns: ColumnDef<Agent>[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -336,6 +353,7 @@ function PolicySetDetailPage() {
|
|||||||
<PolicyRulesSortable
|
<PolicyRulesSortable
|
||||||
setId={setId}
|
setId={setId}
|
||||||
rules={rules}
|
rules={rules}
|
||||||
|
listNames={listNameById}
|
||||||
onDelete={(id) => setDeleteRuleId(id)}
|
onDelete={(id) => setDeleteRuleId(id)}
|
||||||
onAdd={() => setRuleOpen(true)}
|
onAdd={() => setRuleOpen(true)}
|
||||||
/>
|
/>
|
||||||
@@ -441,6 +459,7 @@ function PolicySetDetailPage() {
|
|||||||
<Field>
|
<Field>
|
||||||
<FieldLabel>Список</FieldLabel>
|
<FieldLabel>Список</FieldLabel>
|
||||||
<Select
|
<Select
|
||||||
|
items={listSelectItems}
|
||||||
value={listId || null}
|
value={listId || null}
|
||||||
onValueChange={(v) => setListId(v ?? '')}
|
onValueChange={(v) => setListId(v ?? '')}
|
||||||
>
|
>
|
||||||
@@ -448,9 +467,9 @@ function PolicySetDetailPage() {
|
|||||||
<SelectValue placeholder="Выберите список" />
|
<SelectValue placeholder="Выберите список" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{(listsQ.data?.items ?? []).map((l) => (
|
{listSelectItems.map((l) => (
|
||||||
<SelectItem key={l.id} value={l.id}>
|
<SelectItem key={l.value} value={l.value}>
|
||||||
{l.name}
|
{l.label}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user