Files
M-Project/frontend/static/js/core/permissions.js
Hyungi Ahn 58156da987 🐛 Fix: Project.name → project_name 속성명 수정 및 보고서 시스템 안정화
- backend/routers/reports.py: project.name을 project.project_name으로 수정 (3곳)
- 일일보고서 엑셀 내보내기 오류 해결
- 배포 가이드 업데이트 (DEPLOYMENT_GUIDE_20251028.md)
- 프로젝트 속성명 불일치로 인한 500 에러 해결

Fixes: 'Project' object has no attribute 'name' 오류
2025-10-28 16:36:56 +09:00

268 lines
9.1 KiB
JavaScript

/**
* 단순화된 페이지 권한 관리 시스템
* admin/user 구조에서 페이지별 접근 권한을 관리
*/
class PagePermissionManager {
constructor() {
this.currentUser = null;
this.pagePermissions = new Map();
this.defaultPages = this.initDefaultPages();
}
/**
* 기본 페이지 목록 초기화
*/
initDefaultPages() {
return {
'issues_create': { title: '부적합 등록', defaultAccess: true },
'issues_view': { title: '부적합 조회', defaultAccess: true },
'issues_manage': { title: '부적합 관리', defaultAccess: true },
'issues_inbox': { title: '수신함', defaultAccess: true },
'issues_management': { title: '관리함', defaultAccess: false },
'issues_archive': { title: '폐기함', defaultAccess: false },
'issues_dashboard': { title: '현황판', defaultAccess: true },
'projects_manage': { title: '프로젝트 관리', defaultAccess: false },
'daily_work': { title: '일일 공수', defaultAccess: false },
'reports': { title: '보고서', defaultAccess: false },
'users_manage': { title: '사용자 관리', defaultAccess: false }
};
}
/**
* 사용자 설정
* @param {Object} user - 사용자 객체
*/
setUser(user) {
this.currentUser = user;
this.loadPagePermissions();
}
/**
* 사용자별 페이지 권한 로드
*/
async loadPagePermissions() {
if (!this.currentUser) return;
try {
// API에서 사용자별 페이지 권한 가져오기
const apiUrl = window.API_BASE_URL || 'http://localhost:16080/api';
const response = await fetch(`${apiUrl}/users/${this.currentUser.id}/page-permissions`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
}
});
if (response.ok) {
const pagePermissions = await response.json();
this.pagePermissions.clear(); // 기존 권한 초기화
pagePermissions.forEach(perm => {
this.pagePermissions.set(perm.page_name, perm.can_access);
});
console.log('페이지 권한 로드 완료:', this.pagePermissions);
} else {
console.warn('페이지 권한 로드 실패, 기본 권한 사용');
}
} catch (error) {
console.warn('페이지 권한 로드 실패, 기본 권한 사용:', error);
}
}
/**
* 페이지 접근 권한 체크
* @param {string} pageName - 체크할 페이지명
* @returns {boolean} 접근 권한 여부
*/
canAccessPage(pageName) {
if (!this.currentUser) return false;
// admin은 모든 페이지 접근 가능
if (this.currentUser.role === 'admin') {
return true;
}
// 개별 페이지 권한이 설정되어 있으면 우선 적용
if (this.pagePermissions.has(pageName)) {
return this.pagePermissions.get(pageName);
}
// 기본 권한 확인
const pageConfig = this.defaultPages[pageName];
return pageConfig ? pageConfig.defaultAccess : false;
}
/**
* UI 요소 페이지 권한 제어
* @param {string} selector - CSS 선택자
* @param {string} pageName - 필요한 페이지 권한
* @param {string} action - 'show'|'hide'|'disable'|'enable'
*/
controlElement(selector, pageName, action = 'show') {
const elements = document.querySelectorAll(selector);
const hasAccess = this.canAccessPage(pageName);
elements.forEach(element => {
switch (action) {
case 'show':
element.style.display = hasAccess ? '' : 'none';
break;
case 'hide':
element.style.display = hasAccess ? 'none' : '';
break;
case 'disable':
element.disabled = !hasAccess;
if (!hasAccess) {
element.classList.add('opacity-50', 'cursor-not-allowed');
}
break;
case 'enable':
element.disabled = hasAccess;
if (hasAccess) {
element.classList.remove('opacity-50', 'cursor-not-allowed');
}
break;
}
});
}
/**
* 메뉴 구성 생성
* @returns {Array} 페이지 권한에 따른 메뉴 구성
*/
getMenuConfig() {
const menuItems = [
{
id: 'issues_create',
title: '부적합 등록',
icon: 'fas fa-plus-circle',
path: '#issues/create',
pageName: 'issues_create'
},
{
id: 'issues_view',
title: '부적합 조회',
icon: 'fas fa-search',
path: '#issues/view',
pageName: 'issues_view'
},
{
id: 'issues_manage',
title: '부적합 관리',
icon: 'fas fa-tasks',
path: '#issues/manage',
pageName: 'issues_manage'
},
{
id: 'projects_manage',
title: '프로젝트 관리',
icon: 'fas fa-folder-open',
path: '#projects/manage',
pageName: 'projects_manage'
},
{
id: 'daily_work',
title: '일일 공수',
icon: 'fas fa-calendar-check',
path: '#daily-work',
pageName: 'daily_work'
},
{
id: 'reports',
title: '보고서',
icon: 'fas fa-chart-bar',
path: '#reports',
pageName: 'reports'
},
{
id: 'users_manage',
title: '사용자 관리',
icon: 'fas fa-users-cog',
path: '#users/manage',
pageName: 'users_manage'
}
];
// 페이지 권한에 따라 메뉴 필터링
return menuItems.filter(item => this.canAccessPage(item.pageName));
}
/**
* 페이지 권한 부여
* @param {number} userId - 사용자 ID
* @param {string} pageName - 페이지명
* @param {boolean} canAccess - 접근 허용 여부
* @param {string} notes - 메모
*/
async grantPageAccess(userId, pageName, canAccess, notes = '') {
if (this.currentUser.role !== 'admin') {
throw new Error('관리자만 권한을 설정할 수 있습니다.');
}
try {
const apiUrl = window.API_BASE_URL || 'http://localhost:16080/api';
const response = await fetch(`${apiUrl}/page-permissions/grant`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
},
body: JSON.stringify({
user_id: userId,
page_name: pageName,
can_access: canAccess,
notes: notes
})
});
if (!response.ok) {
throw new Error('페이지 권한 설정 실패');
}
return await response.json();
} catch (error) {
console.error('페이지 권한 설정 오류:', error);
throw error;
}
}
/**
* 사용자 페이지 권한 목록 조회
* @param {number} userId - 사용자 ID
* @returns {Array} 페이지 권한 목록
*/
async getUserPagePermissions(userId) {
try {
const apiUrl = window.API_BASE_URL || 'http://localhost:16080/api';
const response = await fetch(`${apiUrl}/users/${userId}/page-permissions`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
}
});
if (!response.ok) {
throw new Error('페이지 권한 목록 조회 실패');
}
return await response.json();
} catch (error) {
console.error('페이지 권한 목록 조회 오류:', error);
throw error;
}
}
/**
* 모든 페이지 목록과 설명 가져오기
* @returns {Object} 페이지 목록
*/
getAllPages() {
return this.defaultPages;
}
}
// 전역 페이지 권한 관리자 인스턴스
window.pagePermissionManager = new PagePermissionManager();
// 편의 함수들
window.canAccessPage = (pageName) => window.pagePermissionManager.canAccessPage(pageName);
window.controlElement = (selector, pageName, action) => window.pagePermissionManager.controlElement(selector, pageName, action);