🚀 초기 프로젝트 설정 완료
✨ 기능: - 기간제 근로자 작업관리 시스템 기본 구조 - 한국어 기반 프론트엔드 (로그인, 대시보드, 작업자 관리) - Node.js Express 백엔드 API 서버 구조 - MySQL 데이터베이스 스키마 설계 - 14000번대 포트 구성으로 충돌 방지 📁 구조: - frontend/ : HTML, CSS, JS (Bootstrap 5) - backend/ : Node.js, Express, MySQL - database/ : 초기화 스크립트 - docs/ : 문서 🔌 포트: - 웹: 14000, API: 14001, DB: 14002, phpMyAdmin: 14003 🎯 다음 단계: 백엔드 API 라우트 구현 및 Docker 설정
This commit is contained in:
266
frontend/js/auth.js
Normal file
266
frontend/js/auth.js
Normal file
@@ -0,0 +1,266 @@
|
||||
// 인증 관련 JavaScript 함수들
|
||||
|
||||
const API_BASE_URL = 'http://localhost:14001/api';
|
||||
|
||||
// 로그인 함수
|
||||
async function login(username, password) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
// 토큰과 사용자 정보 저장
|
||||
localStorage.setItem('token', data.token);
|
||||
localStorage.setItem('user', JSON.stringify(data.user));
|
||||
|
||||
// 대시보드로 이동
|
||||
window.location.href = 'dashboard.html';
|
||||
} else {
|
||||
throw new Error(data.message || '로그인에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('로그인 오류:', error);
|
||||
showAlert('로그인 실패', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 로그아웃 함수
|
||||
function logout() {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('user');
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
|
||||
// 토큰 확인 함수
|
||||
function checkAuth() {
|
||||
const token = localStorage.getItem('token');
|
||||
const user = localStorage.getItem('user');
|
||||
|
||||
if (!token || !user) {
|
||||
// 로그인 페이지가 아닌 경우에만 리다이렉트
|
||||
if (!window.location.pathname.includes('index.html') && window.location.pathname !== '/') {
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(user);
|
||||
} catch (error) {
|
||||
console.error('사용자 정보 파싱 오류:', error);
|
||||
logout();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 관리자 권한 확인
|
||||
function isAdmin() {
|
||||
const user = checkAuth();
|
||||
return user && user.role === 'admin';
|
||||
}
|
||||
|
||||
// API 요청 헤더에 토큰 추가
|
||||
function getAuthHeaders() {
|
||||
const token = localStorage.getItem('token');
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
};
|
||||
}
|
||||
|
||||
// API 요청 함수
|
||||
async function apiRequest(url, options = {}) {
|
||||
const defaultOptions = {
|
||||
headers: getAuthHeaders()
|
||||
};
|
||||
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
headers: {
|
||||
...defaultOptions.headers,
|
||||
...options.headers
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${url}`, mergedOptions);
|
||||
|
||||
// 인증 오류 처리
|
||||
if (response.status === 401) {
|
||||
logout();
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || '요청 처리에 실패했습니다.');
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('API 요청 오류:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 알림 표시 함수
|
||||
function showAlert(title, message, type = 'danger') {
|
||||
const alertModal = document.getElementById('alertModal');
|
||||
if (alertModal) {
|
||||
document.getElementById('alertMessage').innerHTML = `<strong>${title}</strong><br>${message}`;
|
||||
const modal = new bootstrap.Modal(alertModal);
|
||||
modal.show();
|
||||
} else {
|
||||
alert(`${title}: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 토스트 알림 표시 함수
|
||||
function showToast(message, type = 'success') {
|
||||
const toastElement = document.getElementById('alertToast');
|
||||
const toastMessage = document.getElementById('toastMessage');
|
||||
|
||||
if (toastElement && toastMessage) {
|
||||
toastMessage.textContent = message;
|
||||
|
||||
// 토스트 색상 설정
|
||||
toastElement.className = `toast ${type === 'success' ? 'bg-success' : 'bg-danger'} text-white`;
|
||||
|
||||
const toast = new bootstrap.Toast(toastElement);
|
||||
toast.show();
|
||||
}
|
||||
}
|
||||
|
||||
// 페이지 로드 시 인증 확인
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 로그인 페이지가 아닌 경우 인증 확인
|
||||
if (!window.location.pathname.includes('index.html') && window.location.pathname !== '/') {
|
||||
const user = checkAuth();
|
||||
if (user) {
|
||||
// 사용자 이름 표시
|
||||
const userNameDisplay = document.getElementById('userNameDisplay');
|
||||
if (userNameDisplay) {
|
||||
userNameDisplay.textContent = user.name;
|
||||
}
|
||||
|
||||
// 관리자가 아닌 경우 작업자 관리 메뉴 숨기기
|
||||
if (!isAdmin()) {
|
||||
const workerManagementNav = document.getElementById('workerManagementNav');
|
||||
const addWorkerBtn = document.getElementById('addWorkerBtn');
|
||||
|
||||
if (workerManagementNav) workerManagementNav.style.display = 'none';
|
||||
if (addWorkerBtn) addWorkerBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 로그인 폼 처리
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
if (loginForm) {
|
||||
loginForm.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
if (!username || !password) {
|
||||
showAlert('입력 오류', '아이디와 비밀번호를 모두 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
await login(username, password);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 날짜 포맷 함수
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('ko-KR', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// 시간 포맷 함수
|
||||
function formatTime(timeString) {
|
||||
if (!timeString) return '';
|
||||
return timeString.substring(0, 5); // HH:MM 형식으로 자르기
|
||||
}
|
||||
|
||||
// 직종 한글 변환
|
||||
function getJobTypeText(jobType) {
|
||||
const jobTypes = {
|
||||
'welder': '용접사',
|
||||
'plumber': '배관사'
|
||||
};
|
||||
return jobTypes[jobType] || jobType;
|
||||
}
|
||||
|
||||
// 상태 한글 변환
|
||||
function getStatusText(status, type = 'general') {
|
||||
const statusTexts = {
|
||||
general: {
|
||||
'active': '활성',
|
||||
'inactive': '비활성',
|
||||
'pending': '대기',
|
||||
'approved': '승인',
|
||||
'rejected': '거부',
|
||||
'completed': '완료',
|
||||
'cancelled': '취소'
|
||||
},
|
||||
work: {
|
||||
'planned': '계획',
|
||||
'in_progress': '진행중',
|
||||
'completed': '완료',
|
||||
'cancelled': '취소'
|
||||
},
|
||||
error: {
|
||||
'reported': '신고됨',
|
||||
'investigating': '조사중',
|
||||
'resolved': '해결됨',
|
||||
'closed': '종료'
|
||||
},
|
||||
request: {
|
||||
'pending': '대기',
|
||||
'approved': '승인',
|
||||
'ordered': '주문',
|
||||
'delivered': '배송완료',
|
||||
'rejected': '거부'
|
||||
}
|
||||
};
|
||||
|
||||
return statusTexts[type][status] || status;
|
||||
}
|
||||
|
||||
// 우선순위 한글 변환
|
||||
function getPriorityText(priority) {
|
||||
const priorities = {
|
||||
'low': '낮음',
|
||||
'normal': '보통',
|
||||
'high': '높음',
|
||||
'urgent': '긴급'
|
||||
};
|
||||
return priorities[priority] || priority;
|
||||
}
|
||||
|
||||
// 심각도 한글 변환
|
||||
function getSeverityText(severity) {
|
||||
const severities = {
|
||||
'low': '낮음',
|
||||
'medium': '보통',
|
||||
'high': '높음',
|
||||
'critical': '심각'
|
||||
};
|
||||
return severities[severity] || severity;
|
||||
}
|
||||
Reference in New Issue
Block a user