'use server'; import { getServerSession } from "next-auth"; import { authOptions } from "@/app/api/auth/[...nextauth]/route"; import { getAllScreens, getAuthsByScreenId, getUserRoles, type ScreenEvcp, type RoleRelEvcp } from "@/lib/nonsap/db"; import { getActiveMenuTree } from "./service"; import type { MenuDomain, MenuTreeNode, MenuTreeActiveResult } from "./types"; import db from "@/db/db"; import { users } from "@/db/schema/users"; import { eq } from "drizzle-orm"; /** * Oracle 권한 체크 스킵 여부 확인 * SKIP_ORACLE_PERMISSION_CHECK=true인 경우 Oracle DB 권한 체크를 건너뜀 */ function shouldSkipOraclePermissionCheck(): boolean { return process.env.SKIP_ORACLE_PERMISSION_CHECK === 'true'; } /** * 사용자 ID로 employeeNumber 조회 */ async function getEmployeeNumberByUserId(userId: number): Promise { const [user] = await db.select({ employeeNumber: users.employeeNumber }) .from(users) .where(eq(users.id, userId)) .limit(1); return user?.employeeNumber || null; } /** * Get menu tree filtered by user permissions * * @param domain - Domain (evcp | partners) * @param userId - Optional user ID. If not provided, gets from session. * * Environment variable SKIP_ORACLE_PERMISSION_CHECK=true skips Oracle permission check */ export async function getVisibleMenuTree( domain: MenuDomain, userId?: number ): Promise { const { tree: menuTree } = await getActiveMenuTree(domain); // Partners domain uses its own permission system (not implemented) if (domain === 'partners') { return { tree: menuTree }; } // Skip Oracle permission check in development if (shouldSkipOraclePermissionCheck()) { return { tree: menuTree }; } // Get userId from session if not provided let effectiveUserId = userId; if (!effectiveUserId) { const session = await getServerSession(authOptions); effectiveUserId = session?.user?.id ? parseInt(session.user.id, 10) : undefined; } if (!effectiveUserId) { return { tree: menuTree }; } // Get employeeNumber from userId const empNo = await getEmployeeNumberByUserId(effectiveUserId); if (!empNo) { return { tree: menuTree }; } let screens: ScreenEvcp[]; let userRoles: RoleRelEvcp[]; try { [screens, userRoles] = await Promise.all([ getAllScreens(), getUserRoles(empNo) ]); } catch (error) { // Oracle DB 연결 실패 시 전체 메뉴 반환 (에러로 인한 접근 차단 방지) console.error('[menu-v2] Oracle permission check failed, returning all menus:', error); return { tree: menuTree }; } const userRoleIds = new Set(userRoles.map(r => r.ROLE_ID)); const screenMap = new Map(screens.map(s => [s.SCR_URL, s])); // 메뉴 필터링 (최상위 menu, menu_group, group 모두 처리) async function filterByPermission(nodes: MenuTreeNode[]): Promise { const result: MenuTreeNode[] = []; for (const node of nodes) { // 메뉴 노드 (최상위 단일 링크 또는 하위 메뉴) if (node.nodeType === 'menu' && node.menuPath) { const screen = screenMap.get(node.menuPath); // 화면 정보가 없거나 SCRT_CHK_YN === 'N' 이면 표시 if (!screen || screen.SCRT_CHK_YN === 'N') { result.push(node); continue; } // SCRT_CHK_YN === 'Y' 이면 권한 체크 if (screen.SCRT_CHK_YN === 'Y') { const scrIdToCheck = node.scrId || screen.SCR_ID; const auths = await getAuthsByScreenId(scrIdToCheck); const hasAccess = auths.some(auth => { if (auth.ACSR_GB_CD === 'U' && auth.ACSR_ID === empNo) return true; if (auth.ACSR_GB_CD === 'R' && userRoleIds.has(auth.ACSR_ID)) return true; return false; }); if (hasAccess) result.push(node); } } // 메뉴그룹 또는 그룹 (자식 필터링 후 자식이 있으면 포함) else if (node.nodeType === 'menu_group' || node.nodeType === 'group') { const filteredChildren = await filterByPermission(node.children || []); if (filteredChildren.length > 0) { result.push({ ...node, children: filteredChildren }); } } } return result; } const filteredTree = await filterByPermission(menuTree); return { tree: filteredTree }; } /** * 특정 메뉴 경로에 대한 접근 권한 확인 * * 환경변수 SKIP_ORACLE_PERMISSION_CHECK=true인 경우 항상 true 반환 */ export async function checkMenuAccess( menuPath: string, userId: number ): Promise { // Oracle 권한 체크 스킵 설정된 경우 if (shouldSkipOraclePermissionCheck()) { return true; } const empNo = await getEmployeeNumberByUserId(userId); if (!empNo) return false; try { const screens = await getAllScreens(); const screen = screens.find(s => s.SCR_URL === menuPath); // 등록되지 않은 화면 또는 권한 체크가 필요 없는 화면 if (!screen || screen.SCRT_CHK_YN === 'N') { return true; } // 삭제된 화면 if (screen.DEL_YN === 'Y') { return false; } // 권한 체크 const [auths, userRoles] = await Promise.all([ getAuthsByScreenId(screen.SCR_ID), getUserRoles(empNo) ]); const userRoleIds = new Set(userRoles.map(r => r.ROLE_ID)); return auths.some(auth => { if (auth.ACSR_GB_CD === 'U' && auth.ACSR_ID === empNo) return true; if (auth.ACSR_GB_CD === 'R' && userRoleIds.has(auth.ACSR_ID)) return true; return false; }); } catch (error) { // Oracle DB 연결 실패 시 접근 허용 (에러로 인한 차단 방지) console.error('[menu-v2] Oracle permission check failed for path:', menuPath, error); return true; } }