- Networks: network_service (list w/ subnet/containers/in-use/owning-stack, create bridge/macvlan/ipvlan/overlay + optional subnet/gateway/internal, delete with default-network guard, prune) + routers/networks.py; real Networks page replaces the placeholder. - Fix: local stacks can now be deleted from the UI — Delete button on stack detail (with optional keep-files-on-disk) and a trash action on stack cards, via a shared ConfirmDialog. (Backend DELETE existed; no UI surfaced it.) Verified: py_compile, frontend tsc build, live network list smoke test (defaults flagged, compose nets + in-use detected); main 104 routes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
2.2 KiB
TypeScript
60 lines
2.2 KiB
TypeScript
import { useEffect } from "react";
|
|
import { BrowserRouter, Navigate, Outlet, Route, Routes } from "react-router-dom";
|
|
import { Layout } from "@/components/layout/Layout";
|
|
import { Login } from "@/pages/Login";
|
|
import { Dashboard } from "@/pages/Dashboard";
|
|
import { Stacks } from "@/pages/Stacks";
|
|
import { StackDetail } from "@/pages/StackDetail";
|
|
import { StackEditor } from "@/pages/StackEditor";
|
|
import { RemoteStackDetail } from "@/pages/RemoteStackDetail";
|
|
import { Images } from "@/pages/Images";
|
|
import { Templates } from "@/pages/Templates";
|
|
import { Settings } from "@/pages/Settings";
|
|
import { Audit } from "@/pages/Audit";
|
|
import { Networks } from "@/pages/Networks";
|
|
import { useAuthStore } from "@/store/auth";
|
|
import { useThemeStore } from "@/store/theme";
|
|
|
|
function RequireAuth() {
|
|
const token = useAuthStore((s) => s.accessToken);
|
|
return token ? <Outlet /> : <Navigate to="/login" replace />;
|
|
}
|
|
|
|
export default function App() {
|
|
const applyTheme = useThemeStore((s) => s.apply);
|
|
const fetchMe = useAuthStore((s) => s.fetchMe);
|
|
const token = useAuthStore((s) => s.accessToken);
|
|
|
|
useEffect(() => {
|
|
applyTheme();
|
|
}, [applyTheme]);
|
|
|
|
useEffect(() => {
|
|
if (token) fetchMe().catch(() => {});
|
|
}, [token, fetchMe]);
|
|
|
|
return (
|
|
<BrowserRouter>
|
|
<Routes>
|
|
<Route path="/login" element={<Login />} />
|
|
<Route element={<RequireAuth />}>
|
|
<Route element={<Layout />}>
|
|
<Route path="/" element={<Dashboard />} />
|
|
<Route path="/stacks" element={<Stacks />} />
|
|
<Route path="/stacks/new" element={<StackEditor />} />
|
|
<Route path="/stacks/:id" element={<StackDetail />} />
|
|
<Route path="/stacks/:id/edit" element={<StackEditor />} />
|
|
<Route path="/hosts/:agentId/stacks/:id" element={<RemoteStackDetail />} />
|
|
<Route path="/networks" element={<Networks />} />
|
|
<Route path="/images" element={<Images />} />
|
|
<Route path="/templates" element={<Templates />} />
|
|
<Route path="/audit" element={<Audit />} />
|
|
<Route path="/settings" element={<Settings />} />
|
|
</Route>
|
|
</Route>
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
</BrowserRouter>
|
|
);
|
|
}
|