/** * 헤더 프로필 드롭다운 */ (function initAuthHeader() { const DEFAULT_AVATAR = 'data:image/svg+xml,' + encodeURIComponent( '', ); let currentUser = null; let dropdownOpen = false; /** * HTML 이스케이프 * @param {string} str * @returns {string} */ function escapeHtml(str) { return String(str ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } /** * 드롭다운 닫기 */ function closeDropdown() { const dd = document.getElementById('auth-dropdown'); if (dd) dd.classList.remove('open'); dropdownOpen = false; } /** * 아바타 이미지 src (캐시 무효화 포함) * @param {object|null} user * @returns {string} */ function resolveAvatarSrc(user) { const raw = user?.avatarUrl; if (!raw) return DEFAULT_AVATAR; let src = window.UaDeckApi?.resolveMediaUrl ? window.UaDeckApi.resolveMediaUrl(raw) || raw : raw; if (user?.updatedAt && src && !src.startsWith('data:')) { src += (src.includes('?') ? '&' : '?') + 't=' + encodeURIComponent(user.updatedAt); } return src; } /** * 아바타 HTML * @param {object|null} user * @returns {string} */ function avatarHtml(user) { const src = resolveAvatarSrc(user); return `profile`; } /** * 프로필 버튼 HTML (아바타 + 사용자명) * @param {object|null} user * @returns {string} */ function profileBtnHtml(user) { const avatar = avatarHtml(user); if (!user?.username) { return `${avatar}`; } return ` ${avatar} ${escapeHtml(user.username)} `; } /** * 드롭다운 메뉴 렌더 */ function renderDropdown() { const dd = document.getElementById('auth-dropdown'); if (!dd) return; if (!currentUser) { dd.innerHTML = ` 로그인 회원가입 `; return; } const adminLink = currentUser.role === 'admin' ? `관리자` : ''; dd.innerHTML = `
${currentUser.username}
${currentUser.email || ''}
내 프로필 내 덱 관리 공개 덱 ${adminLink}
`; const logoutBtn = document.getElementById('auth-logout-btn'); if (logoutBtn) { logoutBtn.addEventListener('click', () => { window.UaDeckApi.logout(); currentUser = null; closeDropdown(); location.href = '/'; }); } } /** * 헤더 UI 갱신 */ function renderHeader() { const btn = document.getElementById('auth-avatar-btn'); if (btn) { btn.innerHTML = profileBtnHtml(currentUser); btn.classList.toggle('auth-profile-btn--named', Boolean(currentUser?.username)); } renderDropdown(); } /** * 슬롯에 헤더 마운트 * @param {HTMLElement} slot */ function mount(slot) { slot.innerHTML = `
`; document.getElementById('auth-avatar-btn').addEventListener('click', async (e) => { e.stopPropagation(); dropdownOpen = !dropdownOpen; const dd = document.getElementById('auth-dropdown'); if (dropdownOpen) { await refreshCurrentUser(); renderHeader(); dd.classList.add('open'); } else { dd.classList.remove('open'); } }); document.addEventListener('click', closeDropdown); } /** * 서버에서 최신 사용자 정보 동기화 */ async function refreshCurrentUser() { if (!window.UaDeckApi?.getToken()) { currentUser = null; return; } try { const { user } = await window.UaDeckApi.getMe(); currentUser = user; window.UaDeckApi.saveAuth(window.UaDeckApi.getToken(), user); } catch { window.UaDeckApi.logout(); currentUser = null; } } /** * 초기화 */ async function init() { currentUser = window.UaDeckApi.getCachedUser(); await refreshCurrentUser(); const slot = document.getElementById('auth-header-slot'); if (slot) { mount(slot); renderHeader(); } else { renderHeader(); } } document.addEventListener('DOMContentLoaded', init); window.UaDeckAuth = { getUser: () => currentUser, refresh: init, refreshCurrentUser, }; })();