Files
TK-FB-Project/api.hyungi.net/models/pageAccessModel.js
Hyungi Ahn 4d0c4c0801 feat: TBM 시스템 구축 및 페이지 권한 관리 기능 추가
## 주요 변경사항

### 1. TBM (Tool Box Meeting) 시스템 구축
- **데이터베이스 스키마** (5개 테이블 생성)
  - tbm_sessions: TBM 세션 관리
  - tbm_team_assignments: 팀 구성 관리
  - tbm_safety_checks: 안전 체크리스트 마스터 (17개 항목)
  - tbm_safety_records: 안전 체크 기록
  - team_handovers: 작업 인계 관리

- **API 엔드포인트** (17개)
  - TBM 세션 CRUD
  - 팀 구성 관리
  - 안전 체크리스트
  - 작업 인계
  - 통계 및 리포트

- **프론트엔드**
  - TBM 관리 페이지 (/pages/work/tbm.html)
  - 모달 기반 UI (세션 생성, 팀 구성, 안전 체크)

### 2. 페이지 권한 관리 시스템
- 페이지별 접근 권한 설정 기능
- 관리자 페이지 (/pages/admin/page-access.html)
- 사용자별 페이지 권한 부여/회수
- TBM 페이지 등록 및 권한 연동

### 3. 네비게이션 role 표시 버그 수정
- load-navbar.js: case-insensitive role 매칭 적용
- JWT의 "Admin" role이 "관리자"로 정상 표시
- admin-only 메뉴 항목 정상 표시

### 4. 대시보드 개선
- 작업 현황 테이블 가독성 향상
- 고대비 색상 및 명확한 구분선 적용
- 이모지 제거 및 SVG 아이콘 적용

### 5. 문서화
- TBM 배포 가이드 작성 (docs/TBM_DEPLOYMENT_GUIDE.md)
- 데이터베이스 스키마 상세 기록
- 배포 절차 및 체크리스트 제공

## 기술 스택
- Backend: Node.js, Express, MySQL
- Frontend: Vanilla JavaScript, HTML5, CSS3
- Database: MySQL (InnoDB)

## 파일 변경사항

### 신규 파일
- api.hyungi.net/db/migrations/20260120000000_create_tbm_system.js
- api.hyungi.net/db/migrations/20260120000001_add_tbm_page.js
- api.hyungi.net/models/tbmModel.js
- api.hyungi.net/models/pageAccessModel.js
- api.hyungi.net/controllers/tbmController.js
- api.hyungi.net/controllers/pageAccessController.js
- api.hyungi.net/routes/tbmRoutes.js
- web-ui/pages/work/tbm.html
- web-ui/pages/admin/page-access.html
- web-ui/js/page-access-management.js
- docs/TBM_DEPLOYMENT_GUIDE.md

### 수정 파일
- api.hyungi.net/config/routes.js (TBM 라우트 추가)
- web-ui/js/load-navbar.js (role 매칭 버그 수정)
- web-ui/pages/admin/workers.html (HTML 구조 수정)
- web-ui/pages/dashboard.html (이모지 제거)
- web-ui/css/design-system.css (색상 팔레트 추가)
- web-ui/css/modern-dashboard.css (가독성 개선)
- web-ui/js/modern-dashboard.js (SVG 아이콘 적용)

## 배포 시 주의사항
⚠️ 본 서버 배포 시 반드시 마이그레이션 실행 필요:
```bash
npm run db:migrate
```

상세한 배포 절차는 docs/TBM_DEPLOYMENT_GUIDE.md 참조

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-20 15:38:17 +09:00

161 lines
4.3 KiB
JavaScript

// models/pageAccessModel.js
const db = require('../db/connection');
const PageAccessModel = {
// 사용자의 페이지 권한 조회
getUserPageAccess: (userId, callback) => {
const sql = `
SELECT
p.id,
p.page_key,
p.page_name,
p.page_path,
p.category,
p.is_admin_only,
COALESCE(upa.can_access, 0) as can_access,
upa.granted_at,
upa.granted_by,
granter.username as granted_by_username
FROM pages p
LEFT JOIN user_page_access upa ON p.id = upa.page_id AND upa.user_id = ?
LEFT JOIN users granter ON upa.granted_by = granter.user_id
WHERE p.is_admin_only = 0
ORDER BY p.category, p.display_order
`;
db.query(sql, [userId], callback);
},
// 모든 페이지 목록 조회
getAllPages: (callback) => {
const sql = `
SELECT
id,
page_key,
page_name,
page_path,
category,
description,
is_admin_only,
display_order
FROM pages
WHERE is_admin_only = 0
ORDER BY category, display_order
`;
db.query(sql, callback);
},
// 페이지 권한 부여
grantPageAccess: (userId, pageId, grantedBy, callback) => {
const sql = `
INSERT INTO user_page_access (user_id, page_id, can_access, granted_by, granted_at)
VALUES (?, ?, 1, ?, NOW())
ON DUPLICATE KEY UPDATE
can_access = 1,
granted_by = ?,
granted_at = NOW()
`;
db.query(sql, [userId, pageId, grantedBy, grantedBy], callback);
},
// 페이지 권한 회수
revokePageAccess: (userId, pageId, callback) => {
const sql = `
DELETE FROM user_page_access
WHERE user_id = ? AND page_id = ?
`;
db.query(sql, [userId, pageId], callback);
},
// 여러 페이지 권한 일괄 설정
setUserPageAccess: (userId, pageIds, grantedBy, callback) => {
db.beginTransaction((err) => {
if (err) return callback(err);
// 기존 권한 모두 삭제
const deleteSql = 'DELETE FROM user_page_access WHERE user_id = ?';
db.query(deleteSql, [userId], (err) => {
if (err) {
return db.rollback(() => callback(err));
}
// 새 권한이 없으면 커밋하고 종료
if (!pageIds || pageIds.length === 0) {
return db.commit((err) => {
if (err) return db.rollback(() => callback(err));
callback(null, { affectedRows: 0 });
});
}
// 새 권한 추가
const values = pageIds.map(pageId => [userId, pageId, 1, grantedBy]);
const insertSql = `
INSERT INTO user_page_access (user_id, page_id, can_access, granted_by, granted_at)
VALUES ?
`;
db.query(insertSql, [values], (err, result) => {
if (err) {
return db.rollback(() => callback(err));
}
db.commit((err) => {
if (err) return db.rollback(() => callback(err));
callback(null, result);
});
});
});
});
},
// 특정 페이지 접근 권한 확인
checkPageAccess: (userId, pageKey, callback) => {
const sql = `
SELECT
COALESCE(upa.can_access, 0) as can_access,
p.is_admin_only
FROM pages p
LEFT JOIN user_page_access upa ON p.id = upa.page_id AND upa.user_id = ?
WHERE p.page_key = ?
`;
db.query(sql, [userId, pageKey], (err, results) => {
if (err) return callback(err);
if (results.length === 0) return callback(null, { can_access: false });
callback(null, results[0]);
});
},
// 계정이 있는 작업자 목록 조회 (권한 관리용)
getUsersWithAccounts: (callback) => {
const sql = `
SELECT
u.user_id,
u.username,
u.name,
u.role_id,
r.name as role_name,
u.worker_id,
w.worker_name,
w.job_type,
COUNT(upa.page_id) as granted_pages_count
FROM users u
LEFT JOIN roles r ON u.role_id = r.id
LEFT JOIN workers w ON u.worker_id = w.worker_id
LEFT JOIN user_page_access upa ON u.user_id = upa.user_id AND upa.can_access = 1
WHERE u.is_active = 1
AND u.role_id IN (4, 5)
GROUP BY u.user_id
ORDER BY w.worker_name, u.username
`;
db.query(sql, callback);
}
};
module.exports = PageAccessModel;