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 { Plus, Shield } from "lucide-react";
|
||||
import { api, Project, SecurityGroup, SecurityRule } from "../api/client";
|
||||
import { DataTable } from "../components/DataTable";
|
||||
import { buttonClass, Field, inputClass, selectClass } from "../components/FormControls";
|
||||
import { Modal } from "../components/Modal";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
export function SecurityGroups() {
|
||||
@@ -30,14 +31,22 @@ export function SecurityGroups() {
|
||||
logging: true,
|
||||
description: "Allow HTTPS",
|
||||
});
|
||||
const [groupOpen, setGroupOpen] = useState(false);
|
||||
const [ruleOpen, setRuleOpen] = useState(false);
|
||||
|
||||
const createGroup = useMutation({
|
||||
mutationFn: () => api<SecurityGroup>("/security-groups", { method: "POST", body: JSON.stringify({ ...groupForm, project_id: groupForm.project_id || null }) }),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["security-groups"] }),
|
||||
onSuccess: () => {
|
||||
setGroupOpen(false);
|
||||
queryClient.invalidateQueries({ queryKey: ["security-groups"] });
|
||||
},
|
||||
});
|
||||
const createRule = useMutation({
|
||||
mutationFn: () => api<SecurityRule>("/security-rules", { method: "POST", body: JSON.stringify({ ...ruleForm, security_group_id: selectedGroup }) }),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["security-rules", selectedGroup] }),
|
||||
onSuccess: () => {
|
||||
setRuleOpen(false);
|
||||
queryClient.invalidateQueries({ queryKey: ["security-rules", selectedGroup] });
|
||||
},
|
||||
});
|
||||
|
||||
async function submitGroup(event: FormEvent) {
|
||||
@@ -54,8 +63,13 @@ export function SecurityGroups() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader title="Security Groups" subtitle="Create logical groups and attach ordered ingress or egress rules." />
|
||||
<div className="grid gap-4 xl:grid-cols-[340px_360px_1fr]">
|
||||
<form onSubmit={submitGroup} className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button className={buttonClass} onClick={() => setGroupOpen(true)}><Plus size={16} /> Add Group</button>
|
||||
<button className={buttonClass} onClick={() => setRuleOpen(true)} disabled={!selectedGroup}><Plus size={16} /> Add Rule</button>
|
||||
</div>
|
||||
<Modal title="Add Security Group" open={groupOpen} onClose={() => setGroupOpen(false)}>
|
||||
<form onSubmit={submitGroup}>
|
||||
<div className="mb-4 flex items-center gap-2 font-medium"><Shield size={18} /> Add Group</div>
|
||||
<div className="grid gap-3">
|
||||
<Field label="Project">
|
||||
@@ -68,8 +82,10 @@ export function SecurityGroups() {
|
||||
<Field label="Description"><input className={inputClass} value={groupForm.description} onChange={(event) => setGroupForm({ ...groupForm, description: event.target.value })} /></Field>
|
||||
<button className={buttonClass}><Plus size={16} /> Save Group</button>
|
||||
</div>
|
||||
</form>
|
||||
<form onSubmit={submitRule} className="rounded-md border border-border bg-panel p-4">
|
||||
</form>
|
||||
</Modal>
|
||||
<Modal title="Add Security Rule" open={ruleOpen} onClose={() => setRuleOpen(false)}>
|
||||
<form onSubmit={submitRule}>
|
||||
<div className="mb-4 font-medium">Add Rule</div>
|
||||
<div className="grid gap-3">
|
||||
<Field label="Security Group">
|
||||
@@ -89,7 +105,8 @@ export function SecurityGroups() {
|
||||
</div>
|
||||
<button className={buttonClass} disabled={!selectedGroup}><Plus size={16} /> Save Rule</button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</Modal>
|
||||
<section className="space-y-4">
|
||||
<DataTable rows={(groups.data ?? []) as unknown as Record<string, unknown>[]} columns={[{ key: "name", label: "Group" }, { key: "description", label: "Description" }]} />
|
||||
<DataTable rows={(rules.data ?? []) as unknown as Record<string, unknown>[]} columns={[{ key: "priority", label: "Priority" }, { key: "direction", label: "Direction" }, { key: "action", label: "Action" }, { key: "protocol", label: "Protocol" }, { key: "port", label: "Port" }]} />
|
||||
@@ -98,4 +115,3 @@ export function SecurityGroups() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user