refactor: System2/3, User Management SSO 인증 통합
- System2 신고: SSO JWT 인증 전환, API base 정리 - System3 부적합: SSO 인증 매니저 통합, 권한 체계 정비 - User Management: SSO 토큰 기반 사용자 관리 API 연동 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
@@ -32,11 +33,19 @@ app = FastAPI(
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# CORS 설정 (완전 개방 - CORS 문제 해결)
|
||||
ALLOWED_ORIGINS = [
|
||||
"https://tkfb.technicalkorea.net",
|
||||
"https://tkreport.technicalkorea.net",
|
||||
"https://tkqc.technicalkorea.net",
|
||||
"https://tkuser.technicalkorea.net",
|
||||
]
|
||||
if os.getenv("ENV", "production") == "development":
|
||||
ALLOWED_ORIGINS += ["http://localhost:30080", "http://localhost:30180", "http://localhost:30280"]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=False, # * origin과 credentials는 함께 사용 불가
|
||||
allow_origins=ALLOWED_ORIGINS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allow_headers=["*"],
|
||||
expose_headers=["*"]
|
||||
|
||||
@@ -53,32 +53,28 @@ const API_BASE_URL = (() => {
|
||||
// 토큰 관리 (SSO 쿠키 + localStorage 이중 지원)
|
||||
const TokenManager = {
|
||||
getToken: () => {
|
||||
// SSO 쿠키 우선 (sso_token), localStorage 폴백 (access_token)
|
||||
return _cookieGet('sso_token') || localStorage.getItem('sso_token') || localStorage.getItem('access_token');
|
||||
// SSO 쿠키 우선, localStorage 폴백
|
||||
return _cookieGet('sso_token') || localStorage.getItem('sso_token');
|
||||
},
|
||||
setToken: (token) => localStorage.setItem('access_token', token),
|
||||
setToken: (token) => localStorage.setItem('sso_token', token),
|
||||
removeToken: () => {
|
||||
_cookieRemove('sso_token');
|
||||
_cookieRemove('sso_user');
|
||||
_cookieRemove('sso_refresh_token');
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('sso_token');
|
||||
localStorage.removeItem('sso_user');
|
||||
},
|
||||
|
||||
getUser: () => {
|
||||
// SSO 쿠키 우선, localStorage 폴백
|
||||
const ssoUser = _cookieGet('sso_user') || localStorage.getItem('sso_user');
|
||||
if (ssoUser) {
|
||||
try { return JSON.parse(ssoUser); } catch(e) {}
|
||||
}
|
||||
const userStr = localStorage.getItem('currentUser') || localStorage.getItem('current_user');
|
||||
return userStr ? JSON.parse(userStr) : null;
|
||||
return null;
|
||||
},
|
||||
setUser: (user) => localStorage.setItem('current_user', JSON.stringify(user)),
|
||||
setUser: (user) => localStorage.setItem('sso_user', JSON.stringify(user)),
|
||||
removeUser: () => {
|
||||
localStorage.removeItem('current_user');
|
||||
localStorage.removeItem('currentUser');
|
||||
localStorage.removeItem('sso_user');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -47,22 +47,16 @@ class App {
|
||||
*/
|
||||
async checkAuth() {
|
||||
// SSO 쿠키 우선, localStorage 폴백
|
||||
const token = this._cookieGet('sso_token') || localStorage.getItem('sso_token') || localStorage.getItem('access_token');
|
||||
const token = this._cookieGet('sso_token') || localStorage.getItem('sso_token');
|
||||
if (!token) {
|
||||
throw new Error('토큰 없음');
|
||||
}
|
||||
|
||||
// SSO 쿠키에서 사용자 정보 시도
|
||||
const ssoUser = this._cookieGet('sso_user') || localStorage.getItem('sso_user');
|
||||
if (ssoUser) {
|
||||
try { this.currentUser = JSON.parse(ssoUser); return; } catch(e) {}
|
||||
}
|
||||
const storedUser = localStorage.getItem('currentUser');
|
||||
if (storedUser) {
|
||||
this.currentUser = JSON.parse(storedUser);
|
||||
} else {
|
||||
throw new Error('사용자 정보 없음');
|
||||
}
|
||||
throw new Error('사용자 정보 없음');
|
||||
}
|
||||
|
||||
_cookieGet(name) {
|
||||
@@ -371,10 +365,8 @@ class App {
|
||||
if (window.authManager) {
|
||||
window.authManager.clearAuth();
|
||||
} else {
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('sso_token');
|
||||
localStorage.removeItem('sso_user');
|
||||
localStorage.removeItem('currentUser');
|
||||
}
|
||||
this.redirectToLogin();
|
||||
}
|
||||
|
||||
@@ -628,10 +628,10 @@ class CommonHeader {
|
||||
if (window.authManager) {
|
||||
window.authManager.logout();
|
||||
} else {
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('sso_token');
|
||||
localStorage.removeItem('sso_token');
|
||||
localStorage.removeItem('sso_user');
|
||||
localStorage.removeItem('currentUser');
|
||||
localStorage.removeItem('sso_user');
|
||||
var hostname = window.location.hostname;
|
||||
if (hostname.includes('technicalkorea.net')) {
|
||||
window.location.href = window.location.protocol + '//tkfb.technicalkorea.net/login';
|
||||
@@ -667,7 +667,6 @@ class CommonHeader {
|
||||
initializeKeyboardShortcuts() {
|
||||
if (window.keyboardShortcuts) {
|
||||
window.keyboardShortcuts.setUser(this.currentUser);
|
||||
console.log('⌨️ 키보드 단축키 사용자 설정 완료');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +678,6 @@ class CommonHeader {
|
||||
// 사용자 설정 후 프리로더 초기화
|
||||
setTimeout(() => {
|
||||
window.pagePreloader.init();
|
||||
console.log('🚀 페이지 프리로더 초기화 완료');
|
||||
}, 1000); // 권한 시스템 로드 후 실행
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ class AuthManager {
|
||||
* 초기화
|
||||
*/
|
||||
init() {
|
||||
console.log('🔐 AuthManager 초기화');
|
||||
|
||||
// localStorage에서 사용자 정보 복원
|
||||
this.restoreUserFromStorage();
|
||||
@@ -57,7 +56,7 @@ class AuthManager {
|
||||
* SSO 토큰 가져오기 (쿠키 우선, localStorage 폴백)
|
||||
*/
|
||||
_getToken() {
|
||||
return this._cookieGet('sso_token') || localStorage.getItem('sso_token') || localStorage.getItem('access_token');
|
||||
return this._cookieGet('sso_token') || localStorage.getItem('sso_token');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,10 +67,6 @@ class AuthManager {
|
||||
if (ssoUser && ssoUser !== 'undefined' && ssoUser !== 'null') {
|
||||
try { return JSON.parse(ssoUser); } catch(e) {}
|
||||
}
|
||||
const userStr = localStorage.getItem('currentUser');
|
||||
if (userStr && userStr !== 'undefined' && userStr !== 'null') {
|
||||
try { return JSON.parse(userStr); } catch(e) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -148,7 +143,7 @@ class AuthManager {
|
||||
this.lastAuthCheck = Date.now();
|
||||
|
||||
// localStorage 업데이트
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
localStorage.setItem('sso_user', JSON.stringify(user));
|
||||
|
||||
this.notifyListeners('auth-success', user);
|
||||
return user;
|
||||
@@ -191,11 +186,10 @@ class AuthManager {
|
||||
this._cookieRemove('sso_user');
|
||||
this._cookieRemove('sso_refresh_token');
|
||||
|
||||
// localStorage 삭제
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('sso_token');
|
||||
localStorage.removeItem('sso_user');
|
||||
localStorage.removeItem('currentUser');
|
||||
// localStorage 삭제 (전 시스템 키 통일)
|
||||
['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => {
|
||||
localStorage.removeItem(k);
|
||||
});
|
||||
|
||||
this.notifyListeners('auth-cleared');
|
||||
}
|
||||
@@ -212,9 +206,9 @@ class AuthManager {
|
||||
this.isAuthenticated = true;
|
||||
this.lastAuthCheck = Date.now();
|
||||
|
||||
// localStorage 저장
|
||||
localStorage.setItem('access_token', data.access_token);
|
||||
localStorage.setItem('currentUser', JSON.stringify(data.user));
|
||||
// localStorage 저장 (sso_token/sso_user로 통일)
|
||||
localStorage.setItem('sso_token', data.access_token);
|
||||
localStorage.setItem('sso_user', JSON.stringify(data.user));
|
||||
|
||||
this.notifyListeners('login-success', data.user);
|
||||
return data;
|
||||
@@ -293,4 +287,3 @@ class AuthManager {
|
||||
// 전역 인스턴스 생성
|
||||
window.authManager = new AuthManager();
|
||||
|
||||
console.log('🎯 AuthManager 로드 완료');
|
||||
|
||||
@@ -36,7 +36,6 @@ class KeyboardShortcutManager {
|
||||
this.register('r', () => this.triggerRefreshAction(), '새로고침');
|
||||
this.register('f', () => this.focusSearchField(), '검색 포커스');
|
||||
|
||||
console.log('⌨️ 키보드 단축키 등록 완료');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +177,6 @@ class KeyboardShortcutManager {
|
||||
// 콜백 실행
|
||||
try {
|
||||
shortcut.callback(event);
|
||||
console.log(`⌨️ 단축키 실행: ${combination}`);
|
||||
} catch (error) {
|
||||
console.error('단축키 실행 실패:', combination, error);
|
||||
}
|
||||
@@ -599,7 +597,6 @@ class KeyboardShortcutManager {
|
||||
*/
|
||||
setEnabled(enabled) {
|
||||
this.isEnabled = enabled;
|
||||
console.log(`⌨️ 키보드 단축키 ${enabled ? '활성화' : '비활성화'}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,7 +52,7 @@ class PageManager {
|
||||
* 사용자 인증 확인
|
||||
*/
|
||||
async checkAuthentication() {
|
||||
const token = localStorage.getItem('access_token');
|
||||
const token = localStorage.getItem('sso_token');
|
||||
if (!token) {
|
||||
window.location.href = '/index.html';
|
||||
return null;
|
||||
@@ -63,12 +63,12 @@ class PageManager {
|
||||
await this.waitForAPI();
|
||||
|
||||
const user = await AuthAPI.getCurrentUser();
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
localStorage.setItem('sso_user', JSON.stringify(user));
|
||||
return user;
|
||||
} catch (error) {
|
||||
console.error('인증 실패:', error);
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('currentUser');
|
||||
localStorage.removeItem('sso_token');
|
||||
localStorage.removeItem('sso_user');
|
||||
window.location.href = '/index.html';
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@ class PagePreloader {
|
||||
if (this.isPreloading) return;
|
||||
|
||||
this.isPreloading = true;
|
||||
console.log('🚀 페이지 프리로딩 시작:', pages.map(p => p.id));
|
||||
|
||||
for (const page of pages) {
|
||||
if (this.preloadedPages.has(page.url)) continue;
|
||||
@@ -93,7 +92,6 @@ class PagePreloader {
|
||||
|
||||
// 네트워크 상태 확인 (느린 연결에서는 중단)
|
||||
if (this.isSlowConnection()) {
|
||||
console.log('⚠️ 느린 연결 감지, 프리로딩 중단');
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -106,7 +104,6 @@ class PagePreloader {
|
||||
}
|
||||
|
||||
this.isPreloading = false;
|
||||
console.log('✅ 페이지 프리로딩 완료');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +125,6 @@ class PagePreloader {
|
||||
await this.preloadPageResources(html, page.url);
|
||||
|
||||
this.preloadedPages.add(page.url);
|
||||
console.log(`📄 프리로드 완료: ${page.id}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -251,7 +247,6 @@ class PagePreloader {
|
||||
const html = await response.text();
|
||||
this.preloadCache.set(url, html);
|
||||
this.preloadedPages.add(url);
|
||||
console.log('🖱️ 호버 프리로드 완료:', url);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('호버 프리로드 실패:', url, error);
|
||||
@@ -285,7 +280,6 @@ class PagePreloader {
|
||||
if ('serviceWorker' in navigator) {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.register('/sw.js');
|
||||
console.log('🔧 서비스 워커 등록 완료:', registration);
|
||||
} catch (error) {
|
||||
console.log('서비스 워커 등록 실패:', error);
|
||||
}
|
||||
@@ -306,7 +300,6 @@ class PagePreloader {
|
||||
this.preloadCache.clear();
|
||||
this.resourceCache.clear();
|
||||
this.preloadedPages.clear();
|
||||
console.log('🗑️ 프리로드 캐시 정리 완료');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class PagePermissionManager {
|
||||
const match = document.cookie.match(/(?:^|; )sso_token=([^;]*)/);
|
||||
if (match) return decodeURIComponent(match[1]);
|
||||
// 3) localStorage 폴백
|
||||
return localStorage.getItem('sso_token') || localStorage.getItem('access_token');
|
||||
return localStorage.getItem('sso_token');
|
||||
}
|
||||
|
||||
async loadPagePermissions() {
|
||||
|
||||
@@ -47,7 +47,7 @@ async function loadProjects() {
|
||||
var sel = document.getElementById('projectFilter');
|
||||
sel.innerHTML = '<option value="">전체 프로젝트</option>';
|
||||
projects.forEach(function (p) {
|
||||
sel.innerHTML += '<option value="' + p.id + '">' + p.project_name + '</option>';
|
||||
sel.innerHTML += '<option value="' + p.id + '">' + escapeHtml(p.project_name) + '</option>';
|
||||
});
|
||||
}
|
||||
} catch (e) { console.error('프로젝트 로드 실패:', e); }
|
||||
|
||||
@@ -53,7 +53,7 @@ async function initializeIssueView() {
|
||||
try {
|
||||
const user = await AuthAPI.getCurrentUser();
|
||||
currentUser = user;
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
localStorage.setItem('sso_user', JSON.stringify(user));
|
||||
|
||||
// 공통 헤더 초기화
|
||||
await window.commonHeader.init(user, 'issues_view');
|
||||
@@ -713,7 +713,7 @@ async function handlePasswordChange(e) {
|
||||
|
||||
// 현재 사용자 정보도 업데이트
|
||||
currentUser.password = newPassword;
|
||||
localStorage.setItem('currentUser', JSON.stringify(currentUser));
|
||||
localStorage.setItem('sso_user', JSON.stringify(currentUser));
|
||||
|
||||
alert('비밀번호가 성공적으로 변경되었습니다.');
|
||||
document.querySelector('.fixed').remove(); // 모달 닫기
|
||||
|
||||
@@ -18,7 +18,7 @@ async function initializeArchive() {
|
||||
try {
|
||||
const user = await AuthAPI.getCurrentUser();
|
||||
currentUser = user;
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
localStorage.setItem('sso_user', JSON.stringify(user));
|
||||
|
||||
// 공통 헤더 초기화
|
||||
await window.commonHeader.init(user, 'issues_archive');
|
||||
|
||||
@@ -81,7 +81,7 @@ async function initializeInbox() {
|
||||
try {
|
||||
const user = await AuthAPI.getCurrentUser();
|
||||
currentUser = user;
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
localStorage.setItem('sso_user', JSON.stringify(user));
|
||||
|
||||
// 공통 헤더 초기화
|
||||
await window.commonHeader.init(user, 'issues_inbox');
|
||||
@@ -123,7 +123,7 @@ async function initializeInbox() {
|
||||
|
||||
// 공통 헤더만이라도 초기화
|
||||
try {
|
||||
const user = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||||
const user = JSON.parse(localStorage.getItem('sso_user') || '{}');
|
||||
if (user.id) {
|
||||
await window.commonHeader.init(user, 'issues_inbox');
|
||||
// 에러 상황에서도 애니메이션 적용
|
||||
|
||||
@@ -27,7 +27,7 @@ async function initializeManagement() {
|
||||
try {
|
||||
const user = await AuthAPI.getCurrentUser();
|
||||
currentUser = user;
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
localStorage.setItem('sso_user', JSON.stringify(user));
|
||||
|
||||
// 공통 헤더 초기화
|
||||
await window.commonHeader.init(user, 'issues_management');
|
||||
@@ -694,9 +694,9 @@ function createCompletedRow(issue, project) {
|
||||
// 입력 여부 아이콘 생성
|
||||
function getStatusIcon(value) {
|
||||
if (value && value.toString().trim() !== '') {
|
||||
return '<span class="text-green-500 text-lg">✅</span>';
|
||||
return '<span class="text-green-500 text-lg"></span>';
|
||||
} else {
|
||||
return '<span class="text-gray-400 text-lg">❌</span>';
|
||||
return '<span class="text-gray-400 text-lg"></span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,9 +704,9 @@ function getStatusIcon(value) {
|
||||
function getPhotoStatusIcon(photo1, photo2) {
|
||||
const count = (photo1 ? 1 : 0) + (photo2 ? 1 : 0);
|
||||
if (count > 0) {
|
||||
return `<span class="text-green-500 text-lg">✅</span><span class="text-xs ml-1">${count}장</span>`;
|
||||
return `<span class="text-green-500 text-lg"></span><span class="text-xs ml-1">${count}장</span>`;
|
||||
} else {
|
||||
return '<span class="text-gray-400 text-lg">❌</span>';
|
||||
return '<span class="text-gray-400 text-lg"></span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1194,7 +1194,6 @@ async function saveModalChanges() {
|
||||
updates[fieldName] = base64;
|
||||
}
|
||||
|
||||
console.log(`📸 ${maxPhotos}장의 완료 사진 처리 완료`);
|
||||
}
|
||||
|
||||
console.log('Modal sending updates:', updates);
|
||||
@@ -1744,11 +1743,10 @@ async function saveIssueFromModal(issueId) {
|
||||
const files = completionPhotoElement.files;
|
||||
const maxPhotos = Math.min(files.length, 5);
|
||||
|
||||
console.log(`🔍 총 ${maxPhotos}개의 완료 사진 업로드 시작`);
|
||||
|
||||
for (let i = 0; i < maxPhotos; i++) {
|
||||
const file = files[i];
|
||||
console.log(`🔍 파일 ${i + 1} 정보:`, {
|
||||
console.log(` 파일 ${i + 1} 정보:`, {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type
|
||||
@@ -1760,7 +1758,6 @@ async function saveIssueFromModal(issueId) {
|
||||
const fieldName = i === 0 ? 'completion_photo' : `completion_photo${i + 1}`;
|
||||
completionPhotos[fieldName] = base64Data;
|
||||
|
||||
console.log(`✅ 파일 ${i + 1} 변환 완료 (${fieldName})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('파일 변환 오류:', error);
|
||||
@@ -1814,9 +1811,9 @@ async function saveIssueFromModal(issueId) {
|
||||
if (updatedIssue) {
|
||||
// 완료 사진이 저장되었는지 확인
|
||||
if (updatedIssue.completion_photo_path) {
|
||||
alert('✅ 완료 사진이 성공적으로 저장되었습니다!');
|
||||
alert(' 완료 사진이 성공적으로 저장되었습니다!');
|
||||
} else {
|
||||
alert('⚠️ 저장은 완료되었지만 완료 사진 저장에 실패했습니다. 다시 시도해주세요.');
|
||||
alert(' 저장은 완료되었지만 완료 사진 저장에 실패했습니다. 다시 시도해주세요.');
|
||||
}
|
||||
|
||||
// 모달 내용 업데이트 (완료 사진 표시 갱신)
|
||||
@@ -2075,7 +2072,7 @@ async function saveAndCompleteIssue(issueId) {
|
||||
if (completionPhotoElement && completionPhotoElement.files[0]) {
|
||||
try {
|
||||
const file = completionPhotoElement.files[0];
|
||||
console.log('🔍 업로드할 파일 정보:', {
|
||||
console.log(' 업로드할 파일 정보:', {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
@@ -2083,12 +2080,8 @@ async function saveAndCompleteIssue(issueId) {
|
||||
});
|
||||
|
||||
const base64 = await fileToBase64(file);
|
||||
console.log('🔍 Base64 변환 완료 - 전체 길이:', base64.length);
|
||||
console.log('🔍 Base64 헤더:', base64.substring(0, 50));
|
||||
|
||||
completionPhoto = base64.split(',')[1]; // Base64 데이터만 추출
|
||||
console.log('🔍 헤더 제거 후 길이:', completionPhoto.length);
|
||||
console.log('🔍 전송할 Base64 시작 부분:', completionPhoto.substring(0, 50));
|
||||
} catch (error) {
|
||||
console.error('파일 변환 오류:', error);
|
||||
alert('완료 사진 업로드 중 오류가 발생했습니다.');
|
||||
|
||||
@@ -3,20 +3,28 @@
|
||||
* M-Project 작업보고서 시스템
|
||||
*/
|
||||
|
||||
const CACHE_NAME = 'mproject-v1.0.3';
|
||||
const STATIC_CACHE = 'mproject-static-v1.0.3';
|
||||
const DYNAMIC_CACHE = 'mproject-dynamic-v1.0.3';
|
||||
const CACHE_NAME = 'mproject-v1.1.0';
|
||||
const STATIC_CACHE = 'mproject-static-v1.1.0';
|
||||
const DYNAMIC_CACHE = 'mproject-dynamic-v1.1.0';
|
||||
|
||||
// 캐시할 정적 리소스
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/app.html',
|
||||
'/issue-view.html',
|
||||
'/daily-work.html',
|
||||
'/project-management.html',
|
||||
'/admin.html',
|
||||
'/issues-dashboard.html',
|
||||
'/issues-inbox.html',
|
||||
'/issues-management.html',
|
||||
'/issues-archive.html',
|
||||
'/ai-assistant.html',
|
||||
'/reports.html',
|
||||
'/reports-daily.html',
|
||||
'/reports-weekly.html',
|
||||
'/reports-monthly.html',
|
||||
'/static/js/api.js',
|
||||
'/static/js/app.js',
|
||||
'/static/js/core/permissions.js',
|
||||
'/static/js/core/auth-manager.js',
|
||||
'/static/js/components/common-header.js',
|
||||
'/static/js/core/page-manager.js',
|
||||
'/static/js/core/page-preloader.js',
|
||||
@@ -60,20 +68,20 @@ const CACHE_STRATEGIES = {
|
||||
* 서비스 워커 설치
|
||||
*/
|
||||
self.addEventListener('install', (event) => {
|
||||
console.log('🔧 서비스 워커 설치 중...');
|
||||
console.log(' 서비스 워커 설치 중...');
|
||||
|
||||
event.waitUntil(
|
||||
caches.open(STATIC_CACHE)
|
||||
.then((cache) => {
|
||||
console.log('📦 정적 리소스 캐싱 중...');
|
||||
console.log(' 정적 리소스 캐싱 중...');
|
||||
return cache.addAll(STATIC_ASSETS);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('✅ 서비스 워커 설치 완료');
|
||||
console.log(' 서비스 워커 설치 완료');
|
||||
return self.skipWaiting();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('❌ 서비스 워커 설치 실패:', error);
|
||||
console.error(' 서비스 워커 설치 실패:', error);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -82,7 +90,7 @@ self.addEventListener('install', (event) => {
|
||||
* 서비스 워커 활성화
|
||||
*/
|
||||
self.addEventListener('activate', (event) => {
|
||||
console.log('🚀 서비스 워커 활성화 중...');
|
||||
console.log(' 서비스 워커 활성화 중...');
|
||||
|
||||
event.waitUntil(
|
||||
caches.keys()
|
||||
@@ -93,14 +101,14 @@ self.addEventListener('activate', (event) => {
|
||||
if (cacheName !== STATIC_CACHE &&
|
||||
cacheName !== DYNAMIC_CACHE &&
|
||||
cacheName !== CACHE_NAME) {
|
||||
console.log('🗑️ 이전 캐시 삭제:', cacheName);
|
||||
console.log(' 이전 캐시 삭제:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('✅ 서비스 워커 활성화 완료');
|
||||
console.log(' 서비스 워커 활성화 완료');
|
||||
return self.clients.claim();
|
||||
})
|
||||
);
|
||||
@@ -251,7 +259,7 @@ function isCDNResource(url) {
|
||||
async function handleOffline(request) {
|
||||
// HTML 요청에 대한 오프라인 페이지
|
||||
if (request.destination === 'document') {
|
||||
const offlinePage = await caches.match('/index.html');
|
||||
const offlinePage = await caches.match('/app.html');
|
||||
if (offlinePage) {
|
||||
return offlinePage;
|
||||
}
|
||||
@@ -308,7 +316,7 @@ async function clearAllCaches() {
|
||||
await Promise.all(
|
||||
cacheNames.map(cacheName => caches.delete(cacheName))
|
||||
);
|
||||
console.log('🗑️ 모든 캐시 정리 완료');
|
||||
console.log(' 모든 캐시 정리 완료');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,7 +326,7 @@ async function cachePage(url) {
|
||||
try {
|
||||
const cache = await caches.open(DYNAMIC_CACHE);
|
||||
await cache.add(url);
|
||||
console.log('📦 페이지 캐시 완료:', url);
|
||||
console.log(' 페이지 캐시 완료:', url);
|
||||
} catch (error) {
|
||||
console.error('페이지 캐시 실패:', url, error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user