feat(module-grants): Proxmox, Handelsware und DKV mit Freigabestufe Verwalten

- Proxmox-Schreibwege und Handelsware-Einstellungen auf @ModuleManage umgestellt
- DKV-Fleet: ganze Klasse Verwalten-Stufe, Benutzen allein bleibt ohne Zugriff
- Metadaten-Test belegt umgestellte und bewusst Administratoren vorbehaltene Handler
- Webseiten (Proxmox, Handelsware, Widget) folgen canManage, DKV-Zugriffsseite erklärt die Stufe

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-02 13:31:08 +02:00
parent a222711ad9
commit c2ebc8daa0
23 changed files with 403 additions and 138 deletions
@@ -1,8 +1,16 @@
import { checkModuleAccess } from '@/lib/module-access-actions';
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;
@@ -29,6 +37,26 @@ interface ModuleAccessGateProps {
* 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 (
<ModuleAccessDenied
title={tm('accessDenied.title')}
body={level === 'use' ? tm('accessDenied.manageRequiredBody') : tm('accessDenied.body')}
backToDashboard={tm('accessDenied.backToDashboard')}
/>
);
}
let hasAccess = false;
try {