Files
NexaFabric/frontend/src/App.tsx
T
nessi 1531b7ea47 feat: add configurable flow retention settings with super admin controls and pagination for workload flows
Add RuntimeSettingsRead/RuntimeSettingsUpdate schemas with flow_retention_hours field (1-8760 hours), implement runtime_setting helper to initialize/fetch runtime system setting with 24h default, add require_super_admin guard to validate * permission, update agent_heartbeat to use configurable retention_cutoff from runtime settings instead of hardcoded 24h, replace /settings GET endpoint to return RuntimeSettingsRead with flow_retention_hours,
2026-07-09 22:59:31 +02:00

87 lines
3.7 KiB
TypeScript

import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
import { useEffect } from "react";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import { publicApi, SetupStatus } from "./api/client";
import { Layout } from "./components/Layout";
import { Dashboard } from "./pages/Dashboard";
import { FirewallPreview } from "./pages/FirewallPreview";
import { Clusters } from "./pages/Clusters";
import { Ipam } from "./pages/Ipam";
import { ListPage } from "./pages/ListPage";
import { Login } from "./pages/Login";
import { Networks } from "./pages/Networks";
import { Nodes } from "./pages/Nodes";
import { Policies } from "./pages/Policies";
import { PolicyDesigner } from "./pages/PolicyDesigner";
import { SecurityGroups } from "./pages/SecurityGroups";
import { ServiceCatalog } from "./pages/ServiceCatalog";
import { Settings } from "./pages/Settings";
import { SetupWizard } from "./pages/SetupWizard";
import { TenantsProjects } from "./pages/TenantsProjects";
import { UsersRoles } from "./pages/UsersRoles";
import { WorkloadDetail, WorkloadFlows, Workloads } from "./pages/Workloads";
import { useTheme } from "./stores/theme";
const queryClient = new QueryClient();
function AppRoutes() {
const setup = useQuery({ queryKey: ["setup-status"], queryFn: () => publicApi<SetupStatus>("/setup/status") });
if (setup.isLoading) {
return <div className="grid min-h-screen place-items-center bg-canvas text-sm">Loading NexaFabric...</div>;
}
if (!setup.data?.complete) {
return (
<Routes>
<Route path="/setup" element={<SetupWizard />} />
<Route path="*" element={<Navigate to="/setup" replace />} />
</Routes>
);
}
return (
<Routes>
<Route path="/setup" element={<Navigate to="/login" replace />} />
<Route path="/login" element={<Login />} />
<Route element={<Layout />}>
<Route index element={<Dashboard />} />
<Route path="clusters" element={<Clusters />} />
<Route path="nodes" element={<Nodes />} />
<Route path="workloads" element={<Workloads />} />
<Route path="workloads/:workloadId" element={<WorkloadDetail />} />
<Route path="workloads/:workloadId/flows" element={<WorkloadFlows />} />
<Route path="networks" element={<Networks />} />
<Route path="ipam" element={<Ipam />} />
<Route path="tenants" element={<TenantsProjects />} />
<Route path="security-groups" element={<SecurityGroups />} />
<Route path="policies" element={<Policies />} />
<Route path="services" element={<ServiceCatalog />} />
<Route path="designer" element={<PolicyDesigner />} />
<Route path="firewall" element={<FirewallPreview />} />
<Route path="jobs" element={<ListPage title="Jobs" subtitle="Background task state and execution logs." path="/jobs" columns={[{ key: "kind", label: "Kind" }, { key: "status", label: "Status" }, { key: "progress", label: "Progress" }]} />} />
<Route path="audit" element={<ListPage title="Audit Logs" subtitle="Security-relevant activity and change history." path="/audit" columns={[{ key: "created_at", label: "Time" }, { key: "action", label: "Action" }, { key: "object_type", label: "Object" }, { key: "result", label: "Result" }]} />} />
<Route path="users" element={<UsersRoles />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
);
}
export function App() {
const dark = useTheme((state) => state.dark);
useEffect(() => {
document.documentElement.classList.toggle("dark", dark);
}, [dark]);
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</QueryClientProvider>
);
}