diff --git a/direct-test-result.png b/direct-test-result.png new file mode 100644 index 0000000..9ca753d Binary files /dev/null and b/direct-test-result.png differ diff --git a/direct-test.mjs b/direct-test.mjs new file mode 100644 index 0000000..3d682e7 --- /dev/null +++ b/direct-test.mjs @@ -0,0 +1,127 @@ +import { chromium } from "@playwright/test"; + +(async () => { + console.log("════════════════════════════════════════════════════════"); + console.log(" 🔐 COMPLETE LOGIN FLOW TEST"); + console.log("════════════════════════════════════════════════════════\n"); + + const b = await chromium.launch({ headless: false }); + const p = await b.newPage(); + + // 모든 콘솔 로그 캡처 + const consoleLogs = []; + p.on("console", msg => { + const text = msg.text(); + consoleLogs.push(text); + if (text.includes("[Login]") || text.includes("[Dashboard]") || text.includes("[Auth]")) { + console.log(` 📝 ${text}`); + } + }); + + // 요청/응답 모니터링 + p.on("response", res => { + if (res.url().includes("auth") || res.url().includes("dashboard")) { + console.log(` 📡 ${res.status()} ${res.url().split('/').pop()}`); + } + }); + + try { + // 서버 준비 확인 + let serverReady = false; + for (let attempt = 0; attempt < 5; attempt++) { + try { + const resp = await fetch("http://localhost:5265/login.html"); + if (resp.ok) { + serverReady = true; + break; + } + } catch (e) {} + console.log(` [대기] 서버 시작 확인 중... (${attempt + 1}/5)`); + await new Promise(r => setTimeout(r, 5000)); + } + + if (!serverReady) { + console.log(" ❌ 서버가 시작되지 않음"); + await b.close(); + return; + } + + console.log("\n✅ 서버 준비 완료!\n"); + + // STEP 1: 로그인 페이지 로드 + console.log("1️⃣ 로그인 페이지 로드"); + await p.goto("http://localhost:5265/login.html", { waitUntil: "networkidle" }); + console.log(" ✓ 페이지 로드됨\n"); + + // STEP 2: 폼 입력 + console.log("2️⃣ 로그인 폼 입력 (admin/admin)"); + await p.fill("input[name='username']", "admin"); + await p.fill("input[name='password']", "admin"); + console.log(" ✓ 입력 완료\n"); + + // STEP 3: 로그인 제출 + console.log("3️⃣ 로그인 버튼 클릭"); + await p.click("button[type='submit']"); + console.log(" ✓ 클릭됨\n"); + + // STEP 4: 상태 모니터링 (10초) + console.log("4️⃣ 로그인 처리 모니터링 (10초):"); + let redirected = false; + for (let i = 1; i <= 10; i++) { + await new Promise(r => setTimeout(r, 1000)); + const url = p.url(); + const title = await p.title(); + + process.stdout.write(` [${i}s] URL: ${url}`); + + if (!url.includes("login")) { + console.log(" ✅ REDIRECTED!"); + redirected = true; + break; + } else { + console.log(""); + } + } + + console.log("\n5️⃣ 최종 상태:"); + const finalUrl = p.url(); + const finalTitle = await p.title(); + + console.log(` 📍 URL: ${finalUrl}`); + console.log(` 📄 Page Title: ${finalTitle}`); + + if (finalUrl.includes("/dashboard")) { + console.log(" ✅ 대시보드 URL 확인됨!"); + + const content = await p.content(); + if (content.includes("관리자 대시보드")) { + console.log(" ✅ 대시보드 콘텐츠 확인됨!"); + console.log("\n🎉 로그인 성공! 대시보드 정상 로드!\n"); + } else if (content.includes("Not Found")) { + console.log(" ❌ Not Found 에러"); + } else { + console.log(" ⚠️ 대시보드 콘텐츠 미확인"); + } + } else if (finalUrl.includes("/login")) { + console.log(" ❌ 다시 로그인 페이지로 리다이렉트됨"); + console.log(" → 대시보드 인증 체크에서 실패한 것 같습니다"); + } else if (finalUrl.includes("/not-found")) { + console.log(" ❌ /not-found 에러"); + } else { + console.log(" ⚠️ 예상치 못한 페이지"); + } + + // 스크린샷 + await p.screenshot({ path: "./direct-test-result.png", fullPage: true }); + console.log(" 📷 스크린샷: direct-test-result.png"); + + console.log("\n════════════════════════════════════════════════════════"); + console.log(" 테스트 완료"); + console.log("════════════════════════════════════════════════════════"); + + } catch (e) { + console.error("❌ 테스트 에러:", e.message); + } finally { + await b.close(); + } +})(); diff --git a/final-success-test.png b/final-success-test.png new file mode 100644 index 0000000..1fe702a Binary files /dev/null and b/final-success-test.png differ diff --git a/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor b/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor index 3e28bfc..c5543cb 100644 --- a/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor +++ b/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor @@ -1,4 +1,5 @@ @page "/dashboard" +@rendermode InteractiveWebAssembly @using QuantEngine.Core.Infrastructure @using Microsoft.AspNetCore.Components.Authorization @inject HttpClient Http @@ -244,13 +245,18 @@ { // Check authentication var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + Console.WriteLine($"[Dashboard] Auth state received. IsAuthenticated: {authState.User.Identity?.IsAuthenticated}"); + if (!authState.User.Identity?.IsAuthenticated ?? true) { // Not authenticated - redirect to login + Console.WriteLine("[Dashboard] Not authenticated. Redirecting to login..."); NavManager.NavigateTo("/login.html", forceLoad: true); return; } + Console.WriteLine($"[Dashboard] Authenticated as: {authState.User.Identity?.Name}"); + try { // Load operational report diff --git a/src/dotnet/QuantEngine.Web/Program.cs b/src/dotnet/QuantEngine.Web/Program.cs index cde0a2e..40a9b54 100644 --- a/src/dotnet/QuantEngine.Web/Program.cs +++ b/src/dotnet/QuantEngine.Web/Program.cs @@ -496,8 +496,8 @@ app.MapRazorPages(); // Map Blazor Components - catches all remaining routes app.MapRazorComponents() - .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() + .AddInteractiveServerRenderMode() .AddAdditionalAssemblies(typeof(QuantEngine.Web.Client._Imports).Assembly); app.Run(); diff --git a/src/dotnet/QuantEngine.Web/wwwroot/login.html b/src/dotnet/QuantEngine.Web/wwwroot/login.html index 760b82d..77fcc4d 100644 --- a/src/dotnet/QuantEngine.Web/wwwroot/login.html +++ b/src/dotnet/QuantEngine.Web/wwwroot/login.html @@ -302,16 +302,18 @@ // 토큰 저장 (Blazor 인증 상태에 필요) if (data.accessToken) { localStorage.setItem('quant_admin_access_token', data.accessToken); - console.log('[Login] Access token saved to localStorage'); + console.log('[Login] Access token saved to localStorage: ' + data.accessToken.substring(0, 16) + '...'); } // 사용자 정보 저장 if (data.username) { localStorage.setItem('quant_admin_username', data.username); + console.log('[Login] Username saved: ' + data.username); } if (data.role) { localStorage.setItem('quant_admin_role', data.role); + console.log('[Login] Role saved: ' + data.role); } // 아이디 자동입력 설정 저장 @@ -324,15 +326,20 @@ // 로그인 성공 메시지 표시 alertDiv.className = 'alert alert-success show'; - alertDiv.textContent = '로그인 성공! 대시보드로 이동합니다.'; + alertDiv.textContent = '로그인 성공! 대시보드로 이동합니다...'; - // Blazor 앱이 로드될 시간 제공 - // (대시보드는 이제 [Authorize] 없이 로드되고, 내부에서 인증 체크) - console.log('[Login] Token saved. Redirecting in 3 seconds...'); + // localStorage 확인 + const savedToken = localStorage.getItem('quant_admin_access_token'); + console.log('[Login] Verifying localStorage - Token exists: ' + (savedToken ? 'YES' : 'NO')); + + // Blazor WASM 앱이 로드될 시간 제공 + // Dashboard는 이제 @rendermode InteractiveWebAssembly로 설정되어 + // CustomAuthenticationStateProvider가 localStorage에서 토큰을 읽을 수 있습니다 + console.log('[Login] Redirecting to dashboard in 2 seconds...'); setTimeout(() => { console.log('[Login] Navigating to dashboard'); window.location.href = '/dashboard'; - }, 3000); + }, 2000); } else { const data = await response.json(); alertDiv.className = 'alert alert-error show'; diff --git a/test-verification.mjs b/test-verification.mjs new file mode 100644 index 0000000..fec0b61 --- /dev/null +++ b/test-verification.mjs @@ -0,0 +1,57 @@ +import { chromium } from "@playwright/test"; + +(async () => { + console.log("🔐 FINAL TEST - Server-side Token Verification\n"); + + const b = await chromium.launch({ headless: false }); + const p = await b.newPage(); + + const consoleLogs = []; + p.on("console", msg => { + const text = msg.text(); + consoleLogs.push(text); + if (text.includes("[Login]")) { + console.log(" 📝 " + text); + } + }); + + try { + console.log("1. Loading login page..."); + await p.goto("http://localhost:5265/login.html", { waitUntil: "networkidle" }); + + console.log("2. Submitting login..."); + await p.fill("input[name='username']", "admin"); + await p.fill("input[name='password']", "admin"); + await p.click("button[type='submit']"); + + console.log("3. Monitoring (15 seconds)...\n"); + for (let i = 1; i <= 15; i++) { + await new Promise(r => setTimeout(r, 1000)); + const url = p.url(); + if (!url.includes("login")) { + console.log(`\n ✅ [${i}s] Redirected to: ${url}`); + break; + } + } + + const finalUrl = p.url(); + console.log(`\n📍 Final URL: ${finalUrl}`); + + if (finalUrl.includes("/dashboard")) { + console.log("✅✅✅ SUCCESS! User is on dashboard!\n"); + const content = await p.content(); + if (content.includes("관리자 대시보드")) { + console.log("✅ Dashboard content confirmed!"); + } + } else { + console.log("❌ Still at: " + finalUrl); + } + + await p.screenshot({ path: "./final-success-test.png" }); + + } catch (e) { + console.error("Error:", e.message); + } + + await b.close(); +})(); diff --git a/ultimate-test-result.png b/ultimate-test-result.png new file mode 100644 index 0000000..9ca753d Binary files /dev/null and b/ultimate-test-result.png differ diff --git a/ultimate-test.mjs b/ultimate-test.mjs new file mode 100644 index 0000000..5fcb8da --- /dev/null +++ b/ultimate-test.mjs @@ -0,0 +1,68 @@ +import { chromium } from "@playwright/test"; + +(async () => { + console.log("════════════════════════════════════════════════════════"); + console.log(" ✅ FINAL TEST - @rendermode InteractiveWebAssembly"); + console.log("════════════════════════════════════════════════════════\n"); + + const b = await chromium.launch({ headless: false }); + const p = await b.newPage(); + + p.on("console", msg => { + const text = msg.text(); + if (text.includes("[") && text.includes("]")) { + console.log(" 📝 " + text); + } + }); + + try { + console.log("1️⃣ 로그인 페이지 로드"); + await p.goto("http://localhost:5265/login.html"); + + console.log("2️⃣ 로그인 (admin/admin)"); + await p.fill("input[name='username']", "admin"); + await p.fill("input[name='password']", "admin"); + await p.click("button[type='submit']"); + + console.log("3️⃣ 모니터링 (10초)\n"); + let redirected = false; + for (let i = 1; i <= 10; i++) { + await new Promise(r => setTimeout(r, 1000)); + const url = p.url(); + if (!url.includes("login")) { + console.log(`\n ✅ [${i}s] 리다이렉트됨!`); + console.log(` URL: ${url}`); + redirected = true; + break; + } + process.stdout.write(` [${i}s] ${url}\r`); + } + + const finalUrl = p.url(); + + if (finalUrl.includes("/dashboard")) { + console.log("\n\n✅✅✅ 성공! 대시보드에 도착했습니다!\n"); + + // 페이지 콘텐츠 확인 + await new Promise(r => setTimeout(r, 2000)); + const content = await p.content(); + + if (content.includes("관리자 대시보드")) { + console.log("✅ 대시보드 콘텐츠 확인됨!\n"); + console.log("🎉 로그인 완성! 인증 흐름 정상 작동!\n"); + } else { + console.log("⚠️ 대시보드 콘텐츠 미확인\n"); + } + } else { + console.log(`\n❌ 아직도 ${finalUrl}에 있습니다\n`); + } + + await p.screenshot({ path: "./ultimate-test-result.png", fullPage: true }); + console.log("📷 스크린샷: ultimate-test-result.png"); + + } catch (e) { + console.error("❌ Error:", e.message); + } + + await b.close(); +})();