feat: add human-readable endpoint labels to policy table with workload/network/security group prefix detection

Add endpointLabel helper to format policy endpoints with type-specific prefixes (VM/LXC, Network, Security Group) by parsing workload:/network:/sg: prefixes and resolving workload IDs to names, implement policyEndpoint wrapper to apply endpoint labeling to policy definition fields, fetch workloads query in Policies component for endpoint resolution, update DataTable source/destination columns
This commit is contained in:
2026-07-09 20:23:18 +02:00
parent 2f7da934ea
commit 68c11eba57
+23 -3
View File
@@ -2,7 +2,7 @@ import { FormEvent, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AlertTriangle, CheckCircle2, Clock3, Eye, GitBranch, Pencil, Play, Plus, Shield, Trash2 } from "lucide-react"; import { AlertTriangle, CheckCircle2, Clock3, Eye, GitBranch, Pencil, Play, Plus, Shield, Trash2 } from "lucide-react";
import { api, Policy, Project, ServiceCatalogItem } from "../api/client"; import { api, Policy, Project, ServiceCatalogItem, Workload } from "../api/client";
import { DataTable } from "../components/DataTable"; import { DataTable } from "../components/DataTable";
import { buttonClass, Field, iconButtonClass, inputClass, selectClass } from "../components/FormControls"; import { buttonClass, Field, iconButtonClass, inputClass, selectClass } from "../components/FormControls";
import { LoadingOverlay } from "../components/LoadingOverlay"; import { LoadingOverlay } from "../components/LoadingOverlay";
@@ -13,6 +13,25 @@ function policyValue(policy: Policy, key: string) {
return String(policy.definition?.[key] ?? ""); return String(policy.definition?.[key] ?? "");
} }
function endpointLabel(value: string, workloads: Workload[]) {
if (value.startsWith("workload:")) {
const workloadId = value.replace("workload:", "");
const workload = workloads.find((item) => item.id === workloadId);
return workload ? `VM/LXC: ${workload.name}` : "VM/LXC: unknown";
}
if (value.startsWith("network:")) {
return `Network: ${value.replace("network:", "")}`;
}
if (value.startsWith("sg:")) {
return `Security Group: ${value.replace("sg:", "")}`;
}
return value || "any";
}
function policyEndpoint(policy: Policy, key: string, workloads: Workload[]) {
return endpointLabel(policyValue(policy, key), workloads);
}
function policyService(policy: Policy) { function policyService(policy: Policy) {
const service = policy.definition?.service; const service = policy.definition?.service;
if (!service || typeof service !== "object") { if (!service || typeof service !== "object") {
@@ -95,6 +114,7 @@ export function Policies() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const policies = useQuery({ queryKey: ["policies"], queryFn: () => api<Policy[]>("/policies") }); const policies = useQuery({ queryKey: ["policies"], queryFn: () => api<Policy[]>("/policies") });
const projects = useQuery({ queryKey: ["projects"], queryFn: () => api<Project[]>("/projects") }); const projects = useQuery({ queryKey: ["projects"], queryFn: () => api<Project[]>("/projects") });
const workloads = useQuery({ queryKey: ["workloads"], queryFn: () => api<Workload[]>("/vms") });
const services = useQuery({ queryKey: ["service-catalog"], queryFn: () => api<ServiceCatalogItem[]>("/service-catalog") }); const services = useQuery({ queryKey: ["service-catalog"], queryFn: () => api<ServiceCatalogItem[]>("/service-catalog") });
const [preview, setPreview] = useState(""); const [preview, setPreview] = useState("");
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -250,8 +270,8 @@ export function Policies() {
rows={(policies.data ?? []) as unknown as Record<string, unknown>[]} rows={(policies.data ?? []) as unknown as Record<string, unknown>[]}
columns={[ columns={[
{ key: "name", label: "Policy" }, { key: "name", label: "Policy" },
{ key: "source", label: "Source", render: (row) => policyValue(row as unknown as Policy, "source") }, { key: "source", label: "Source", render: (row) => policyEndpoint(row as unknown as Policy, "source", workloads.data ?? []) },
{ key: "destination", label: "Destination", render: (row) => policyValue(row as unknown as Policy, "destination") }, { key: "destination", label: "Destination", render: (row) => policyEndpoint(row as unknown as Policy, "destination", workloads.data ?? []) },
{ key: "service", label: "Service", render: (row) => policyService(row as unknown as Policy) }, { key: "service", label: "Service", render: (row) => policyService(row as unknown as Policy) },
{ key: "enforcement_mode", label: "Mode" }, { key: "enforcement_mode", label: "Mode" },
{ key: "deployment_status", label: "Status", render: (row) => <PolicyDeploymentStatus policy={row as unknown as Policy} /> }, { key: "deployment_status", label: "Status", render: (row) => <PolicyDeploymentStatus policy={row as unknown as Policy} /> },