From ea6e94adcf42268ca9311295660b40ad684bce2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Wed, 11 Nov 2026 22:02:49 +0100 Subject: [PATCH] perf(monitoring): read the monitoring switches in the root layout Every document load, the sign-in page included, mounted a client effect that asked `/api/monitoring/settings` for three instance-wide values (false, null, false on a default install) before it mounted the analytics script or the error reporter. The root layout is a server component and now reads them while it renders and passes them in, so the request and the effect go. The key-mismatch page reads nothing, as before. The route stays for other callers. --- src/app/layout.tsx | 18 ++++++- .../monitoring/__tests__/bootstrap.test.tsx | 49 +++++++++++++++++ src/components/monitoring/bootstrap.tsx | 52 ++++++------------- 3 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 src/components/monitoring/__tests__/bootstrap.test.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 2e41e3b20..ad5c734a4 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,6 +8,7 @@ import "./globals.css"; import { Providers } from "@/components/providers"; import { AuthShell } from "@/components/layout/auth-shell"; import { MonitoringBootstrap } from "@/components/monitoring/bootstrap"; +import { getPublicMonitoringSettings } from "@/lib/monitoring-settings"; import { WebVitalsReporter } from "@/components/monitoring/web-vitals-reporter"; import { resolveInitialLocale } from "@/lib/i18n/resolve-initial-locale"; import { isKeyMismatch } from "@/lib/boot/key-mismatch-state"; @@ -86,7 +87,12 @@ export default async function RootLayout({ ? ((await headers()).get("x-nonce") ?? undefined) : undefined; - const initialLocale = await resolveInitialLocale(); + // The monitoring switches ride the document render instead of a request + // from every page load; the key-mismatch page reads no data. + const [initialLocale, monitoring] = await Promise.all([ + resolveInitialLocale(), + isKeyMismatch() ? null : getPublicMonitoringSettings(), + ]); return ( @@ -139,7 +145,15 @@ export default async function RootLayout({ ) : ( - + {/* `DEMO_MODE` is a server-only env var; the proxy uses it to block mutations. Resolve it here (server component) and diff --git a/src/components/monitoring/__tests__/bootstrap.test.tsx b/src/components/monitoring/__tests__/bootstrap.test.tsx new file mode 100644 index 000000000..e0a8c8fdb --- /dev/null +++ b/src/components/monitoring/__tests__/bootstrap.test.tsx @@ -0,0 +1,49 @@ +import { describe, expect, it, vi } from "vitest"; +import { renderToStaticMarkup } from "react-dom/server"; + +/** + * The monitoring switches come from the root layout's render, not from a + * request every page load sent to `/api/monitoring/settings` for three + * instance-wide values. The first render already mounts what they turn on. + */ + +const apiGet = vi.fn(); +vi.mock("@/lib/api/api-fetch", () => ({ + apiGet: (...args: unknown[]) => apiGet(...args), +})); +vi.mock("@/components/monitoring/umami-script", () => ({ + UmamiScript: (props: { enabled: boolean; websiteId: string | null }) => ( + + ), +})); +vi.mock("@/components/monitoring/glitchtip-reporter", () => ({ + GlitchtipReporter: (props: { enabled: boolean }) => ( + + ), +})); + +import { MonitoringBootstrap } from "../bootstrap"; + +describe("MonitoringBootstrap", () => { + it("mounts what the layout's switches turn on, without asking the server", () => { + const html = renderToStaticMarkup( + , + ); + + expect(html).toContain('data-umami="true:site-1"'); + expect(html).toContain('data-glitchtip="true"'); + expect(apiGet).not.toHaveBeenCalled(); + }); + + it("mounts nothing without settings", () => { + expect(renderToStaticMarkup()).toBe( + "", + ); + }); +}); diff --git a/src/components/monitoring/bootstrap.tsx b/src/components/monitoring/bootstrap.tsx index 0598dcff5..1f6c790ae 100644 --- a/src/components/monitoring/bootstrap.tsx +++ b/src/components/monitoring/bootstrap.tsx @@ -1,43 +1,21 @@ -"use client"; - -import { useEffect, useState } from "react"; import { UmamiScript } from "@/components/monitoring/umami-script"; import { GlitchtipReporter } from "@/components/monitoring/glitchtip-reporter"; -import { apiGet } from "@/lib/api/api-fetch"; - -interface MonitoringSettings { - umamiEnabled: boolean; - umamiWebsiteId: string | null; - glitchtipEnabled: boolean; -} - -export function MonitoringBootstrap() { - const [settings, setSettings] = useState(null); - - useEffect(() => { - let active = true; - - apiGet("/api/monitoring/settings", { - cache: "no-store", - }) - .then((data) => { - if (!active || !data) return; - setSettings(data); - }) - .catch(() => { - if (!active) return; - setSettings({ - umamiEnabled: false, - umamiWebsiteId: null, - glitchtipEnabled: false, - }); - }); - - return () => { - active = false; - }; - }, []); +import type { PublicMonitoringSettings } from "@/lib/monitoring-settings"; +/** + * Mounts the operator's analytics script and error reporter. The root layout + * reads the instance-wide switches on the server and passes them in, so no + * page load asks `/api/monitoring/settings` for three values the document + * render already had. `null` (the key-mismatch page) mounts nothing. + */ +export function MonitoringBootstrap({ + settings, +}: { + settings: Pick< + PublicMonitoringSettings, + "umamiEnabled" | "umamiWebsiteId" | "glitchtipEnabled" + > | null; +}) { if (!settings) return null; return (