Files
HealthLog/src/hooks/use-module-page-guard.ts
T
Marc-André Bombeck a524cad4c4 fix(insights): guard the module sub-pages against direct-URL hits
The sleep, blood-glucose, workouts, and recovery insights pages
fetched their series and half-rendered when the backing module was
disabled — a disabled account that navigated straight to the URL saw
the surface flash before any guard. They now resolve a shared
useModulePageGuard hook (mirroring the cycle page): an unauthenticated
visitor bounces to login, a disabled-module account bounces to
/insights, and the page renders a calm loader until the gate clears.
The backing /api routes enforce the gate server-side, so this is the
UX layer, not the security boundary.
2026-06-16 02:01:47 +02:00

46 lines
1.6 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/hooks/use-auth";
import type { ModuleKey } from "@/lib/modules/registry";
/**
* v1.18.0 B1 — client-side page guard for a toggleable module.
*
* Mirrors the cycle page (`src/app/cycle/page.tsx`): an unauthenticated
* visitor bounces to login; an authenticated account without the module
* enabled bounces to `/insights`. The nav entry is already hidden for a
* disabled account, so this only catches a direct URL hit. Every backing
* `/api/*` route enforces the gate server-side, so this is a UX redirect,
* not the security boundary.
*
* Returns `ready` — `true` only once auth has resolved, the visitor is
* authenticated, AND the module is enabled. While `false` the caller
* should render a calm loader / nothing rather than the (about-to-
* redirect) module surface, so a disabled-module page never half-renders.
*
* Default-on (disabled allowlist): an absent map, a missing key, or any
* non-`false` value reads as enabled — matching the server gate.
*/
export function useModulePageGuard(moduleKey: ModuleKey): {
ready: boolean;
} {
const { user, isLoading, isAuthenticated } = useAuth();
const router = useRouter();
const enabled = user?.modules?.[moduleKey] !== false;
useEffect(() => {
if (isLoading) return;
if (!isAuthenticated) {
router.push("/auth/login");
} else if (!enabled) {
router.push("/insights");
}
}, [isLoading, isAuthenticated, enabled, router]);
return { ready: !isLoading && isAuthenticated && enabled };
}