import { lazy, useEffect } from "react"; import { Navigate, Outlet, Route, RouterProvider, createBrowserRouter, createRoutesFromElements, } from "react-router-dom"; import { AppShell } from "@/components/layout/AppShell"; import { Login } from "@/pages/Login"; import { Dashboard } from "@/pages/Dashboard"; import { useAuthStore } from "@/store/auth"; import { useThemeStore } from "@/store/theme"; /** * Routes are split so a page's code is fetched when it is first opened. * * Everything used to land in one chunk that every visitor downloaded in full, * including the two heavy pieces most sessions never touch: the compose editor * (Monaco's React wrapper plus the diff view) and, through the container card, * xterm.js. * * Login and the Dashboard stay eagerly imported — they are the first thing * everyone sees, and deferring them would only add a spinner to the critical * path. */ const Stacks = lazy(() => import("@/pages/Stacks").then((m) => ({ default: m.Stacks }))); const StackDetail = lazy(() => import("@/pages/StackDetail").then((m) => ({ default: m.StackDetail })) ); const StackEditor = lazy(() => import("@/pages/StackEditor").then((m) => ({ default: m.StackEditor })) ); const Images = lazy(() => import("@/pages/Images").then((m) => ({ default: m.Images }))); const Files = lazy(() => import("@/pages/Files").then((m) => ({ default: m.Files }))); const Volumes = lazy(() => import("@/pages/Volumes").then((m) => ({ default: m.Volumes }))); const Templates = lazy(() => import("@/pages/Templates").then((m) => ({ default: m.Templates })) ); const Settings = lazy(() => import("@/pages/Settings").then((m) => ({ default: m.Settings }))); const Audit = lazy(() => import("@/pages/Audit").then((m) => ({ default: m.Audit }))); const Networks = lazy(() => import("@/pages/Networks").then((m) => ({ default: m.Networks }))); function RequireAuth() { const token = useAuthStore((s) => s.accessToken); const ready = useAuthStore((s) => s.ready); // The access token lives in memory, so on a reload we have nothing until the // cookie-based restore has run. Bouncing to /login before that would sign // people out on every refresh. if (!ready) return ; return token ? : ; } function BootSplash() { // Set while the backend does not answer yet (starting, restarting after an // update) — the session is kept and the restore keeps trying. const waiting = useAuthStore((s) => s.waiting); return (
Restoring your session… ); } // A data router rather than : only it supports useBlocker, which // is what keeps the stack editor from dropping unsaved edits on a stray click. const router = createBrowserRouter( createRoutesFromElements( <> } /> }> }> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> ) ); export default function App() { const applyTheme = useThemeStore((s) => s.apply); const restore = useAuthStore((s) => s.restore); useEffect(() => { applyTheme(); }, [applyTheme]); // Trade the httpOnly refresh cookie for an access token once on boot. useEffect(() => { restore(); }, [restore]); return ; }