feat: add initial setup wizard, workload insights, and policy audit mode
Add setup wizard with status tracking via SystemSetting model, implement /setup/status and /setup/complete endpoints to create initial admin user and optional cluster configuration, add workload insights endpoint with traffic analysis and policy matching including audit mode detection, implement enforcement_mode property on Policy model with audit/enforced states, add Modal component for dialogs, create SetupWizard page with multi
This commit is contained in:
@@ -5,6 +5,7 @@ import { GitBranch, Play, Plus } from "lucide-react";
|
||||
import { api, Policy, Project, ServiceCatalogItem } from "../api/client";
|
||||
import { DataTable } from "../components/DataTable";
|
||||
import { buttonClass, Field, inputClass, secondaryButtonClass, selectClass } from "../components/FormControls";
|
||||
import { Modal } from "../components/Modal";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
export function Policies() {
|
||||
@@ -13,6 +14,7 @@ export function Policies() {
|
||||
const projects = useQuery({ queryKey: ["projects"], queryFn: () => api<Project[]>("/projects") });
|
||||
const services = useQuery({ queryKey: ["service-catalog"], queryFn: () => api<ServiceCatalogItem[]>("/service-catalog") });
|
||||
const [preview, setPreview] = useState("");
|
||||
const [open, setOpen] = useState(false);
|
||||
const [form, setForm] = useState({
|
||||
project_id: "",
|
||||
name: "Web to DB",
|
||||
@@ -23,6 +25,7 @@ export function Policies() {
|
||||
ports: "5432",
|
||||
action: "allow",
|
||||
direction: "egress",
|
||||
enforcement_mode: "enforced",
|
||||
logging: true,
|
||||
description: "Allow application database traffic",
|
||||
});
|
||||
@@ -42,13 +45,17 @@ export function Policies() {
|
||||
service: { protocol: service?.protocol ?? form.protocol, ports: service?.ports ?? form.ports },
|
||||
action: form.action,
|
||||
direction: form.direction,
|
||||
enforcement_mode: form.enforcement_mode,
|
||||
logging: form.logging,
|
||||
description: form.description,
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["policies"] }),
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
queryClient.invalidateQueries({ queryKey: ["policies"] });
|
||||
},
|
||||
});
|
||||
|
||||
async function submit(event: FormEvent) {
|
||||
@@ -70,8 +77,10 @@ export function Policies() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader title="Policies" subtitle="Create versioned microsegmentation policies and compile firewall previews." />
|
||||
<div className="grid gap-4 xl:grid-cols-[420px_1fr]">
|
||||
<form onSubmit={submit} className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="space-y-4">
|
||||
<button className={buttonClass} onClick={() => setOpen(true)}><Plus size={16} /> Add Policy</button>
|
||||
<Modal title="Add Policy" open={open} onClose={() => setOpen(false)}>
|
||||
<form onSubmit={submit}>
|
||||
<div className="mb-4 flex items-center gap-2 font-medium"><GitBranch size={18} /> Add Policy</div>
|
||||
<div className="grid gap-3">
|
||||
<Field label="Project"><select className={selectClass} value={form.project_id} onChange={(event) => setForm({ ...form, project_id: event.target.value })}><option value="">Global</option>{(projects.data ?? []).map((project) => <option key={project.id} value={project.id}>{project.name}</option>)}</select></Field>
|
||||
@@ -89,10 +98,17 @@ export function Policies() {
|
||||
<Field label="Action"><select className={selectClass} value={form.action} onChange={(event) => setForm({ ...form, action: event.target.value })}><option>allow</option><option>deny</option><option>reject</option></select></Field>
|
||||
<Field label="Direction"><select className={selectClass} value={form.direction} onChange={(event) => setForm({ ...form, direction: event.target.value })}><option>ingress</option><option>egress</option></select></Field>
|
||||
</div>
|
||||
<Field label="Mode">
|
||||
<select className={selectClass} value={form.enforcement_mode} onChange={(event) => setForm({ ...form, enforcement_mode: event.target.value })}>
|
||||
<option value="enforced">enforced</option>
|
||||
<option value="audit">audit</option>
|
||||
</select>
|
||||
</Field>
|
||||
<Field label="Description"><input className={inputClass} value={form.description} onChange={(event) => setForm({ ...form, description: event.target.value })} /></Field>
|
||||
<button className={buttonClass}><Plus size={16} /> Save Policy</button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</Modal>
|
||||
<section className="space-y-4">
|
||||
<DataTable rows={(policies.data ?? []) as unknown as Record<string, unknown>[]} columns={[{ key: "name", label: "Policy" }, { key: "version", label: "Version" }, { key: "enabled", label: "Enabled" }]} />
|
||||
<div className="flex flex-wrap gap-2">
|
||||
@@ -109,4 +125,3 @@ export function Policies() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user