diff --git a/src/views/Main.vue b/src/views/Main.vue index 2db6c41..1232820 100644 --- a/src/views/Main.vue +++ b/src/views/Main.vue @@ -175,42 +175,51 @@ const todayUnique = ref(0); const activeNow = ref(0); let analyticsTimer = null; -// ๐ŸŸข [NEW] ์›Œ์ปค ํ‘œ์ง€ํŒ์„ ๋‹ด์„ ๋ฐ˜์‘ํ˜• ๋ณ€์ˆ˜ (๊ธฐ๋ณธ๊ฐ’: nas) const currentServerSource = ref('nas'); const menuItems = [ { id: 'main', name: 'HOME' }, { id: 'youtube', name: 'YOUTUBE' }, - { id: 'project', name: 'PROJECT' }, + // { id: 'project', name: 'PROJECT' }, { id: 'about', name: 'ABOUT' }, { id: 'contact', name: 'CONTACT' } ]; const isLoggedIn = computed(() => !!authToken.value); -// ๐ŸŸข ํ—ค๋” ์ƒํƒœ์— ๋”ฐ๋ฅธ ํ„ฐ๋ฏธ๋„ UI ํ…์ŠคํŠธ ๋ณ€ํ™˜ const currentServerSourceDisplay = computed(() => { if (currentServerSource.value === 'firebase_night') { - return 'HOST: FIREBASE (NIGHT)'; + return 'SYS: CLOUD_REDUNDANCY (NIGHT)'; } if (currentServerSource.value === 'firebase_failover') { - return 'HOST: FIREBASE (FAILOVER)'; + return 'SYS: EMERGENCY_MIRROR (FAILOVER)'; } - return 'HOST: GNET-NAS (LOCAL)'; + if (currentServerSource.value === 'firebase_direct') { + return 'SYS: CLOUD_DIRECT (BYPASS)'; + } + return 'SYS: LOCAL_CORE (ACTIVE)'; }); -// ๐ŸŸข [NEW] ์›Œ์ปค๊ฐ€ ๋‚ ๋ ค์ค€ 'x-served-by' ํ—ค๋”๋ฅผ ์ง์ ‘ ์ฝ์–ด ํŒ๋ณ„ํ•˜๋Š” ํ•ต์‹ฌ ๊ฐ์ง€ ์—”์ง„ const detectRealtimeServerSource = async () => { + const hostname = window.location.hostname; + + if (hostname.includes('web.app') || hostname.includes('firebase')) { + currentServerSource.value = 'firebase_direct'; + return; + } + try { - // 0๋ฐ”์ดํŠธ ์งœ๋ฆฌ ์ดˆ๊ณ ์† HEAD ์š”์ฒญ์„ ๋‚ด ๋„๋ฉ”์ธ์œผ๋กœ ์ „์†กํ•˜์—ฌ ํ—ค๋”๋งŒ ๊ฐ€๋ณ๊ฒŒ ๊ธ์–ด์˜ด const res = await fetch(window.location.origin, { method: 'HEAD' }); const servedBy = res.headers.get('x-served-by'); if (servedBy) { - currentServerSource.value = servedBy; // 'nas', 'firebase_night', 'firebase_failover' ์ ์šฉ + currentServerSource.value = servedBy; + } else { + currentServerSource.value = 'nas'; } } catch (err) { console.error("์‹ค์‹œ๊ฐ„ ์„œ๋ฒ„ ์†Œ์Šค ํŒ๋ณ„ ์‹คํŒจ:", err); + currentServerSource.value = 'firebase_failover'; } }; @@ -290,26 +299,6 @@ const changeMenu = (id) => { }, 20); }; -const triggerOverlayProcess = (durationMs, initialMsg) => { - isProcessing.value = true; - overlayProgress.value = 0; - overlayStatus.value = initialMsg; - - const steps = 20; - const stepTime = durationMs / steps; - - const interval = setInterval(() => { - if (overlayProgress.value >= 100) { - clearInterval(interval); - setTimeout(() => { - isProcessing.value = false; - }, 150); - return; - } - overlayProgress.value += (100 / steps); - }, stepTime); -}; - const apiKey = 'AIzaSyD51LHwqoY8spjq6rsY_RlhBXzb96j7D6o'; const channelId = 'UC8L92ju0V88s6TcmLPb8Bkg'; const maxResults = 8; @@ -398,12 +387,11 @@ onMounted(() => { fetchVideos(); fetchAnalyticsData(); - // ๐ŸŸข [NEW] ๋ถ€ํŒ… ์‹œ ํ‘œ์ง€ํŒ ํ—ค๋”๋ฅผ ํ™•์ธํ•˜์—ฌ ์†Œ์Šค์ง€ ์ฆ‰์‹œ ๊ฐ์ง€ detectRealtimeServerSource(); analyticsTimer = setInterval(() => { fetchAnalyticsData(); - detectRealtimeServerSource(); // 30์ดˆ๋งˆ๋‹ค ์ƒ์กด ์—ฌ๋ถ€ ๋ฐ ๊ฒฝ๋กœ ์‹ค์‹œ๊ฐ„ ํŠธ๋ž˜ํ‚น + detectRealtimeServerSource(); }, 30000); const globaljQuery = window.$; @@ -442,7 +430,7 @@ onMounted(() => { }, 'help': function () { - this.echo(`- hello : Say hello\n- specialsource : Core info\n- email : Contact\n- status : Check Live Connected Users (์ ‘์†์ž ํ™•์ธ)\n- clear : Clear console`); + this.echo(`- hello : Say hello\n- specialsource : Core info\n- email : Contact\n- status : Check Live Connected Users\n- clear : Clear console`); }, }, { greetings: `[SYSTEM V3.2] INTERFACE ONLINE. TYPE 'help' FOR COMMANDS.`, @@ -550,7 +538,6 @@ onUnmounted(() => { box-shadow: 0 0 8px var(--tui-glow); } -/* ๐ŸŸข [NEW] ํ„ฐ๋ฏธ๋„ ๊ฐ์„ฑ์˜ ์‹ค์‹œ๊ฐ„ ๊ฐ์ง€ ์„œ๋ฒ„ ๋ฐฐ์ง€ ์Šคํƒ€์ผ๋ง */ .server-badge { font-size: 11px; font-weight: bold; @@ -561,25 +548,10 @@ onUnmounted(() => { border-radius: 2px; transition: 0.3s; } -/* NAS ํ™œ์„ฑํ™” ์ƒํƒœ (๊ทธ๋ฆฐ ๊ณ„์—ด/ํ˜„์žฌ ํ…Œ๋งˆ ์ปฌ๋Ÿฌ ์ ์šฉ) */ -.server-badge.nas { - color: var(--tui-color); - text-shadow: 0 0 5px var(--tui-glow); - border-color: var(--tui-border); -} -/* ์ •๊ธฐ ์•ผ๊ฐ„ Firebase ๋ฐฑ์—… ์ž‘๋™ ์ƒํƒœ (ํ˜ธ๋ฐ•์ƒ‰) */ -.server-badge.firebase_night { - color: #ffb000; - text-shadow: 0 0 5px rgba(255, 176, 0, 0.6); - border-color: rgba(255, 176, 0, 0.3); -} -/* ๋Œ€๋‚ฎ ๋น„์ƒ ํ†ต์‹ ์žฅ์•  Firebase ๋ฐฑ์—… ๊ฐ€๋™ ์ƒํƒœ (๊ฐ•๋ ฅํ•œ ๋ ˆ๋“œ ๊ฒฝ๊ณ  & ์ ๋ฉธ) */ -.server-badge.firebase_failover { - color: #ff3b3b; - text-shadow: 0 0 8px #ff3b3b; - border-color: rgba(255, 59, 59, 0.4); - animation: badgeBlink 1.5s infinite ease-in-out; -} +.server-badge.nas { color: var(--tui-color); text-shadow: 0 0 5px var(--tui-glow); border-color: var(--tui-border); } +.server-badge.firebase_night { color: #ffb000; text-shadow: 0 0 5px rgba(255, 176, 0, 0.6); border-color: rgba(255, 176, 0, 0.3); } +.server-badge.firebase_direct { color: #ffb000; text-shadow: 0 0 8px rgba(255, 176, 0, 0.6); border-color: rgba(255, 176, 0, 0.4); } +.server-badge.firebase_failover { color: #ff3b3b; text-shadow: 0 0 8px #ff3b3b; border-color: rgba(255, 59, 59, 0.4); animation: badgeBlink 1.5s infinite ease-in-out; } /* --- GNB --- */ .auth-nav { @@ -592,117 +564,53 @@ onUnmounted(() => { } .nav-left { display: flex; align-items: center; gap: 15px; } .nav-menu { display: flex; gap: 20px; } -.menu-btn { - background: none; border: none; color: var(--tui-dim); - font-size: 16px; cursor: pointer; - transition: 0.3s; -} +.menu-btn { background: none; border: none; color: var(--tui-dim); font-size: 16px; cursor: pointer; transition: 0.3s; } .menu-btn:hover, .menu-btn.active { color: var(--tui-color); text-shadow: 0 0 10px var(--tui-color); } - .auth-group { display: flex; align-items: center; } .nav-item { - color: var(--tui-color) !important; - text-decoration: none; - background: none; - border: 1px solid var(--tui-color) !important; - padding: 6px 18px; - margin-left: 10px; - cursor: pointer; - font-size: 13px; - box-shadow: 0 0 5px var(--tui-border); - transition: 0.3s; + color: var(--tui-color) !important; text-decoration: none; background: none; + border: 1px solid var(--tui-color) !important; padding: 6px 18px; margin-left: 10px; + cursor: pointer; font-size: 13px; box-shadow: 0 0 5px var(--tui-border); transition: 0.3s; } .nav-item:hover, .nav-item.active-auth { - background: var(--tui-border) !important; - color: var(--tui-color) !important; - box-shadow: 0 0 10px var(--tui-color); + background: var(--tui-border) !important; color: var(--tui-color) !important; box-shadow: 0 0 10px var(--tui-color); } .user-info { color: var(--tui-dim) !important; font-size: 12px; margin-right: 15px; letter-spacing: 1px;} .status-dot { width: 8px; height: 8px; background: #ff3b3b; border-radius: 50%; box-shadow: 0 0 8px #ff3b3b; transition: 0.5s; } .status-dot.secure-mode { background: var(--tui-color); box-shadow: 0 0 8px var(--tui-color); } /* --- ์ปจํ…์ธ  ์˜์—ญ --- */ -.content-area { - flex: 1; padding: 20px; min-height: 400px; - overflow-y: auto; - display: flex; - flex-direction: column; -} +.content-area { flex: 1; padding: 20px; min-height: 400px; overflow-y: auto; display: flex; flex-direction: column; } .sub-page { max-width: 1000px; margin: 0 auto; padding-top: 15px; width: 100%; } .page-title { border-bottom: 1px solid var(--tui-border); padding-bottom: 10px; margin-bottom: 20px; color: var(--tui-color);} -.main-page { - display: flex; - justify-content: center; - align-items: center; - flex: 1; - position: relative; -} +.main-page { display: flex; justify-content: center; align-items: center; flex: 1; position: relative; } .tui-global-overlay { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background-color: rgba(0, 0, 0, 0.88); - z-index: 9999; - display: flex; - justify-content: center; - align-items: center; - pointer-events: all; + position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; + background-color: rgba(0, 0, 0, 0.88); z-index: 9999; display: flex; + justify-content: center; align-items: center; pointer-events: all; } -.ascii-container { - width: 100%; - overflow: auto; - padding: 20px; - box-sizing: border-box; - position: relative; -} +.ascii-container { width: 100%; overflow: auto; padding: 20px; box-sizing: border-box; position: relative; } .ascii-art { - font-weight: bold; - white-space: pre; - display: block; - margin: 0 auto; - width: max-content; - - color: var(--tui-color) !important; - font-size: 14px; - line-height: 1.2; - border: none; - background: transparent; - transform-origin: center; - opacity: 1; - + font-weight: bold; white-space: pre; display: block; margin: 0 auto; width: max-content; + color: var(--tui-color) !important; font-size: 14px; line-height: 1.2; + border: none; background: transparent; transform-origin: center; opacity: 1; animation: once-cyber-glitch-wobble 2.5s linear 1 forwards !important; } -:deep(.bg-block) { - color: #050505 !important; - text-shadow: none !important; -} +:deep(.bg-block) { color: #050505 !important; text-shadow: none !important; } /* ๐Ÿ“บ ์œ ํŠœ๋ธŒ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ */ #videos-container { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 20px; - padding: 10px 20px; - max-width: 1300px; - margin: 0 auto; - width: 100%; - box-sizing: border-box; + display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; + padding: 10px 20px; max-width: 1300px; margin: 0 auto; width: 100%; box-sizing: border-box; } .video-card { - width: 100%; - background: #000; - padding: 10px; - border: 1px solid var(--tui-border); - position: relative; - cursor: pointer; - transition: 0.3s; + width: 100%; background: #000; padding: 10px; border: 1px solid var(--tui-border); box-sizing: border-box; + position: relative; cursor: pointer; transition: 0.3s; } .video-card:hover { border-color: var(--tui-color); box-shadow: 0 0 10px var(--tui-border); } .video-title { color: var(--tui-dim); margin-top: 10px; font-size: 14px; height: 40px; overflow: hidden; font-family: sans-serif; } @@ -710,27 +618,22 @@ iframe { width: 100%; height: 180px; border-radius: 4px; } /* ๋ณด์•ˆ ๋กœ๊ทธ์ธ ํ”„๋ ˆ์ž„ */ .login-terminal-frame { - max-width: 550px; margin: 40px auto; - border: 1px dashed var(--tui-color); padding: 35px; - background: #020a02; box-shadow: 0 0 15px var(--tui-border); + max-width: 550px; margin: 40px auto; border: 1px dashed var(--tui-color); padding: 35px; + background: #020a02; box-shadow: 0 0 15px var(--tui-border); box-sizing: border-box; } .login-gate-title { color: var(--tui-color); font-size: 20px; margin-bottom: 10px; font-weight: bold;} .login-gate-desc { color: var(--tui-dim); font-size: 12px; margin-bottom: 30px; line-height: 1.4; } .login-form-form { display: flex; flex-direction: column; gap: 20px; } .login-input-group { display: flex; align-items: center; border-bottom: 1px solid var(--tui-dim); padding-bottom: 5px; } -.login-label { width: 120px; color: var(--tui-color); font-weight: bold; font-size: 14px; } -.login-input { - flex: 1; background: none; border: none; color: var(--tui-color); - font-size: 15px; outline: none; -} +.login-label { width: 120px; color: var(--tui-color); font-weight: bold; font-size: 14px; flex-shrink: 0; } +.login-input { flex: 1; background: none; border: none; color: var(--tui-color); font-size: 15px; outline: none; min-width: 0; } .login-submit-btn { margin-top: 15px; background: none; border: 1px solid var(--tui-color); - color: var(--tui-color); padding: 12px; - font-size: 14px; font-weight: bold; cursor: pointer; transition: 0.3s; + color: var(--tui-color); padding: 12px; font-size: 14px; font-weight: bold; cursor: pointer; transition: 0.3s; } .login-submit-btn:hover { background: var(--tui-color); color: #000; box-shadow: 0 0 15px var(--tui-color); } -/* ํ”„๋กœ์ ํŠธ ๋ฆฌ์ŠคํŠธ */ +/* ํ”„๋กœ์ ํŠธ, ์–ด๋ฐ”์›ƒ, ์—ฐ๋ฝ์ฒ˜ ๋“ฑ */ .project-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; } .project-item { border: 1px solid var(--tui-border); padding: 15px; background: #080808; } .item-header { font-weight: bold; margin-bottom: 10px; color: var(--tui-color); } @@ -742,10 +645,7 @@ iframe { width: 100%; height: 180px; border-radius: 4px; } .fade-enter-from, .fade-leave-to { opacity: 0; } /* ํ•˜๋‹จ CRT ํ„ฐ๋ฏธ๋„ */ -.crt-monitor-frame { - height: 300px; margin: 10px 40px 30px; - border: 2px solid #1a1a1a; background: #0a0a0a; overflow: hidden; -} +.crt-monitor-frame { height: 300px; margin: 10px 40px 30px; border: 2px solid #1a1a1a; background: #0a0a0a; overflow: hidden; } .monitor-header { background: #151515; padding: 5px 15px; color: #555; display: flex; justify-content: space-between; font-size: 12px;} .monitor-status { color: var(--tui-dim); animation: blink 2s infinite; } .crt-screen-overlay { position: relative; height: calc(100% - 30px); background: #020802; } @@ -754,22 +654,36 @@ iframe { width: 100%; height: 180px; border-radius: 4px; } background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); z-index: 2; background-size: 100% 4px, 6px 100%; pointer-events: none; } -.terminal-box { width: 100%; height: 100%; padding: 10px; } +.terminal-box { width: 100%; height: 100%; padding: 10px; box-sizing: border-box; } -/* ๐Ÿ“ฑ ๋ฐ˜์‘ํ˜• ๋ถ„๊ธฐ์  */ + +/* ============================================================== + ๐Ÿ“ฑ ๋ฐ˜์‘ํ˜• ๋ถ„๊ธฐ์  (Responsive Design) + ============================================================== */ @media screen and (max-width: 1100px) { #videos-container { grid-template-columns: repeat(2, 1fr); } .project-grid { grid-template-columns: repeat(2, 1fr); } } + @media screen and (max-width: 650px) { - #videos-container { grid-template-columns: 1fr; } + /* 1. ๋ ˆ์ด์•„์›ƒ & ํŒจ๋”ฉ ์ดˆ๊ธฐํ™” */ + .content-area { padding: 10px; } + #videos-container { grid-template-columns: 1fr; padding: 10px; } .project-grid { grid-template-columns: 1fr; } + /* 2. ๋„ค๋น„๊ฒŒ์ด์…˜๋ฐ” ์ค„๋ฐ”๊ฟˆ ํ—ˆ์šฉ (ํ™”๋ฉด ๋ฐ–์œผ๋กœ ๋„˜์–ด๊ฐ€์ง€ ์•Š๊ฒŒ ๋ฐฉ์–ด) */ .auth-nav { flex-direction: column; gap: 15px; - padding: 15px 20px; - align-items: center; + padding: 15px 10px; /* ์ขŒ์šฐ ํŒจ๋”ฉ ์ค„์ž„ */ + align-items: stretch; /* ์ „์ฒด ๋„ˆ๋น„ ์ฑ„์šฐ๊ธฐ */ + } + .nav-left { + flex-wrap: wrap; /* ๋ฐฐ์ง€, ํ†ต๊ณ„ ๋“ฑ์ด ์ข์œผ๋ฉด ์•„๋ž˜๋กœ ์ค„๋ฐ”๊ฟˆ */ + justify-content: center; + text-align: center; + gap: 10px; + width: 100%; } .nav-menu { display: flex; @@ -779,51 +693,49 @@ iframe { width: 100%; height: 180px; border-radius: 4px; } width: 100%; } .menu-btn { font-size: 14px; } - .auth-group { width: 100%; justify-content: center; margin-top: 5px; } - .nav-item { margin: 0 6px; } - .ascii-container { + .auth-group { width: 100%; justify-content: center; margin-top: 5px; flex-wrap: wrap; gap: 10px; } + .nav-item { margin: 0; } /* ๊ฐ„๊ฒฉ ์กฐ์ • */ + + /* 3. ๋กœ๊ทธ์ธ ํผ ์„ธ๋กœ ๋ฐฐ์น˜ (๊ธ€์”จ๊ฐ€ ๊ฒน์น˜๋Š” ๊ฒƒ ๋ฐฉ์ง€) */ + .login-terminal-frame { + margin: 15px 5px; + padding: 20px 15px; /* ๋ชจ๋ฐ”์ผ์—์„œ๋Š” ์—ฌ๋ฐฑ์„ ์ข๊ฒŒ */ + } + .login-input-group { + flex-direction: column; /* ๋ผ๋ฒจ๊ณผ ์ธํ’‹์„ ์œ„์•„๋ž˜๋กœ ๋ฐฐ์น˜ */ + align-items: flex-start; + border-bottom: none; /* ํ•˜๋‹จ ์„  ์ œ๊ฑฐ */ + gap: 8px; + } + .login-label { width: 100%; font-size: 13px; } + .login-input { width: 100%; - overflow-x: auto !important; - overflow-y: hidden !important; - padding: 20px 10px; - display: block; - box-sizing: border-box; - -webkit-overflow-scrolling: touch; + border-bottom: 1px solid var(--tui-dim); + padding-bottom: 5px; + border-radius: 0; } + /* 4. ASCII ์•„ํŠธ ๊ฐ€๋…์„ฑ ๋ฐ ๊ฐ€๋กœ ์Šคํฌ๋กค ํ—ˆ์šฉ */ + .ascii-container { + padding: 10px 5px; + } .ascii-art { - font-size: 11px !important; - line-height: 1.35 !important; - white-space: pre !important; - word-break: keep-all !important; - width: max-content !important; - margin: 0 !important; - -webkit-text-size-adjust: none !important; - text-size-adjust: none !important; - max-height: 999999px !important; + font-size: 9px !important; /* ๋ชจ๋ฐ”์ผ ์‚ฌ์ด์ฆˆ์— ๋งž๊ฒŒ ์ถ•์†Œ */ + line-height: 1.2 !important; } + /* 5. ํ„ฐ๋ฏธ๋„ ์ฐฝ ๋ชจ๋ฐ”์ผ ์ตœ์ ํ™” */ .crt-monitor-frame { height: 180px !important; - margin: 10px 15px 15px !important; + margin: 10px !important; } .monitor-header { padding: 4px 10px; font-size: 10px; } } + @keyframes blink { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } } - -@keyframes textPulse { - 0%, 100% { opacity: 0.3; } - 50% { opacity: 1; text-shadow: 0 0 8px var(--tui-glow); } -} - -/* ๐ŸŸข [NEW] ๋น„์ƒ Failover ์ ๋ฉธ ์• ๋‹ˆ๋ฉ”์ด์…˜ */ -@keyframes badgeBlink { - 0%, 100% { opacity: 0.6; box-shadow: 0 0 5px rgba(255, 59, 59, 0.2); } - 50% { opacity: 1; box-shadow: 0 0 15px rgba(255, 59, 59, 0.6); text-shadow: 0 0 12px #ff3b3b; } -} - -/* ์ผํšŒ์„ฑ ์‚ฌ์ด๋ฒ„ ๊ธ€๋ฆฌ์น˜ ํ‚คํ”„๋ ˆ์ž„ */ +@keyframes textPulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; text-shadow: 0 0 8px var(--tui-glow); } } +@keyframes badgeBlink { 0%, 100% { opacity: 0.6; box-shadow: 0 0 5px rgba(255, 59, 59, 0.2); } 50% { opacity: 1; box-shadow: 0 0 15px rgba(255, 59, 59, 0.6); text-shadow: 0 0 12px #ff3b3b; } } @keyframes once-cyber-glitch-wobble { 0%, 25% { transform: translate(0) scale(1) skewX(0deg); color: var(--tui-color) !important; text-shadow: 0 0 8px var(--tui-glow) !important; opacity: 1; } 26% { transform: translate(-3px, 1.5px) scale(1.02); color: #ffffff !important; text-shadow: 2px 0 #ff0000, -2px 0 #0000ff !important; }