feat: add cluster update/delete endpoints, expand tcp/udp protocol handling, and enhance cluster management UI
CI / backend (push) Failing after 3s
CI / frontend (push) Failing after 28s

Add PATCH /clusters/{cluster_id} endpoint with optional token update and audit logging, implement DELETE /clusters/{cluster_id} with cascading deletion of nodes, workloads, networks, subnets, and IP addresses, expand firewall rule generation to split tcp/udp protocol into separate tcp and udp rules for Proxmox compatibility, add ClusterUpdate schema with optional api_token field, include
This commit is contained in:
2026-07-09 14:04:17 +02:00
parent 1ada59d3c7
commit 88badf1f22
7 changed files with 270 additions and 79 deletions
+19 -4
View File
@@ -42,7 +42,6 @@ export function PolicyDesigner() {
}, [networks.data, securityGroups.data, workloads.data]);
function payload() {
const service = services.data?.find((item) => item.id === form.service_id);
return {
project_id: null,
name: form.name.trim(),
@@ -50,7 +49,7 @@ export function PolicyDesigner() {
definition: {
source: form.source,
destination: form.destination,
service: { protocol: service?.protocol ?? form.protocol, ports: service?.ports ?? form.ports },
service: { protocol: form.protocol, ports: form.ports },
action: form.action,
direction: form.direction,
enforcement_mode: form.enforcement_mode,
@@ -89,6 +88,16 @@ export function PolicyDesigner() {
});
}
function chooseService(serviceId: string) {
const service = services.data?.find((item) => item.id === serviceId);
setForm({
...form,
service_id: serviceId,
protocol: service?.protocol ?? form.protocol,
ports: service?.ports ?? form.ports,
});
}
return (
<>
<PageHeader title="Policy Designer" subtitle="Build tenant-aware microsegmentation policies and inspect their intended impact." />
@@ -116,7 +125,7 @@ export function PolicyDesigner() {
</select>
</Field>
<Field label="Service">
<select className={selectClass} value={form.service_id} onChange={(event) => setForm({ ...form, service_id: event.target.value })}>
<select className={selectClass} value={form.service_id} onChange={(event) => chooseService(event.target.value)}>
<option value="">Custom</option>
{(services.data ?? []).map((service) => <option key={service.id} value={service.id}>{service.name} {service.ports}</option>)}
</select>
@@ -128,7 +137,13 @@ export function PolicyDesigner() {
<option>reject</option>
</select>
</Field>
<Field label="Protocol"><input className={inputClass} value={form.protocol} onChange={(event) => setForm({ ...form, protocol: event.target.value })} /></Field>
<Field label="Protocol">
<select className={selectClass} value={form.protocol} onChange={(event) => setForm({ ...form, protocol: event.target.value })}>
<option value="tcp">tcp</option>
<option value="udp">udp</option>
<option value="tcp/udp">tcp & udp</option>
</select>
</Field>
<Field label="Ports"><input className={inputClass} value={form.ports} onChange={(event) => setForm({ ...form, ports: event.target.value })} /></Field>
<Field label="Direction">
<select className={selectClass} value={form.direction} onChange={(event) => setForm({ ...form, direction: event.target.value })}>