refactor: System1 API 인증 체계 SSO 전환 및 마이그레이션 정비
- SSO JWT 인증으로 전환 (auth.service.js) - worker_id → user_id 마이그레이션 완료 - departments 연동, CORS 미들웨어 정리 - 불필요 파일 삭제 (tk_database.db, visitRequestController.js) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,20 +9,12 @@
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const mysql = require('mysql2/promise');
|
||||
const { getDb } = require('../dbPool');
|
||||
const { verifyToken } = require('../middlewares/auth');
|
||||
const { validatePassword, getPasswordError } = require('../utils/passwordValidator');
|
||||
const router = express.Router();
|
||||
const authController = require('../controllers/authController');
|
||||
|
||||
// DB 연결 설정
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'db_hyungi_net',
|
||||
user: process.env.DB_USER || 'hyungi',
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME || 'hyungi'
|
||||
};
|
||||
|
||||
// 로그인 시도 추적 (메모리 기반 - 실제로는 Redis 권장)
|
||||
const loginAttempts = new Map();
|
||||
|
||||
@@ -143,7 +135,7 @@ router.post('/refresh-token', async (req, res) => {
|
||||
return res.status(401).json({ error: '유효하지 않은 토큰입니다.' });
|
||||
}
|
||||
|
||||
const connection = await mysql.createConnection(dbConfig);
|
||||
const connection = await getDb();
|
||||
|
||||
// 사용자 정보 조회
|
||||
const [users] = await connection.execute(
|
||||
@@ -151,8 +143,6 @@ router.post('/refresh-token', async (req, res) => {
|
||||
[decoded.user_id]
|
||||
);
|
||||
|
||||
await connection.end();
|
||||
|
||||
if (users.length === 0) {
|
||||
return res.status(404).json({ error: '사용자를 찾을 수 없습니다.' });
|
||||
}
|
||||
@@ -167,7 +157,7 @@ router.post('/refresh-token', async (req, res) => {
|
||||
access_level: user.access_level,
|
||||
name: user.name || user.username
|
||||
},
|
||||
process.env.JWT_SECRET || 'your-secret-key',
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
||||
);
|
||||
|
||||
@@ -224,7 +214,7 @@ router.post('/change-password', verifyToken, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
|
||||
// 현재 사용자의 비밀번호 조회
|
||||
const [users] = await connection.execute(
|
||||
@@ -290,10 +280,6 @@ router.post('/change-password', verifyToken, async (req, res) => {
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.'
|
||||
});
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -334,7 +320,7 @@ router.post('/admin/change-password', verifyToken, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
|
||||
// 대상 사용자 확인
|
||||
const [users] = await connection.execute(
|
||||
@@ -391,10 +377,6 @@ router.post('/admin/change-password', verifyToken, async (req, res) => {
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.'
|
||||
});
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -453,7 +435,7 @@ router.get('/me', verifyToken, async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.user_id;
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
const [rows] = await connection.execute(
|
||||
'SELECT user_id, username, name, email, access_level, last_login_at, created_at FROM users WHERE user_id = ?',
|
||||
[userId]
|
||||
@@ -477,10 +459,6 @@ router.get('/me', verifyToken, async (req, res) => {
|
||||
} catch (error) {
|
||||
console.error('Get current user error:', error);
|
||||
res.status(500).json({ error: '서버 오류가 발생했습니다.' });
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -516,7 +494,7 @@ router.post('/register', verifyToken, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
|
||||
// 사용자명 중복 체크
|
||||
const [existing] = await connection.execute(
|
||||
@@ -586,10 +564,6 @@ router.post('/register', verifyToken, async (req, res) => {
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.'
|
||||
});
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -600,7 +574,7 @@ router.get('/users', verifyToken, async (req, res) => {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
|
||||
// 기본 쿼리 (role 테이블과 JOIN)
|
||||
let query = `
|
||||
@@ -656,10 +630,6 @@ router.get('/users', verifyToken, async (req, res) => {
|
||||
} catch (error) {
|
||||
console.error('Get users error:', error);
|
||||
res.status(500).json({ error: '서버 오류가 발생했습니다.' });
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -691,7 +661,7 @@ router.put('/users/:id', verifyToken, async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
|
||||
// 사용자 존재 확인
|
||||
const [existing] = await connection.execute(
|
||||
@@ -802,10 +772,6 @@ router.put('/users/:id', verifyToken, async (req, res) => {
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.'
|
||||
});
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -834,7 +800,7 @@ router.delete('/users/:id', verifyToken, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
|
||||
// 사용자 존재 확인
|
||||
const [existing] = await connection.execute(
|
||||
@@ -871,10 +837,6 @@ router.delete('/users/:id', verifyToken, async (req, res) => {
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.'
|
||||
});
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -887,17 +849,13 @@ router.post('/logout', verifyToken, async (req, res) => {
|
||||
// 로그아웃 시간 기록 (선택사항)
|
||||
let connection;
|
||||
try {
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
await connection.execute(
|
||||
'UPDATE login_logs SET logout_time = NOW() WHERE user_id = ? AND logout_time IS NULL ORDER BY login_time DESC LIMIT 1',
|
||||
[req.user.user_id]
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('로그아웃 기록 실패:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
@@ -916,7 +874,7 @@ router.get('/login-history', verifyToken, async (req, res) => {
|
||||
const { limit = 50, offset = 0 } = req.query;
|
||||
const userId = req.user.user_id;
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
connection = await getDb();
|
||||
|
||||
// 본인의 로그인 이력만 조회 (관리자는 전체 조회 가능)
|
||||
let query = `
|
||||
@@ -958,10 +916,6 @@ router.get('/login-history', verifyToken, async (req, res) => {
|
||||
} catch (error) {
|
||||
console.error('Get login history error:', error);
|
||||
res.status(500).json({ error: '서버 오류가 발생했습니다.' });
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ router.get('/check-overwrite', (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🔍 덮어쓰기 권한 확인: 날짜=${date}, 작업자=${user_id} (누적입력모드)`);
|
||||
|
||||
// 누적입력 시스템에서는 항상 덮어쓰기 가능 (실제로는 누적만 함)
|
||||
res.json({
|
||||
|
||||
@@ -8,7 +8,6 @@ router.post('/setup-monthly-status', async (req, res) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
|
||||
console.log('📊 월별 집계 테이블 생성 중...');
|
||||
|
||||
// 1. 월별 작업자 상태 집계 테이블
|
||||
await db.execute(`
|
||||
@@ -86,7 +85,6 @@ router.post('/setup-monthly-status', async (req, res) => {
|
||||
) COMMENT='월별 일자별 요약 테이블 (캘린더 최적화용)'
|
||||
`);
|
||||
|
||||
console.log('📊 집계 프로시저 생성 중...');
|
||||
|
||||
// 3. 집계 업데이트 프로시저
|
||||
await db.execute(`DROP PROCEDURE IF EXISTS UpdateMonthlyWorkerStatus`);
|
||||
@@ -239,7 +237,6 @@ router.post('/setup-monthly-status', async (req, res) => {
|
||||
END
|
||||
`);
|
||||
|
||||
console.log('📊 트리거 생성 중...');
|
||||
|
||||
// 4. 트리거 생성
|
||||
await db.execute(`DROP TRIGGER IF EXISTS tr_daily_work_reports_insert`);
|
||||
@@ -276,7 +273,6 @@ router.post('/setup-monthly-status', async (req, res) => {
|
||||
END
|
||||
`);
|
||||
|
||||
console.log('📊 기존 데이터로 집계 테이블 초기화 중...');
|
||||
|
||||
// 5. 기존 작업 데이터로 집계 테이블 초기화
|
||||
const [existingDates] = await db.execute(`
|
||||
@@ -302,7 +298,6 @@ router.post('/setup-monthly-status', async (req, res) => {
|
||||
}
|
||||
|
||||
if (i % 100 === 0) {
|
||||
console.log(`📊 집계 초기화 진행률: ${processedCount}/${existingDates.length}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +324,7 @@ router.post('/setup-monthly-status', async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 월별 집계 시스템 설정 오류:', error);
|
||||
console.error(' 월별 집계 시스템 설정 오류:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '월별 집계 시스템 설정 중 오류가 발생했습니다.',
|
||||
@@ -340,12 +335,10 @@ router.post('/setup-monthly-status', async (req, res) => {
|
||||
|
||||
router.post('/setup-attendance-db', async (req, res) => {
|
||||
try {
|
||||
console.log('🚀 근태 관리 DB 설정 API 호출됨');
|
||||
|
||||
const db = await getDb();
|
||||
|
||||
// 1. 근로 유형 테이블 생성
|
||||
console.log('📋 근로 유형 테이블 생성 중...');
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS work_attendance_types (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
@@ -359,7 +352,6 @@ router.post('/setup-attendance-db', async (req, res) => {
|
||||
`);
|
||||
|
||||
// 2. 휴가 유형 테이블 생성
|
||||
console.log('🏖️ 휴가 유형 테이블 생성 중...');
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS vacation_types (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
@@ -374,7 +366,6 @@ router.post('/setup-attendance-db', async (req, res) => {
|
||||
`);
|
||||
|
||||
// 3. 일일 근태 기록 테이블 생성
|
||||
console.log('📊 일일 근태 기록 테이블 생성 중...');
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS daily_attendance_records (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
@@ -394,7 +385,6 @@ router.post('/setup-attendance-db', async (req, res) => {
|
||||
`);
|
||||
|
||||
// 4. 작업자별 휴가 잔여 관리 테이블 생성
|
||||
console.log('👥 작업자별 휴가 잔여 관리 테이블 생성 중...');
|
||||
await db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS worker_vacation_balance (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
@@ -410,7 +400,6 @@ router.post('/setup-attendance-db', async (req, res) => {
|
||||
`);
|
||||
|
||||
// 5. 기본 데이터 삽입
|
||||
console.log('📝 기본 데이터 삽입 중...');
|
||||
|
||||
// 근로 유형 기본 데이터
|
||||
await db.execute(`
|
||||
@@ -446,7 +435,7 @@ router.post('/setup-attendance-db', async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ DB 설정 API 오류:', error);
|
||||
console.error(' DB 설정 API 오류:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'DB 설정 중 오류가 발생했습니다.',
|
||||
@@ -460,7 +449,6 @@ router.post('/add-overtime-warning', async (req, res) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
|
||||
console.log('⚠️ 12시간 초과 상태 컬럼 추가 중...');
|
||||
|
||||
// 1. monthly_summary 테이블에 컬럼 추가
|
||||
try {
|
||||
@@ -468,10 +456,8 @@ router.post('/add-overtime-warning', async (req, res) => {
|
||||
ALTER TABLE monthly_summary
|
||||
ADD COLUMN overtime_warning_workers INT DEFAULT 0 COMMENT '확인필요(12시간초과) 작업자 수' AFTER error_workers
|
||||
`);
|
||||
console.log('✅ overtime_warning_workers 컬럼 추가 완료');
|
||||
} catch (error) {
|
||||
if (error.code === 'ER_DUP_FIELDNAME') {
|
||||
console.log('ℹ️ overtime_warning_workers 컬럼이 이미 존재합니다.');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
@@ -482,10 +468,8 @@ router.post('/add-overtime-warning', async (req, res) => {
|
||||
ALTER TABLE monthly_summary
|
||||
ADD COLUMN has_overtime_warning BOOLEAN DEFAULT FALSE COMMENT '확인필요 상태 있음' AFTER has_errors
|
||||
`);
|
||||
console.log('✅ has_overtime_warning 컬럼 추가 완료');
|
||||
} catch (error) {
|
||||
if (error.code === 'ER_DUP_FIELDNAME') {
|
||||
console.log('ℹ️ has_overtime_warning 컬럼이 이미 존재합니다.');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
@@ -551,7 +535,6 @@ router.post('/add-overtime-warning', async (req, res) => {
|
||||
last_updated = CURRENT_TIMESTAMP;
|
||||
END
|
||||
`);
|
||||
console.log('✅ UpdateDailySummary 프로시저 업데이트 완료');
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
@@ -561,7 +544,7 @@ router.post('/add-overtime-warning', async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 12시간 초과 상태 설정 오류:', error);
|
||||
console.error(' 12시간 초과 상태 설정 오류:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '12시간 초과 상태 설정 실패',
|
||||
@@ -575,7 +558,6 @@ router.post('/migrate-existing-data', async (req, res) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
|
||||
console.log('🔄 기존 데이터 마이그레이션 시작...');
|
||||
|
||||
// 1. 기존 데이터 범위 확인
|
||||
const [dateRange] = await db.execute(`
|
||||
@@ -595,12 +577,10 @@ router.post('/migrate-existing-data', async (req, res) => {
|
||||
}
|
||||
|
||||
const { min_date, max_date, total_reports } = dateRange[0];
|
||||
console.log(`📊 데이터 범위: ${min_date} ~ ${max_date} (총 ${total_reports}건)`);
|
||||
|
||||
// 2. 기존 monthly_worker_status, monthly_summary 데이터 삭제
|
||||
await db.execute('DELETE FROM monthly_summary');
|
||||
await db.execute('DELETE FROM monthly_worker_status');
|
||||
console.log('🗑️ 기존 집계 데이터 삭제 완료');
|
||||
|
||||
// 3. 날짜별로 작업자별 상태 재계산
|
||||
const [allDates] = await db.execute(`
|
||||
@@ -610,7 +590,6 @@ router.post('/migrate-existing-data', async (req, res) => {
|
||||
ORDER BY report_date, worker_id
|
||||
`, [min_date, max_date]);
|
||||
|
||||
console.log(`🔄 ${allDates.length}개 날짜-작업자 조합 처리 중...`);
|
||||
|
||||
let processedCount = 0;
|
||||
for (const { report_date, worker_id } of allDates) {
|
||||
@@ -620,10 +599,9 @@ router.post('/migrate-existing-data', async (req, res) => {
|
||||
processedCount++;
|
||||
|
||||
if (processedCount % 50 === 0) {
|
||||
console.log(`📈 진행률: ${processedCount}/${allDates.length} (${Math.round(processedCount/allDates.length*100)}%)`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ ${report_date} ${worker_id} 처리 오류:`, error.message);
|
||||
console.error(` ${report_date} ${worker_id} 처리 오류:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,7 +609,6 @@ router.post('/migrate-existing-data', async (req, res) => {
|
||||
const [workerStatusCount] = await db.execute('SELECT COUNT(*) as count FROM monthly_worker_status');
|
||||
const [summaryCount] = await db.execute('SELECT COUNT(*) as count FROM monthly_summary');
|
||||
|
||||
console.log(`✅ 마이그레이션 완료:`);
|
||||
console.log(` - monthly_worker_status: ${workerStatusCount[0].count}건`);
|
||||
console.log(` - monthly_summary: ${summaryCount[0].count}건`);
|
||||
|
||||
@@ -649,7 +626,7 @@ router.post('/migrate-existing-data', async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 데이터 마이그레이션 오류:', error);
|
||||
console.error(' 데이터 마이그레이션 오류:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '데이터 마이그레이션 실패',
|
||||
@@ -705,7 +682,7 @@ router.get('/check-data-status', async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ DB 상태 확인 오류:', error);
|
||||
console.error(' DB 상태 확인 오류:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'DB 상태 확인 실패',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const systemController = require('../controllers/systemController');
|
||||
const userController = require('../controllers/userController');
|
||||
const { requireAuth, requireRole } = require('../middlewares/auth');
|
||||
|
||||
// 모든 라우트에 인증 및 시스템 권한 확인 적용
|
||||
@@ -46,31 +47,31 @@ router.get('/users/stats', systemController.getUserStats);
|
||||
* GET /api/system/users
|
||||
* 모든 사용자 목록 조회
|
||||
*/
|
||||
router.get('/users', systemController.getAllUsers);
|
||||
router.get('/users', userController.getAllUsers);
|
||||
|
||||
/**
|
||||
* POST /api/system/users
|
||||
* 새 사용자 생성
|
||||
*/
|
||||
router.post('/users', systemController.createUser);
|
||||
router.post('/users', userController.createUser);
|
||||
|
||||
/**
|
||||
* PUT /api/system/users/:id
|
||||
* 사용자 정보 수정
|
||||
*/
|
||||
router.put('/users/:id', systemController.updateUser);
|
||||
router.put('/users/:id', userController.updateUser);
|
||||
|
||||
/**
|
||||
* DELETE /api/system/users/:id
|
||||
* 사용자 삭제
|
||||
*/
|
||||
router.delete('/users/:id', systemController.deleteUser);
|
||||
router.delete('/users/:id', userController.deleteUser);
|
||||
|
||||
/**
|
||||
* POST /api/system/users/:id/reset-password
|
||||
* 사용자 비밀번호 재설정
|
||||
*/
|
||||
router.post('/users/:id/reset-password', systemController.resetUserPassword);
|
||||
router.post('/users/:id/reset-password', userController.resetUserPassword);
|
||||
|
||||
// ===== 시스템 로그 관련 =====
|
||||
|
||||
@@ -219,7 +220,6 @@ router.post('/migrations/fix-work-type-id', async (req, res) => {
|
||||
const { getDb } = require('../dbPool');
|
||||
const db = await getDb();
|
||||
|
||||
console.log('🔄 TBM 기반 작업보고서 work_type_id 수정 시작...');
|
||||
|
||||
// 1. 수정 대상 확인
|
||||
const [checkResult] = await db.query(`
|
||||
@@ -256,7 +256,6 @@ router.post('/migrations/fix-work-type-id', async (req, res) => {
|
||||
AND dwr.work_type_id != ta.task_id
|
||||
`);
|
||||
|
||||
console.log(`✅ 업데이트 완료: ${updateResult.affectedRows}개 레코드 수정됨`);
|
||||
|
||||
// 3. 수정된 샘플 조회
|
||||
const [samples] = await db.query(`
|
||||
|
||||
Reference in New Issue
Block a user