// 시스템 관리 라우트 const express = require('express'); const router = express.Router(); const systemController = require('../controllers/systemController'); const { requireAuth, requireRole } = require('../middlewares/auth'); // 모든 라우트에 인증 및 시스템 권한 확인 적용 router.use(requireAuth); router.use(requireRole('system')); // ===== 시스템 상태 관련 ===== /** * GET /api/system/status * 시스템 전체 상태 확인 */ router.get('/status', systemController.getSystemStatus); /** * GET /api/system/db-status * 데이터베이스 상태 확인 */ router.get('/db-status', systemController.getDatabaseStatus); /** * GET /api/system/alerts * 시스템 알림 조회 */ router.get('/alerts', systemController.getSystemAlerts); /** * GET /api/system/recent-activities * 최근 시스템 활동 조회 */ router.get('/recent-activities', systemController.getRecentActivities); // ===== 사용자 관리 관련 ===== /** * GET /api/system/users/stats * 사용자 통계 조회 */ router.get('/users/stats', systemController.getUserStats); /** * GET /api/system/users * 모든 사용자 목록 조회 */ router.get('/users', systemController.getAllUsers); /** * POST /api/system/users * 새 사용자 생성 */ router.post('/users', systemController.createUser); /** * PUT /api/system/users/:id * 사용자 정보 수정 */ router.put('/users/:id', systemController.updateUser); /** * DELETE /api/system/users/:id * 사용자 삭제 */ router.delete('/users/:id', systemController.deleteUser); /** * POST /api/system/users/:id/reset-password * 사용자 비밀번호 재설정 */ router.post('/users/:id/reset-password', systemController.resetUserPassword); // ===== 시스템 로그 관련 ===== /** * GET /api/system/logs/login * 로그인 로그 조회 */ router.get('/logs/login', async (req, res) => { try { const { getDb } = require('../dbPool'); const db = await getDb(); const { page = 1, limit = 50, status, user_id, start_date, end_date } = req.query; const offset = (page - 1) * limit; let whereClause = '1=1'; const params = []; if (status) { whereClause += ' AND ll.login_status = ?'; params.push(status); } if (user_id) { whereClause += ' AND ll.user_id = ?'; params.push(user_id); } if (start_date) { whereClause += ' AND ll.login_time >= ?'; params.push(start_date); } if (end_date) { whereClause += ' AND ll.login_time <= ?'; params.push(end_date); } const [logs] = await db.query(` SELECT ll.log_id, ll.user_id, u.username, u.name, ll.login_time, ll.ip_address, ll.user_agent, ll.login_status, ll.failure_reason FROM login_logs ll LEFT JOIN Users u ON ll.user_id = u.user_id WHERE ${whereClause} ORDER BY ll.login_time DESC LIMIT ? OFFSET ? `, [...params, parseInt(limit), parseInt(offset)]); const [totalCount] = await db.query(` SELECT COUNT(*) as count FROM login_logs ll WHERE ${whereClause} `, params); res.json({ success: true, data: { logs, pagination: { page: parseInt(page), limit: parseInt(limit), total: totalCount[0].count, pages: Math.ceil(totalCount[0].count / limit) } } }); } catch (error) { console.error('로그인 로그 조회 오류:', error); res.status(500).json({ success: false, error: '로그인 로그를 조회할 수 없습니다.' }); } }); /** * GET /api/system/logs/password-changes * 비밀번호 변경 로그 조회 */ router.get('/logs/password-changes', async (req, res) => { try { const { getDb } = require('../dbPool'); const db = await getDb(); const { page = 1, limit = 50 } = req.query; const offset = (page - 1) * limit; const [logs] = await db.query(` SELECT pcl.log_id, pcl.user_id, u.username, u.name, pcl.changed_by_user_id, admin.username as changed_by_username, admin.name as changed_by_name, pcl.changed_at, pcl.change_type, pcl.ip_address FROM password_change_logs pcl LEFT JOIN Users u ON pcl.user_id = u.user_id LEFT JOIN Users admin ON pcl.changed_by_user_id = admin.user_id ORDER BY pcl.changed_at DESC LIMIT ? OFFSET ? `, [parseInt(limit), parseInt(offset)]); const [totalCount] = await db.query('SELECT COUNT(*) as count FROM password_change_logs'); res.json({ success: true, data: { logs, pagination: { page: parseInt(page), limit: parseInt(limit), total: totalCount[0].count, pages: Math.ceil(totalCount[0].count / limit) } } }); } catch (error) { console.error('비밀번호 변경 로그 조회 오류:', error); res.status(500).json({ success: false, error: '비밀번호 변경 로그를 조회할 수 없습니다.' }); } }); /** * POST /api/system/migrations/fix-work-type-id * TBM 기반 작업보고서의 work_type_id를 task_id로 수정 */ router.post('/migrations/fix-work-type-id', async (req, res) => { try { const { getDb } = require('../dbPool'); const db = await getDb(); console.log('🔄 TBM 기반 작업보고서 work_type_id 수정 시작...'); // 1. 수정 대상 확인 const [checkResult] = await db.query(` SELECT dwr.id, dwr.work_type_id as current_work_type_id, ta.task_id as correct_task_id, w.worker_name, dwr.report_date FROM daily_work_reports dwr INNER JOIN tbm_team_assignments ta ON dwr.tbm_assignment_id = ta.assignment_id INNER JOIN workers w ON dwr.user_id = w.user_id WHERE dwr.tbm_assignment_id IS NOT NULL AND ta.task_id IS NOT NULL AND dwr.work_type_id != ta.task_id ORDER BY dwr.report_date DESC `); if (checkResult.length === 0) { return res.json({ success: true, message: '수정할 데이터가 없습니다.', data: { affected_rows: 0, samples: [] } }); } // 2. 업데이트 실행 const [updateResult] = await db.query(` UPDATE daily_work_reports dwr INNER JOIN tbm_team_assignments ta ON dwr.tbm_assignment_id = ta.assignment_id SET dwr.work_type_id = ta.task_id WHERE dwr.tbm_assignment_id IS NOT NULL AND ta.task_id IS NOT NULL AND dwr.work_type_id != ta.task_id `); console.log(`✅ 업데이트 완료: ${updateResult.affectedRows}개 레코드 수정됨`); // 3. 수정된 샘플 조회 const [samples] = await db.query(` SELECT dwr.id, dwr.work_type_id, t.task_name, wt.name as work_type_name, w.worker_name, dwr.report_date FROM daily_work_reports dwr INNER JOIN tbm_team_assignments ta ON dwr.tbm_assignment_id = ta.assignment_id LEFT JOIN tasks t ON dwr.work_type_id = t.task_id LEFT JOIN work_types wt ON t.work_type_id = wt.id LEFT JOIN workers w ON dwr.user_id = w.user_id WHERE dwr.tbm_assignment_id IS NOT NULL ORDER BY dwr.report_date DESC LIMIT 10 `); res.json({ success: true, message: `${updateResult.affectedRows}개 레코드가 수정되었습니다.`, data: { affected_rows: updateResult.affectedRows, before_count: checkResult.length, samples: samples.map(s => ({ id: s.id, worker: s.worker_name, date: s.report_date, task: s.task_name, work_type: s.work_type_name })) } }); } catch (error) { console.error('마이그레이션 실패:', error); res.status(500).json({ success: false, error: '마이그레이션 실패: ' + error.message }); } }); /** * GET /api/system/logs/activity * 활동 로그 조회 (activity_logs 테이블이 있는 경우) */ router.get('/logs/activity', async (req, res) => { try { const { getDb } = require('../dbPool'); const db = await getDb(); const { page = 1, limit = 50, activity_type, user_id } = req.query; const offset = (page - 1) * limit; let whereClause = '1=1'; const params = []; if (activity_type) { whereClause += ' AND al.activity_type = ?'; params.push(activity_type); } if (user_id) { whereClause += ' AND al.user_id = ?'; params.push(user_id); } const [logs] = await db.query(` SELECT al.log_id, al.user_id, u.username, u.name, al.activity_type, al.table_name, al.record_id, al.action, al.ip_address, al.user_agent, al.created_at FROM activity_logs al LEFT JOIN Users u ON al.user_id = u.user_id WHERE ${whereClause} ORDER BY al.created_at DESC LIMIT ? OFFSET ? `, [...params, parseInt(limit), parseInt(offset)]); const [totalCount] = await db.query(` SELECT COUNT(*) as count FROM activity_logs al WHERE ${whereClause} `, params); res.json({ success: true, data: { logs, pagination: { page: parseInt(page), limit: parseInt(limit), total: totalCount[0].count, pages: Math.ceil(totalCount[0].count / limit) } } }); } catch (error) { console.error('활동 로그 조회 오류:', error); res.status(500).json({ success: false, error: '활동 로그를 조회할 수 없습니다.' }); } }); module.exports = router;