diff --git a/api.hyungi.net/models/projectModel.js b/api.hyungi.net/models/projectModel.js
index 1b1b976..6c3ee64 100644
--- a/api.hyungi.net/models/projectModel.js
+++ b/api.hyungi.net/models/projectModel.js
@@ -10,7 +10,7 @@ const create = async (project, callback) => {
} = project;
const [result] = await db.query(
- `INSERT INTO Projects
+ `INSERT INTO projects
(job_no, project_name, contract_date, due_date, delivery_method, site, pm)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[job_no, project_name, contract_date, due_date, delivery_method, site, pm]
@@ -26,7 +26,7 @@ const getAll = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
- `SELECT * FROM Projects ORDER BY project_id DESC`
+ `SELECT * FROM projects ORDER BY project_id DESC`
);
callback(null, rows);
} catch (err) {
@@ -38,7 +38,7 @@ const getById = async (project_id, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
- `SELECT * FROM Projects WHERE project_id = ?`,
+ `SELECT * FROM projects WHERE project_id = ?`,
[project_id]
);
callback(null, rows[0]);
@@ -57,7 +57,7 @@ const update = async (project, callback) => {
} = project;
const [result] = await db.query(
- `UPDATE Projects
+ `UPDATE projects
SET job_no = ?,
project_name = ?,
contract_date = ?,
@@ -79,7 +79,7 @@ const remove = async (project_id, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
- `DELETE FROM Projects WHERE project_id = ?`,
+ `DELETE FROM projects WHERE project_id = ?`,
[project_id]
);
callback(null, result.affectedRows);
diff --git a/api.hyungi.net/models/taskModel.js b/api.hyungi.net/models/taskModel.js
index 6ee247d..24ca845 100644
--- a/api.hyungi.net/models/taskModel.js
+++ b/api.hyungi.net/models/taskModel.js
@@ -7,7 +7,7 @@ const create = async (task, callback) => {
const { category, subcategory, task_name, description } = task;
const [result] = await db.query(
- `INSERT INTO Tasks (category, subcategory, task_name, description)
+ `INSERT INTO tasks (category, subcategory, task_name, description)
VALUES (?, ?, ?, ?)`,
[category, subcategory, task_name, description]
);
@@ -23,7 +23,7 @@ const getAll = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
- `SELECT * FROM Tasks ORDER BY task_id DESC`
+ `SELECT * FROM tasks ORDER BY task_id DESC`
);
callback(null, rows);
} catch (err) {
@@ -36,7 +36,7 @@ const getById = async (task_id, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
- `SELECT * FROM Tasks WHERE task_id = ?`,
+ `SELECT * FROM tasks WHERE task_id = ?`,
[task_id]
);
callback(null, rows[0]);
@@ -52,7 +52,7 @@ const update = async (task, callback) => {
const { task_id, category, subcategory, task_name, description } = task;
const [result] = await db.query(
- `UPDATE Tasks
+ `UPDATE tasks
SET category = ?,
subcategory = ?,
task_name = ?,
@@ -72,7 +72,7 @@ const remove = async (task_id, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
- `DELETE FROM Tasks WHERE task_id = ?`,
+ `DELETE FROM tasks WHERE task_id = ?`,
[task_id]
);
callback(null, result.affectedRows);
diff --git a/docker-compose.yml b/docker-compose.yml
index 87a974d..dd0676a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -45,6 +45,8 @@ services:
- DB_NAME=hyungi
- DB_ROOT_PASSWORD=tkfb2024!
- JWT_SECRET=tkfb_jwt_secret_2024_hyungi_secure_key
+ - JWT_EXPIRES_IN=7d
+ - JWT_REFRESH_EXPIRES_IN=30d
volumes:
- ./api.hyungi.net/public/img:/usr/src/app/public/img:ro
- ./api.hyungi.net/uploads:/usr/src/app/uploads
diff --git a/web-ui/js/api-config.js b/web-ui/js/api-config.js
index 3ec1610..a6881cd 100644
--- a/web-ui/js/api-config.js
+++ b/web-ui/js/api-config.js
@@ -34,15 +34,45 @@ window.API_BASE_URL = API_URL;
function ensureAuthenticated() {
const token = localStorage.getItem('token');
- if (!token || token === 'undefined') {
- alert('로그인이 필요합니다');
- localStorage.removeItem('token');
- window.location.href = '/';
- return null;
+ if (!token || token === 'undefined' || token === 'null') {
+ console.log('🚨 인증되지 않은 사용자. 로그인 페이지로 이동합니다.');
+ clearAuthData(); // 만약을 위해 한번 더 정리
+ window.location.href = '/index.html';
+ return false; // 이후 코드 실행 방지
}
+
+ // 토큰 만료 확인
+ if (isTokenExpired(token)) {
+ console.log('🚨 토큰이 만료되었습니다. 로그인 페이지로 이동합니다.');
+ clearAuthData();
+ alert('세션이 만료되었습니다. 다시 로그인해주세요.');
+ window.location.href = '/index.html';
+ return false;
+ }
+
return token;
}
+// 토큰 만료 확인 함수
+function isTokenExpired(token) {
+ try {
+ const payload = JSON.parse(atob(token.split('.')[1]));
+ const currentTime = Math.floor(Date.now() / 1000);
+ return payload.exp < currentTime;
+ } catch (error) {
+ console.error('토큰 파싱 오류:', error);
+ return true; // 파싱 실패 시 만료된 것으로 간주
+ }
+}
+
+// 인증 데이터 정리 함수
+function clearAuthData() {
+ localStorage.removeItem('token');
+ localStorage.removeItem('user');
+ localStorage.removeItem('userInfo');
+ localStorage.removeItem('currentUser');
+}
+
function getAuthHeaders() {
const token = localStorage.getItem('token');
return {
@@ -72,11 +102,11 @@ async function apiCall(url, options = {}) {
// 인증 만료 처리
if (response.status === 401) {
- console.error('❌ 인증 만료');
- localStorage.removeItem('token');
- alert('인증이 만료되었습니다. 다시 로그인해주세요.');
- window.location.href = '/';
- return;
+ console.error('🚨 인증 실패: 토큰이 만료되었거나 유효하지 않습니다.');
+ clearAuthData();
+ alert('세션이 만료되었습니다. 다시 로그인해주세요.');
+ window.location.href = '/index.html';
+ throw new Error('인증에 실패했습니다.');
}
// 응답 실패 처리
@@ -143,10 +173,23 @@ window.ensureAuthenticated = ensureAuthenticated;
window.getAuthHeaders = getAuthHeaders;
window.apiCall = apiCall;
window.testApiConnection = testApiConnection;
+window.isTokenExpired = isTokenExpired;
+window.clearAuthData = clearAuthData;
// 개발 모드에서 자동 테스트
if (window.location.hostname === 'localhost' || window.location.hostname.startsWith('192.168.')) {
setTimeout(() => {
testApiConnection();
}, 1000);
-}
\ No newline at end of file
+}
+
+// 주기적으로 토큰 만료 확인 (5분마다)
+setInterval(() => {
+ const token = localStorage.getItem('token');
+ if (token && isTokenExpired(token)) {
+ console.log('🚨 주기적 확인: 토큰이 만료되었습니다.');
+ clearAuthData();
+ alert('세션이 만료되었습니다. 다시 로그인해주세요.');
+ window.location.href = '/index.html';
+ }
+}, 5 * 60 * 1000); // 5분마다 확인
\ No newline at end of file
diff --git a/web-ui/js/daily-work-report.js b/web-ui/js/daily-work-report.js
index d8785ab..afcf762 100644
--- a/web-ui/js/daily-work-report.js
+++ b/web-ui/js/daily-work-report.js
@@ -1,9 +1,9 @@
-// daily-work-report.js - 통합 API 설정 적용 버전
+// daily-work-report.js - 브라우저 호환 버전
// =================================================================
-// 🌐 통합 API 설정 import
+// 🌐 API 설정 (window 객체에서 가져오기)
// =================================================================
-import { API, getAuthHeaders, apiCall } from '/js/api-config.js';
+// API 설정은 api-config.js에서 window 객체에 설정됨
// 전역 변수
let workTypes = [];
@@ -117,7 +117,7 @@ async function loadData() {
async function loadWorkers() {
try {
console.log('Workers API 호출 중... (통합 API 사용)');
- const data = await apiCall(`${API}/workers`);
+ const data = await window.apiCall(`${window.API}/workers`);
workers = Array.isArray(data) ? data : (data.data || data.workers || []);
console.log('✅ Workers 로드 성공:', workers.length);
} catch (error) {
@@ -129,7 +129,7 @@ async function loadWorkers() {
async function loadProjects() {
try {
console.log('Projects API 호출 중... (통합 API 사용)');
- const data = await apiCall(`${API}/projects`);
+ const data = await window.apiCall(`${window.API}/projects`);
projects = Array.isArray(data) ? data : (data.data || data.projects || []);
console.log('✅ Projects 로드 성공:', projects.length);
} catch (error) {
@@ -140,7 +140,7 @@ async function loadProjects() {
async function loadWorkTypes() {
try {
- const data = await apiCall(`${API}/daily-work-reports/work-types`);
+ const data = await window.apiCall(`${window.API}/daily-work-reports/work-types`);
if (Array.isArray(data) && data.length > 0) {
workTypes = data;
console.log('✅ 작업 유형 API 사용 (통합 설정)');
@@ -159,7 +159,7 @@ async function loadWorkTypes() {
async function loadWorkStatusTypes() {
try {
- const data = await apiCall(`${API}/daily-work-reports/work-status-types`);
+ const data = await window.apiCall(`${window.API}/daily-work-reports/work-status-types`);
if (Array.isArray(data) && data.length > 0) {
workStatusTypes = data;
console.log('✅ 업무 상태 유형 API 사용 (통합 설정)');
@@ -177,7 +177,7 @@ async function loadWorkStatusTypes() {
async function loadErrorTypes() {
try {
- const data = await apiCall(`${API}/daily-work-reports/error-types`);
+ const data = await window.apiCall(`${window.API}/daily-work-reports/error-types`);
if (Array.isArray(data) && data.length > 0) {
errorTypes = data;
console.log('✅ 에러 유형 API 사용 (통합 설정)');
@@ -424,7 +424,7 @@ async function saveWorkReport() {
console.log('전송 데이터 (통합 API 사용):', requestData);
try {
- const result = await apiCall(`${API}/daily-work-reports`, {
+ const result = await window.apiCall(`${window.API}/daily-work-reports`, {
method: 'POST',
body: JSON.stringify(requestData)
});
@@ -510,7 +510,7 @@ async function loadTodayWorkers() {
console.log(`🔒 본인 입력분만 조회 (통합 API): ${API}/daily-work-reports?${queryParams}`);
- const rawData = await apiCall(`${API}/daily-work-reports?${queryParams}`);
+ const rawData = await window.apiCall(`${window.API}/daily-work-reports?${queryParams}`);
console.log('📊 당일 작업 데이터 (통합 API):', rawData);
let data = [];
@@ -645,7 +645,7 @@ async function editWorkItem(workId) {
// 1. 기존 데이터 조회 (통합 API 사용)
showMessage('작업 정보를 불러오는 중... (통합 API)', 'loading');
- const workData = await apiCall(`${API}/daily-work-reports/${workId}`);
+ const workData = await window.apiCall(`${window.API}/daily-work-reports/${workId}`);
console.log('수정할 작업 데이터 (통합 API):', workData);
// 2. 수정 모달 표시
@@ -784,7 +784,7 @@ async function saveEditedWork() {
showMessage('작업을 수정하는 중... (통합 API)', 'loading');
- const result = await apiCall(`${API}/daily-work-reports/${editingWorkId}`, {
+ const result = await window.apiCall(`${window.API}/daily-work-reports/${editingWorkId}`, {
method: 'PUT',
body: JSON.stringify(updateData)
});
@@ -813,7 +813,7 @@ async function deleteWorkItem(workId) {
showMessage('작업을 삭제하는 중... (통합 API)', 'loading');
// 개별 항목 삭제 API 호출 (본인 작성분만 삭제 가능) - 통합 API 사용
- const result = await apiCall(`${API}/daily-work-reports/my-entry/${workId}`, {
+ const result = await window.apiCall(`${window.API}/daily-work-reports/my-entry/${workId}`, {
method: 'DELETE'
});
diff --git a/web-ui/pages/common/daily-work-report.html b/web-ui/pages/common/daily-work-report.html
index a8e0156..a38a914 100644
--- a/web-ui/pages/common/daily-work-report.html
+++ b/web-ui/pages/common/daily-work-report.html
@@ -1055,8 +1055,8 @@
-
-
-
+
+
+