From 6e3c5d674821a4a2bc08e87c15f8e2e8af7e2d5d Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Wed, 1 Apr 2026 13:53:47 +0900 Subject: [PATCH] =?UTF-8?q?fix(auth):=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=91=9C=EC=8B=9C=20+=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EB=AA=A8?= =?UTF-8?q?=EB=B0=94=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인: errEl.style.display='' → 'block' (CSS display:none과 충돌 해소) - 비밀번호 변경: ES모듈→일반 스크립트 전환 (모바일 import 체인 실패 대응) - api-config.js/config.js/navigation.js 의존 제거 - tkfb-core.js 전역 함수(getSSOToken) + fetch 직접 사용 - TODO: ES모듈 import 체인 실패 원인 별도 조사 필요 Co-Authored-By: Claude Opus 4.6 (1M context) --- gateway/html/dashboard.html | 2 +- system1-factory/web/js/change-password.js | 312 +++++++----------- .../web/pages/profile/password.html | 3 +- 3 files changed, 118 insertions(+), 199 deletions(-) diff --git a/gateway/html/dashboard.html b/gateway/html/dashboard.html index 1ca90d6..056f828 100644 --- a/gateway/html/dashboard.html +++ b/gateway/html/dashboard.html @@ -787,7 +787,7 @@ } } catch (err) { errEl.textContent = err.message; - errEl.style.display = ''; + errEl.style.display = 'block'; } finally { btn.disabled = false; btn.textContent = '\uB85C\uADF8\uC778'; diff --git a/system1-factory/web/js/change-password.js b/system1-factory/web/js/change-password.js index 806fe71..f6b207d 100644 --- a/system1-factory/web/js/change-password.js +++ b/system1-factory/web/js/change-password.js @@ -1,208 +1,128 @@ -// js/change-password.js -// 개인 비밀번호 변경 페이지 JavaScript +// js/change-password.js — 비밀번호 변경 (일반 스크립트, tkfb-core.js 전역 함수 사용) +(function() { + var form = document.getElementById('changePasswordForm'); + var messageArea = document.getElementById('message-area'); + var submitBtn = document.getElementById('submitBtn'); + var resetBtn = document.getElementById('resetBtn'); -import { API, getAuthHeaders, ensureAuthenticated } from '/js/api-config.js'; + if (!form) return; -// 인증 확인 -const token = ensureAuthenticated(); - -// DOM 요소 -const form = document.getElementById('changePasswordForm'); -const messageArea = document.getElementById('message-area'); -const submitBtn = document.getElementById('submitBtn'); -const resetBtn = document.getElementById('resetBtn'); - -// 비밀번호 토글 기능 -document.querySelectorAll('.password-toggle').forEach(button => { - button.addEventListener('click', function() { - const targetId = this.getAttribute('data-target'); - const input = document.getElementById(targetId); - - if (input) { - const isPassword = input.type === 'password'; - input.type = isPassword ? 'text' : 'password'; - this.textContent = isPassword ? '👁️‍🗨️' : '👁️'; - } + // 비밀번호 토글 + document.querySelectorAll('.password-toggle').forEach(function(button) { + button.addEventListener('click', function() { + var input = document.getElementById(this.getAttribute('data-target')); + if (input) { + var isPassword = input.type === 'password'; + input.type = isPassword ? 'text' : 'password'; + this.textContent = isPassword ? '숨기기' : '보기'; + } + }); }); -}); -// 초기화 버튼 -resetBtn?.addEventListener('click', () => { - form.reset(); - clearMessages(); - document.getElementById('passwordStrength').innerHTML = ''; -}); + // 초기화 + if (resetBtn) resetBtn.addEventListener('click', function() { + form.reset(); + messageArea.innerHTML = ''; + var s = document.getElementById('passwordStrength'); + if (s) s.innerHTML = ''; + }); -// 메시지 표시 함수 -function showMessage(type, message) { - messageArea.innerHTML = ` -
- ${type === 'error' ? '❌' : '✅'} ${message} -
- `; - - // 에러 메시지는 5초 후 자동 제거 - if (type === 'error') { - setTimeout(clearMessages, 5000); - } -} - -function clearMessages() { - messageArea.innerHTML = ''; -} - -// 비밀번호 강도 체크 -async function checkPasswordStrength(password) { - if (!password) { - document.getElementById('passwordStrength').innerHTML = ''; - return; + function showMessage(type, msg) { + messageArea.innerHTML = '
' + + (type === 'error' ? '❌ ' : '✅ ') + msg + '
'; + if (type === 'error') setTimeout(function() { messageArea.innerHTML = ''; }, 5000); } - try { - const res = await fetch(`${API}/auth/check-password-strength`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ password }) - }); - - const result = await res.json(); - updatePasswordStrengthUI(result); - } catch (error) { - console.error('Password strength check error:', error); - } -} + // 비밀번호 강도 체크 + var strengthTimer; + var newPwInput = document.getElementById('newPassword'); + if (newPwInput) newPwInput.addEventListener('input', function() { + clearTimeout(strengthTimer); + var pw = this.value; + strengthTimer = setTimeout(function() { + if (!pw) { document.getElementById('passwordStrength').innerHTML = ''; return; } + var token = (window.getSSOToken && window.getSSOToken()) || localStorage.getItem('sso_token') || ''; + fetch('/api/auth/check-password-strength', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, + body: JSON.stringify({ password: pw }) + }).then(function(r) { return r.json(); }).then(function(result) { + if (!result.success) return; + var d = result.data; + var colors = { weak: '#f44336', medium: '#ffc107', strong: '#4caf50' }; + var labels = { weak: '약함', medium: '보통', strong: '강함' }; + var pct = (d.score / 5) * 100; + document.getElementById('passwordStrength').innerHTML = + '
' + + '' + (labels[d.level]||'') + '' + + '' + d.score + '/5
' + + '
' + + '
'; + }).catch(function() {}); + }, 300); + }); -// 비밀번호 강도 UI 업데이트 -function updatePasswordStrengthUI(strength) { - const container = document.getElementById('passwordStrength'); - if (!container) return; - - const colors = { - 0: '#f44336', - 1: '#ff9800', - 2: '#ffc107', - 3: '#4caf50', - 4: '#2196f3' - }; - - const strengthText = strength.strengthText || '비밀번호를 입력하세요'; - const color = colors[strength.strength] || '#ccc'; - const percentage = (strength.score / strength.maxScore) * 100; - - container.innerHTML = ` -
-
- - ${strengthText} - - - ${strength.score}/${strength.maxScore} - -
-
-
-
- ${strength.feedback && strength.feedback.length > 0 ? ` -
    - ${strength.feedback.map(f => `
  • ${f}
  • `).join('')} -
- ` : ''} -
- `; -} + // 폼 제출 + form.addEventListener('submit', function(e) { + e.preventDefault(); + messageArea.innerHTML = ''; -// 비밀번호 입력 이벤트 -let strengthCheckTimer; -document.getElementById('newPassword')?.addEventListener('input', (e) => { - clearTimeout(strengthCheckTimer); - strengthCheckTimer = setTimeout(() => { - checkPasswordStrength(e.target.value); - }, 300); -}); + var currentPassword = document.getElementById('currentPassword').value; + var newPassword = document.getElementById('newPassword').value; + var confirmPassword = document.getElementById('confirmPassword').value; -// 폼 제출 -form?.addEventListener('submit', async (e) => { - e.preventDefault(); - clearMessages(); - - const currentPassword = document.getElementById('currentPassword').value; - const newPassword = document.getElementById('newPassword').value; - const confirmPassword = document.getElementById('confirmPassword').value; - - // 유효성 검사 - if (!currentPassword || !newPassword || !confirmPassword) { - showMessage('error', '모든 필드를 입력해주세요.'); - return; - } - - if (newPassword !== confirmPassword) { - showMessage('error', '새 비밀번호가 일치하지 않습니다.'); - return; - } - - if (newPassword.length < 6) { - showMessage('error', '비밀번호는 최소 6자 이상이어야 합니다.'); - return; - } - - if (currentPassword === newPassword) { - showMessage('error', '새 비밀번호는 현재 비밀번호와 달라야 합니다.'); - return; - } - - // 버튼 상태 변경 - const originalText = submitBtn.innerHTML; - submitBtn.disabled = true; - submitBtn.innerHTML = '처리 중...'; - - try { - const res = await fetch(`${API}/auth/change-password`, { - method: 'POST', - headers: getAuthHeaders(), - body: JSON.stringify({ - currentPassword, - newPassword - }) - }); - - const result = await res.json(); - - if (res.ok && result.success) { - showMessage('success', '비밀번호가 성공적으로 변경되었습니다.'); - form.reset(); - document.getElementById('passwordStrength').innerHTML = ''; - - // 카운트다운 시작 - let countdown = 3; - const countdownInterval = setInterval(() => { - showMessage('success', - `비밀번호가 변경되었습니다. ${countdown}초 후 로그인 페이지로 이동합니다.` - ); - countdown--; - - if (countdown < 0) { - clearInterval(countdownInterval); - if (window.clearSSOAuth) window.clearSSOAuth(); - window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login'; - } - }, 1000); - - } else { - const errorMessage = result.message || result.error || '비밀번호 변경에 실패했습니다.'; - showMessage('error', errorMessage); + if (!currentPassword || !newPassword || !confirmPassword) { + showMessage('error', '모든 필드를 입력해주세요.'); + return; + } + if (newPassword !== confirmPassword) { + showMessage('error', '새 비밀번호가 일치하지 않습니다.'); + return; + } + if (newPassword.length < 6) { + showMessage('error', '비밀번호는 최소 6자 이상이어야 합니다.'); + return; + } + if (currentPassword === newPassword) { + showMessage('error', '새 비밀번호는 현재 비밀번호와 달라야 합니다.'); + return; } - } catch (error) { - console.error('Password change error:', error); - showMessage('error', '서버와의 연결에 실패했습니다. 잠시 후 다시 시도해주세요.'); - } finally { - submitBtn.disabled = false; - submitBtn.innerHTML = originalText; - } -}); -// 페이지 로드 시 현재 사용자 정보 표시 -document.addEventListener('DOMContentLoaded', () => { - const user = JSON.parse(localStorage.getItem('sso_user') || '{}'); -}); \ No newline at end of file + var originalText = submitBtn.innerHTML; + submitBtn.disabled = true; + submitBtn.innerHTML = '처리 중...'; + + var token = (window.getSSOToken && window.getSSOToken()) || localStorage.getItem('sso_token') || ''; + fetch('/api/auth/change-password', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, + body: JSON.stringify({ currentPassword: currentPassword, newPassword: newPassword }) + }).then(function(res) { + return res.json().then(function(data) { return { ok: res.ok, data: data }; }); + }).then(function(result) { + if (result.ok && result.data.success) { + showMessage('success', '비밀번호가 변경되었습니다.'); + form.reset(); + var s = document.getElementById('passwordStrength'); + if (s) s.innerHTML = ''; + var countdown = 3; + var interval = setInterval(function() { + showMessage('success', '비밀번호가 변경되었습니다. ' + countdown + '초 후 로그인 페이지로 이동합니다.'); + countdown--; + if (countdown < 0) { + clearInterval(interval); + if (window.clearSSOAuth) window.clearSSOAuth(); + window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login'; + } + }, 1000); + } else { + showMessage('error', result.data.message || result.data.error || '비밀번호 변경에 실패했습니다.'); + } + }).catch(function() { + showMessage('error', '서버와의 연결에 실패했습니다. 잠시 후 다시 시도해주세요.'); + }).finally(function() { + submitBtn.disabled = false; + submitBtn.innerHTML = originalText; + }); + }); +})(); diff --git a/system1-factory/web/pages/profile/password.html b/system1-factory/web/pages/profile/password.html index e7bd5d5..ce20e76 100644 --- a/system1-factory/web/pages/profile/password.html +++ b/system1-factory/web/pages/profile/password.html @@ -391,8 +391,7 @@ - - +