const database = require('../config/database'); const { ROLES, SIDEBAR_PAGES, SIDEBAR_PAGE_MAP } = require('../constants/sidebarPages'); const SETTINGS_PAGE_KEY = 'settings'; const PLANNING_UPLOAD_PAGE_KEY = 'planning_upload'; function getDefaultRoleAllowed(pageKey, role) { return Boolean(SIDEBAR_PAGE_MAP[pageKey]?.defaultRoles?.[role]); } function getDefaultDepartmentAllowed(pageKey) { return Boolean(SIDEBAR_PAGE_MAP[pageKey]?.departmentDefaultAllowed); } function shouldRepairExistingPermission(pageKey, role, allowed) { if (pageKey === SETTINGS_PAGE_KEY && role === 'admin') { return !(allowed === 1 || allowed === true); } return false; } async function ensureSidebarRolePermissions() { for (const page of SIDEBAR_PAGES) { for (const role of ROLES) { const existing = await database.local.query( 'SELECT id, page_name, allowed FROM permissions WHERE page_key = ? AND role = ? LIMIT 1', [page.pageKey, role] ); if (existing.length > 0) { const shouldRepairAllowed = shouldRepairExistingPermission(page.pageKey, role, existing[0].allowed); const shouldRepairPageName = existing[0].page_name !== page.pageName; if (shouldRepairAllowed || shouldRepairPageName) { await database.local.query( 'UPDATE permissions SET page_name = ?, allowed = ? WHERE id = ?', [ page.pageName, shouldRepairAllowed ? (getDefaultRoleAllowed(page.pageKey, role) ? 1 : 0) : existing[0].allowed, existing[0].id, ] ); } continue; } await database.local.insert('permissions', { page_key: page.pageKey, page_name: page.pageName, role, allowed: getDefaultRoleAllowed(page.pageKey, role) ? 1 : 0, }); } } } async function getSidebarRolePermissions() { const rows = await database.local.query( `SELECT page_key as pageKey, page_name as pageName, role, allowed FROM permissions WHERE page_key IN (${SIDEBAR_PAGES.map(() => '?').join(', ')})`, SIDEBAR_PAGES.map((item) => item.pageKey) ); return rows; } async function ensureDepartmentPagePermissions() { const departments = await database.local.query('SELECT id FROM departments'); for (const department of departments) { const rows = await database.local.query( `SELECT id, page_key as pageKey, page_name as pageName FROM department_page_permissions WHERE department_id = ?`, [department.id] ); const rowMap = rows.reduce((acc, row) => { acc[row.pageKey] = row; return acc; }, {}); for (const page of SIDEBAR_PAGES) { const existing = rowMap[page.pageKey]; if (existing) { if (existing.pageName !== page.pageName) { await database.local.query( `UPDATE department_page_permissions SET page_name = ? WHERE id = ?`, [page.pageName, existing.id] ); } continue; } await database.local.insert('department_page_permissions', { department_id: department.id, page_key: page.pageKey, page_name: page.pageName, allowed: getDefaultDepartmentAllowed(page.pageKey) ? 1 : 0, }); } } } async function getDepartmentPagePermissions(departmentId) { const rows = await database.local.query( `SELECT page_key as pageKey, allowed FROM department_page_permissions WHERE department_id = ?`, [departmentId] ); const rowMap = rows.reduce((acc, row) => { acc[row.pageKey] = row.allowed === 1 || row.allowed === true; return acc; }, {}); return SIDEBAR_PAGES.map((page) => ({ pageKey: page.pageKey, pageName: page.pageName, path: page.path, category: page.category, allowed: rowMap[page.pageKey] === undefined ? getDefaultDepartmentAllowed(page.pageKey) : rowMap[page.pageKey], })); } async function saveDepartmentPagePermissions(departmentId, pagePermissions = []) { const normalized = SIDEBAR_PAGES.map((page) => { const incoming = pagePermissions.find((item) => item.pageKey === page.pageKey); return { pageKey: page.pageKey, pageName: page.pageName, allowed: incoming?.allowed !== undefined ? Boolean(incoming.allowed) : getDefaultDepartmentAllowed(page.pageKey), }; }); await database.local.query('DELETE FROM department_page_permissions WHERE department_id = ?', [departmentId]); for (const permission of normalized) { await database.local.insert('department_page_permissions', { department_id: departmentId, page_key: permission.pageKey, page_name: permission.pageName, allowed: permission.allowed ? 1 : 0, }); } return getDepartmentPagePermissions(departmentId); } async function getUserPageOverrideRows(userId) { return database.local.query( `SELECT page_key as pageKey, allowed FROM user_page_permissions WHERE user_id = ?`, [userId] ); } async function getBaseAllowedPageKeysForUser(user) { if (!user) { return []; } if (user.role === 'admin') { return SIDEBAR_PAGES.map((page) => page.pageKey); } const roleRows = await database.local.query( `SELECT page_key as pageKey, allowed FROM permissions WHERE role = ? AND page_key IN (${SIDEBAR_PAGES.map(() => '?').join(', ')})`, [user.role, ...SIDEBAR_PAGES.map((item) => item.pageKey)] ); const roleAllowed = new Set( SIDEBAR_PAGES.filter((page) => { const row = roleRows.find((item) => item.pageKey === page.pageKey); if (!row) { return getDefaultRoleAllowed(page.pageKey, user.role); } return row.allowed === 1 || row.allowed === true; }).map((page) => page.pageKey) ); if (!user.department_id) { if (user.role === 'admin') { return Array.from(roleAllowed); } return Array.from(roleAllowed).filter((pageKey) => pageKey !== PLANNING_UPLOAD_PAGE_KEY); } const departmentRows = await database.local.query( `SELECT page_key as pageKey, allowed FROM department_page_permissions WHERE department_id = ?`, [user.department_id] ); if (departmentRows.length === 0) { return Array.from(roleAllowed).filter((pageKey) => getDefaultDepartmentAllowed(pageKey)); } const departmentRowMap = departmentRows.reduce((acc, row) => { acc[row.pageKey] = row.allowed === 1 || row.allowed === true; return acc; }, {}); const departmentAllowed = new Set( SIDEBAR_PAGES .filter((page) => departmentRowMap[page.pageKey] === undefined ? getDefaultDepartmentAllowed(page.pageKey) : departmentRowMap[page.pageKey]) .map((page) => page.pageKey) ); let pageKeys = Array.from(roleAllowed).filter((pageKey) => departmentAllowed.has(pageKey)); return pageKeys; } async function getAllowedPageKeysForUser(user) { const basePageKeys = await getBaseAllowedPageKeysForUser(user); if (!user || user.role === 'admin') { return basePageKeys; } const overrideRows = await getUserPageOverrideRows(user.id); const userAllowed = overrideRows .filter((row) => row.allowed === 1 || row.allowed === true) .map((row) => row.pageKey); return Array.from(new Set([...basePageKeys, ...userAllowed])); } async function getUserPagePermissions(userId) { const users = await database.local.query( `SELECT id, username, name, role, department_id FROM users WHERE id = ? LIMIT 1`, [userId] ); const user = users[0]; if (!user) { return null; } const [basePageKeys, overrideRows] = await Promise.all([ getBaseAllowedPageKeysForUser(user), getUserPageOverrideRows(userId), ]); const baseAllowed = new Set(basePageKeys); const overrideMap = overrideRows.reduce((acc, row) => { acc[row.pageKey] = row.allowed === 1 || row.allowed === true; return acc; }, {}); return { user, permissions: SIDEBAR_PAGES.map((page) => { const userAllowed = overrideMap[page.pageKey] === true; return { pageKey: page.pageKey, pageName: page.pageName, path: page.path, category: page.category, baseAllowed: baseAllowed.has(page.pageKey), userAllowed, effectiveAllowed: baseAllowed.has(page.pageKey) || userAllowed, }; }), }; } async function saveUserPagePermissions(userId, pagePermissions = []) { const pageMap = new Map(SIDEBAR_PAGES.map((page) => [page.pageKey, page])); const normalized = pagePermissions .filter((item) => item?.allowed === true && pageMap.has(item.pageKey)) .map((item) => { const page = pageMap.get(item.pageKey); return { pageKey: page.pageKey, pageName: page.pageName, }; }); const deduped = Array.from( normalized.reduce((acc, item) => acc.set(item.pageKey, item), new Map()).values() ); await database.local.query('DELETE FROM user_page_permissions WHERE user_id = ?', [userId]); for (const permission of deduped) { await database.local.insert('user_page_permissions', { user_id: userId, page_key: permission.pageKey, page_name: permission.pageName, allowed: 1, }); } return getUserPagePermissions(userId); } async function getResolvedUserWithPageAccess(userId) { const rows = await database.local.query( `SELECT u.id, u.username, u.name, u.email, u.role, u.status, u.department_id, u.avatar_url, u.auth_source, d.name as departmentName FROM users u LEFT JOIN departments d ON u.department_id = d.id WHERE u.id = ? LIMIT 1`, [userId] ); const user = rows[0]; if (!user) { return null; } const allowedPageKeys = await getAllowedPageKeysForUser(user); return { ...user, picture: user.avatar_url || '', departmentId: user.department_id || null, allowedPageKeys, }; } module.exports = { SIDEBAR_PAGES, ensureSidebarRolePermissions, ensureDepartmentPagePermissions, getAllowedPageKeysForUser, getDepartmentPagePermissions, getResolvedUserWithPageAccess, getSidebarRolePermissions, getUserPagePermissions, saveDepartmentPagePermissions, saveUserPagePermissions, };