mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
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.
This commit is contained in:
1 parent
d2ac3b91bd
commit
ea6e94adcf
3 files changed
+80
-39
No files matched your search
+16
-2
@@ -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 (
|
||||
<html lang={initialLocale} suppressHydrationWarning>
|
||||
@@ -139,7 +145,15 @@ export default async function RootLayout({
|
||||
<KeyMismatchPage locale={initialLocale} />
|
||||
) : (
|
||||
<Providers initialLocale={initialLocale}>
|
||||
<MonitoringBootstrap />
|
||||
<MonitoringBootstrap
|
||||
settings={
|
||||
monitoring && {
|
||||
umamiEnabled: monitoring.umamiEnabled,
|
||||
umamiWebsiteId: monitoring.umamiWebsiteId,
|
||||
glitchtipEnabled: monitoring.glitchtipEnabled,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<WebVitalsReporter />
|
||||
{/* `DEMO_MODE` is a server-only env var; the proxy uses it to
|
||||
block mutations. Resolve it here (server component) and
|
||||
|
||||
@@ -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 }) => (
|
||||
<i data-umami={`${props.enabled}:${props.websiteId}`} />
|
||||
),
|
||||
}));
|
||||
vi.mock("@/components/monitoring/glitchtip-reporter", () => ({
|
||||
GlitchtipReporter: (props: { enabled: boolean }) => (
|
||||
<i data-glitchtip={String(props.enabled)} />
|
||||
),
|
||||
}));
|
||||
|
||||
import { MonitoringBootstrap } from "../bootstrap";
|
||||
|
||||
describe("MonitoringBootstrap", () => {
|
||||
it("mounts what the layout's switches turn on, without asking the server", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<MonitoringBootstrap
|
||||
settings={{
|
||||
umamiEnabled: true,
|
||||
umamiWebsiteId: "site-1",
|
||||
glitchtipEnabled: true,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
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(<MonitoringBootstrap settings={null} />)).toBe(
|
||||
"",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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<MonitoringSettings | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
|
||||
apiGet<MonitoringSettings | undefined>("/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 (
|
||||
|
||||
Reference in new issue
Block a user