import { MutationCache, QueryCache, QueryClient, QueryClientProvider, } from "@tanstack/react-query" import { createRouter, RouterProvider } from "@tanstack/react-router" import { MotionConfig } from "motion/react" import { StrictMode } from "react" import ReactDOM from "react-dom/client" import { ApiError, OpenAPI } from "./client" import { ThemeProvider } from "./components/theme-provider" import { Toaster } from "./components/ui/sonner" import "./index.css" import { connectionStore, offlineDetail } from "./lib/connectionStore" import { apiToken, portalConfig } from "./lib/portal" import { routeTree } from "./routeTree.gen" const portal = portalConfig() // Served through a portal, the API is a path on this same origin and the // credential arrives with the page. Everywhere else this is the build-time URL // and the token in storage, exactly as before. OpenAPI.BASE = portal ? window.location.origin + portal.apiBase : import.meta.env.VITE_API_URL OpenAPI.TOKEN = async () => apiToken() /** A session the server will not accept, whatever we do next. */ const isAuthFailure = (error: unknown) => error instanceof ApiError && [401, 403].includes(error.status) const handleApiError = (error: Error) => { const offline = offlineDetail(error) if (offline) { // The installation is unreachable, not the session invalid: keep the user // where they are and let the banner explain. connectionStore.setOffline(offline.lastSeen) return } if (isAuthFailure(error)) { // A paired wall panel has no login screen to go back to — it asks for a // new code instead. Only a 401 is worth throwing its credential away for: // a 403 there is a dashboard it was just unassigned from, which the next // read of the panel corrects on its own. if (window.location.pathname.startsWith("/panel")) { if (error instanceof ApiError && error.status === 401) { localStorage.removeItem("access_token") window.location.href = "/panel" } return } if (portal) { // The portal knows whether they are still signed in; it can mint a fresh // handoff or send them to the login screen. window.location.href = `${portal.portalUrl}?reauth=${portal.installationId}` return } localStorage.removeItem("access_token") window.location.href = "/login" } } const queryClient = new QueryClient({ queryCache: new QueryCache({ onError: handleApiError, // Any answer at all means the tunnel is up again. onSuccess: () => connectionStore.setOnline(), }), mutationCache: new MutationCache({ onError: handleApiError, }), defaultOptions: { queries: { // Retrying an expired session only delays the trip to the login screen. retry: (count, error) => !isAuthFailure(error) && count < 3, }, mutations: { retry: false, }, }, }) const router = createRouter({ routeTree, basepath: portal?.basePath }) declare module "@tanstack/react-router" { interface Register { router: typeof router } } ReactDOM.createRoot(document.getElementById("root")!).render( , )