diff --git a/system1-factory/web/index.html b/system1-factory/web/index.html index 96a1ad0..7b93a9a 100644 --- a/system1-factory/web/index.html +++ b/system1-factory/web/index.html @@ -1,15 +1,29 @@
- - -로딩 중...
+ diff --git a/system1-factory/web/js/api-base.js b/system1-factory/web/js/api-base.js index fb4952a..f90b434 100644 --- a/system1-factory/web/js/api-base.js +++ b/system1-factory/web/js/api-base.js @@ -20,9 +20,10 @@ /** * SSO 토큰 가져오기 (쿠키 우선, localStorage 폴백) + * sso_token이 없으면 기존 token도 확인 (하위 호환) */ window.getSSOToken = function() { - return cookieGet('sso_token') || localStorage.getItem('sso_token'); + return cookieGet('sso_token') || localStorage.getItem('sso_token') || localStorage.getItem('token'); }; /** @@ -30,6 +31,10 @@ */ window.getSSOUser = function() { var raw = cookieGet('sso_user') || localStorage.getItem('sso_user'); + if (!raw) { + // 기존 user 키도 확인 (하위 호환) + raw = localStorage.getItem('user'); + } try { return raw ? JSON.parse(raw) : null; } catch(e) { return null; } }; @@ -41,7 +46,8 @@ if (hostname.includes('technicalkorea.net')) { return window.location.protocol + '//tkfb.technicalkorea.net/login?redirect=' + encodeURIComponent(window.location.href); } - return '/login'; + // 개발 환경: 게이트웨이 SSO 로그인 페이지 + return '/login?redirect=' + encodeURIComponent(window.location.href); }; /** @@ -54,6 +60,9 @@ localStorage.removeItem('sso_token'); localStorage.removeItem('sso_user'); localStorage.removeItem('sso_refresh_token'); + // 기존 키도 삭제 (하위 호환) + localStorage.removeItem('token'); + localStorage.removeItem('user'); localStorage.removeItem('userPageAccess'); }; diff --git a/system1-factory/web/js/api-helper.js b/system1-factory/web/js/api-helper.js index ce77e84..3987901 100644 --- a/system1-factory/web/js/api-helper.js +++ b/system1-factory/web/js/api-helper.js @@ -6,13 +6,18 @@ const API_BASE_URL = window.API_BASE_URL || 'http://localhost:30005/api'; // 인증 관련 함수들 (직접 구현) function getToken() { - const token = localStorage.getItem('sso_token'); + // SSO 토큰 우선, 기존 token 폴백 + if (window.getSSOToken) return window.getSSOToken(); + const token = localStorage.getItem('sso_token') || localStorage.getItem('token'); return token && token !== 'undefined' && token !== 'null' ? token : null; } function clearAuthData() { + if (window.clearSSOAuth) { window.clearSSOAuth(); return; } localStorage.removeItem('sso_token'); localStorage.removeItem('sso_user'); + localStorage.removeItem('token'); + localStorage.removeItem('user'); } /** @@ -45,11 +50,11 @@ async function login(username, password) { */ async function authFetch(endpoint, options = {}) { const token = getToken(); - + if (!token) { console.error('토큰이 없습니다. 로그인이 필요합니다.'); clearAuthData(); // 인증 정보 정리 - window.location.href = '/login'; // 로그인 페이지로 리디렉션 + window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login?redirect=' + encodeURIComponent(window.location.href); // 에러를 던져서 후속 실행을 중단 throw new Error('인증 토큰이 없습니다.'); } @@ -71,7 +76,7 @@ async function authFetch(endpoint, options = {}) { if (response.status === 401) { console.error('인증 실패. 토큰이 만료되었거나 유효하지 않습니다.'); clearAuthData(); // 만료된 인증 정보 정리 - window.location.href = '/login'; + window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login?redirect=' + encodeURIComponent(window.location.href); throw new Error('인증에 실패했습니다.'); } @@ -133,4 +138,4 @@ window.apiPost = apiPost; window.apiPut = apiPut; window.apiDelete = apiDelete; window.getToken = getToken; -window.clearAuthData = clearAuthData; \ No newline at end of file +window.clearAuthData = clearAuthData; diff --git a/system1-factory/web/js/app-init.js b/system1-factory/web/js/app-init.js index 086b118..f07c54a 100644 --- a/system1-factory/web/js/app-init.js +++ b/system1-factory/web/js/app-init.js @@ -1,6 +1,7 @@ // /js/app-init.js // 앱 초기화 - 인증, 네비바, 사이드바를 한 번에 로드 // 모든 페이지에서 이 하나의 스크립트만 로드하면 됨 +// api-base.js가 먼저 로드되어야 함 (getSSOToken, getSSOUser, clearSSOAuth 등) (function() { 'use strict'; @@ -9,24 +10,29 @@ const CACHE_DURATION = 10 * 60 * 1000; // 10분 const COMPONENT_CACHE_PREFIX = 'component_v3_'; - // ===== 인증 함수 (api-base.js의 전역 헬퍼 활용) ===== + // ===== 인증 함수 (api-base.js의 SSO 함수 활용) ===== function isLoggedIn() { - var token = window.getSSOToken ? window.getSSOToken() : localStorage.getItem('sso_token'); + const token = window.getSSOToken ? window.getSSOToken() : (localStorage.getItem('sso_token') || localStorage.getItem('token')); return token && token !== 'undefined' && token !== 'null'; } function getUser() { - return window.getSSOUser ? window.getSSOUser() : (function() { - var u = localStorage.getItem('sso_user'); - return u ? JSON.parse(u) : null; - })(); + if (window.getSSOUser) return window.getSSOUser(); + const user = localStorage.getItem('sso_user') || localStorage.getItem('user'); + try { return user ? JSON.parse(user) : null; } catch(e) { return null; } + } + + function getToken() { + return window.getSSOToken ? window.getSSOToken() : (localStorage.getItem('sso_token') || localStorage.getItem('token')); } function clearAuthData() { if (window.clearSSOAuth) { window.clearSSOAuth(); return; } localStorage.removeItem('sso_token'); localStorage.removeItem('sso_user'); - localStorage.removeItem('userPageAccess_v2'); + localStorage.removeItem('token'); + localStorage.removeItem('user'); + localStorage.removeItem('userPageAccess'); } // ===== 페이지 권한 캐시 ===== @@ -36,7 +42,7 @@ if (!currentUser || !currentUser.user_id) return null; // 캐시 확인 - const cached = localStorage.getItem('userPageAccess_v2'); + const cached = localStorage.getItem('userPageAccess'); if (cached) { try { const cacheData = JSON.parse(cached); @@ -44,7 +50,7 @@ return cacheData.pages; } } catch (e) { - localStorage.removeItem('userPageAccess_v2'); + localStorage.removeItem('userPageAccess'); } } @@ -54,11 +60,12 @@ // 새로운 API 호출 pageAccessPromise = (async () => { try { + const token = getToken(); const response = await fetch(`${window.API_BASE_URL}/users/${currentUser.user_id}/page-access`, { method: 'GET', headers: { 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + (window.getSSOToken ? window.getSSOToken() : localStorage.getItem('sso_token')) + 'Authorization': `Bearer ${token}` } }); @@ -67,7 +74,7 @@ const data = await response.json(); const pages = data.data.pageAccess || []; - localStorage.setItem('userPageAccess_v2', JSON.stringify({ + localStorage.setItem('userPageAccess', JSON.stringify({ pages: pages, timestamp: Date.now() })); @@ -87,15 +94,17 @@ async function getAccessiblePageKeys(currentUser) { const pages = await getPageAccess(currentUser); if (!pages) return []; - return pages.filter(p => p.can_access === 1).map(p => p.page_key); + return pages.filter(p => p.can_access == 1).map(p => p.page_key); } // ===== 현재 페이지 키 추출 ===== // 하위 페이지 → 부모 페이지 키 매핑 (동일 권한 공유) - var PAGE_KEY_ALIASES = { - 'work.tbm-create': 'work.tbm', + const PAGE_KEY_ALIASES = { 'work.tbm-mobile': 'work.tbm', - 'work.report-create-mobile': 'work.report-create' + 'work.tbm-create': 'work.tbm', + 'work.report-create-mobile': 'work.report-create', + 'admin.equipment-detail': 'admin.equipments', + 'safety.issue-detail': 'safety.issue-report' }; function getCurrentPageKey() { @@ -186,7 +195,6 @@ async function processSidebar(doc, currentUser, accessiblePageKeys) { const userRole = (currentUser.role || '').toLowerCase(); const accessLevel = (currentUser.access_level || '').toLowerCase(); - // role 또는 access_level로 관리자 확인 const isAdmin = userRole === 'admin' || userRole === 'system admin' || userRole === 'system' || accessLevel === 'admin' || accessLevel === 'system'; @@ -212,26 +220,6 @@ } }); - // 크로스 시스템 링크 URL 설정 - var hostname = window.location.hostname; - var protocol = window.location.protocol; - var systemUrls = {}; - if (hostname.includes('technicalkorea.net')) { - systemUrls.report = protocol + '//tkreport.technicalkorea.net'; - systemUrls.nc = protocol + '//tkqc.technicalkorea.net'; - } else { - systemUrls.report = protocol + '//' + hostname + ':30180'; - systemUrls.nc = protocol + '//' + hostname + ':30280'; - } - doc.querySelectorAll('.cross-system-link').forEach(function(link) { - var system = link.getAttribute('data-system'); - var path = link.getAttribute('data-path'); - if (systemUrls[system]) { - link.setAttribute('href', systemUrls[system] + path); - link.setAttribute('target', '_blank'); - } - }); - // 저장된 상태 복원 (기본값: 접힌 상태) const isCollapsed = localStorage.getItem('sidebarCollapsed') !== 'false'; const sidebar = doc.querySelector('.sidebar-nav'); @@ -281,7 +269,8 @@ logoutButton.addEventListener('click', () => { if (confirm('로그아웃 하시겠습니까?')) { clearAuthData(); - window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login'; + if (window.clearSSOAuth) window.clearSSOAuth(); + window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login?redirect=' + encodeURIComponent('/pages/dashboard.html'); } }); } @@ -308,7 +297,7 @@ // ===== 알림 로드 ===== async function loadNotifications() { try { - const token = window.getSSOToken ? window.getSSOToken() : localStorage.getItem('sso_token'); + const token = getToken(); if (!token) return; const response = await fetch(`${window.API_BASE_URL}/notifications/unread`, { @@ -351,11 +340,11 @@ return; } - const icons = { repair: '🔧', safety: '⚠️', system: '📢', equipment: '🔩', maintenance: '🛠️' }; + const icons = { repair: '\ud83d\udd27', safety: '\u26a0\ufe0f', system: '\ud83d\udce2', equipment: '\ud83d\udea9', maintenance: '\ud83d\udee0\ufe0f' }; list.innerHTML = notifications.slice(0, 5).map(n => `