- 시스템 관리자 전용 웹페이지 구현 (system.html) - 깔끔한 흰색 배경의 올드스쿨 스타일 적용 - 반응형 그리드 레이아웃으로 카드 배치 개선 - ES6 모듈 방식으로 JavaScript 구조 개선 - 이벤트 리스너 방식으로 버튼 클릭 처리 변경 - 시스템 상태, 사용자 통계, 계정 관리 기능 구현 - 시스템 로그 조회 기능 추가 - 나머지 관리 기능들 스켈레톤 구현 (개발 중 상태) - 인코딩 문제 해결을 위한 영어 로그 메시지 적용 - hyungi 계정을 system 권한으로 설정 - JWT 토큰에 role 필드 추가 - 시스템 전용 API 엔드포인트 구현 주요 변경사항: - web-ui/pages/dashboard/system.html: 시스템 관리자 전용 페이지 - web-ui/css/system-dashboard.css: 시스템 대시보드 전용 스타일 - web-ui/js/system-dashboard.js: 시스템 대시보드 로직 - api.hyungi.net/controllers/systemController.js: 시스템 API 컨트롤러 - api.hyungi.net/routes/systemRoutes.js: 시스템 API 라우트 - api.hyungi.net/controllers/authController.js: 시스템 권한 로그인 처리 - api.hyungi.net/services/auth.service.js: JWT 토큰에 role 필드 추가
145 lines
4.1 KiB
JavaScript
145 lines
4.1 KiB
JavaScript
// api-config.js - nginx 프록시 대응 API 설정
|
|
|
|
function getApiBaseUrl() {
|
|
const hostname = window.location.hostname;
|
|
const protocol = window.location.protocol;
|
|
const port = window.location.port;
|
|
|
|
console.log('🌐 감지된 환경:', { hostname, protocol, port });
|
|
|
|
// 🔗 nginx 프록시를 통한 접근 (권장)
|
|
// nginx가 /api/ 요청을 백엔드로 프록시하므로 포트 없이 접근
|
|
if (hostname.startsWith('192.168.') || hostname.startsWith('10.') || hostname.startsWith('172.') ||
|
|
hostname === 'localhost' || hostname === '127.0.0.1' ||
|
|
hostname.includes('.local') || hostname.includes('hyungi')) {
|
|
|
|
// 현재 웹서버의 도메인/IP를 그대로 사용하되 API 포트(20005)로 직접 연결
|
|
const baseUrl = `${protocol}//${hostname}:20005/api`;
|
|
|
|
console.log('✅ nginx 프록시 사용:', baseUrl);
|
|
return baseUrl;
|
|
}
|
|
|
|
// 🚨 백업: 직접 접근 (nginx 프록시 실패시에만)
|
|
console.warn('⚠️ 직접 API 접근 (백업 모드)');
|
|
return `${protocol}//${hostname}:20005/api`;
|
|
}
|
|
|
|
// API 설정
|
|
const API_URL = getApiBaseUrl();
|
|
|
|
export const API = API_URL;
|
|
export const API_BASE_URL = API_URL;
|
|
|
|
export function ensureAuthenticated() {
|
|
const token = localStorage.getItem('token');
|
|
if (!token || token === 'undefined') {
|
|
alert('로그인이 필요합니다');
|
|
localStorage.removeItem('token');
|
|
window.location.href = '/';
|
|
return null;
|
|
}
|
|
return token;
|
|
}
|
|
|
|
export function getAuthHeaders() {
|
|
const token = localStorage.getItem('token');
|
|
return {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${token}`
|
|
};
|
|
}
|
|
|
|
// 🔧 개선된 API 호출 함수 (에러 처리 강화)
|
|
export async function apiCall(url, options = {}) {
|
|
const defaultOptions = {
|
|
headers: getAuthHeaders()
|
|
};
|
|
|
|
const finalOptions = {
|
|
...defaultOptions,
|
|
...options,
|
|
headers: {
|
|
...defaultOptions.headers,
|
|
...options.headers
|
|
}
|
|
};
|
|
|
|
try {
|
|
console.log(`📡 API 호출: ${url}`);
|
|
const response = await fetch(url, finalOptions);
|
|
|
|
// 인증 만료 처리
|
|
if (response.status === 401) {
|
|
console.error('❌ 인증 만료');
|
|
localStorage.removeItem('token');
|
|
alert('인증이 만료되었습니다. 다시 로그인해주세요.');
|
|
window.location.href = '/';
|
|
return;
|
|
}
|
|
|
|
// 응답 실패 처리
|
|
if (!response.ok) {
|
|
let errorMessage = `HTTP ${response.status}`;
|
|
try {
|
|
const errorData = await response.json();
|
|
errorMessage = errorData.error || errorData.message || errorMessage;
|
|
} catch (e) {
|
|
// JSON 파싱 실패시 기본 메시지 사용
|
|
}
|
|
throw new Error(errorMessage);
|
|
}
|
|
|
|
const result = await response.json();
|
|
console.log(`✅ API 성공: ${url}`);
|
|
return result;
|
|
|
|
} catch (error) {
|
|
console.error(`❌ API 오류 (${url}):`, error);
|
|
|
|
// 네트워크 오류 vs 서버 오류 구분
|
|
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
|
throw new Error('네트워크 연결 오류입니다. 인터넷 연결을 확인해주세요.');
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// 디버깅 정보
|
|
console.log('🔗 API Base URL:', API);
|
|
console.log('🌐 Current Location:', {
|
|
hostname: window.location.hostname,
|
|
protocol: window.location.protocol,
|
|
port: window.location.port,
|
|
href: window.location.href
|
|
});
|
|
|
|
// 🧪 API 연결 테스트 함수 (개발용)
|
|
export async function testApiConnection() {
|
|
try {
|
|
console.log('🧪 API 연결 테스트 시작...');
|
|
const response = await fetch(`${API}/health`, {
|
|
method: 'GET',
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
|
|
if (response.ok) {
|
|
console.log('✅ API 연결 성공!');
|
|
return true;
|
|
} else {
|
|
console.log('❌ API 연결 실패:', response.status);
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
console.log('❌ API 연결 오류:', error.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 개발 모드에서 자동 테스트
|
|
if (window.location.hostname === 'localhost' || window.location.hostname.startsWith('192.168.')) {
|
|
setTimeout(() => {
|
|
testApiConnection();
|
|
}, 1000);
|
|
} |