import { checkModuleAccess, getModuleAccessLevel } from '@/lib/module-access-actions'; import { getTranslations } from 'next-intl/server'; import type { ReactNode } from 'react'; import { ModuleAccessDenied } from './module-access-denied'; /** * Module, die als Ganzes Verwalten-Stufe verlangen (261002-icv): die API * traegt dort `@ModuleManage` auf der ganzen Klasse (DkvController). Benutzer * mit nur "Benutzen" bekaemen von der API ohnehin 403 — die Seite zeigt ihnen * stattdessen eine erklaerende Zugriffsseite. */ const MANAGE_ONLY_MODULE_SLUGS = new Set(['dkv-fleet']); interface ModuleAccessGateProps { moduleSlug: string; children: ReactNode; } /** * Reusable server-side access gate for module routes (D-07, PERM-04). * * Calls checkModuleAccess(moduleSlug) — the same ModuleAccessService * resolution the sidebar and ModuleGuard use (D-01), no separate role * logic in the frontend. Renders the children only when access resolves * to explicitly `true`; every other outcome (denied, or the access check * throwing) renders the shared 403 markup instead. * * checkModuleAccess already fails closed itself and does not throw * (T-15-29) — the try/catch here is a second line of defense so a future * change to that function cannot silently flip this gate open. The * condition is deliberately "only pass through on explicit grant", never * "only block on explicit denial". * * No redirect and no not-found: the 403 rendering here is the server * response itself (D-07) — the user learns the module exists and they * lack a grant, they aren't bounced elsewhere or left thinking it's * missing. */ export async function ModuleAccessGate({ moduleSlug, children }: ModuleAccessGateProps) { if (MANAGE_ONLY_MODULE_SLUGS.has(moduleSlug)) { let level: 'none' | 'use' | 'manage' = 'none'; try { level = await getModuleAccessLevel(moduleSlug); } catch { level = 'none'; } if (level === 'manage') { return children; } const tm = await getTranslations('modules'); return ( ); } let hasAccess = false; try { hasAccess = await checkModuleAccess(moduleSlug); } catch { hasAccess = false; } if (hasAccess === true) { return children; } const t = await getTranslations('modules'); return ( ); }