export const DEPLOYMENT_ROLES = ['all', 'ops', 'server', 'learner'] as const; export type DeploymentRole = (typeof DEPLOYMENT_ROLES)[number]; export type DeploymentCapability = 'manage_courses' | 'manage_settings'; export function resolveDeploymentRole( rawRole: string | undefined, nodeEnv: string | undefined, ): DeploymentRole { const normalized = rawRole?.trim().toLowerCase(); if (DEPLOYMENT_ROLES.includes(normalized as DeploymentRole)) { return normalized as DeploymentRole; } // Production is fail-closed: an unconfigured build is a learner surface, // never an accidental operations console. Local development keeps the // historical all-in-one experience. return nodeEnv === 'production' ? 'learner' : 'all'; } export function hasDeploymentCapability( role: DeploymentRole, capability: DeploymentCapability, ): boolean { switch (capability) { case 'manage_courses': return role === 'ops' || role === 'all'; case 'manage_settings': return role === 'ops' || role === 'all'; } } /** Non-secret client affordance only; every privileged API checks server-side too. */ export function isOpsWorkbenchEnabled(): boolean { const role = resolveDeploymentRole( process.env.NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE, process.env.NODE_ENV, ); return hasDeploymentCapability(role, 'manage_courses'); } /** * Settings are an operator concern. Learner deployments consume the provider * and model configuration already supplied by the server and do not expose * controls that let a learner replace it. */ export function isSettingsManagementEnabled(): boolean { const role = resolveDeploymentRole( process.env.NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE, process.env.NODE_ENV, ); return hasDeploymentCapability(role, 'manage_settings'); }