From c79619898a8cf6888574f941a953f8c849deedfe Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Sat, 30 May 2026 14:48:09 +0800 Subject: [PATCH] fix: avoid terminal cold-start paywall race --- .../dashboard/ScanTerminalDashboard.tsx | 7 +++- .../__tests__/terminalAuthBootstrap.test.ts | 39 +++++++++++++++++++ .../__tests__/terminalGridPolicy.test.ts | 6 +++ .../scan-terminal/terminal-auth-bootstrap.ts | 8 +++- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx index 6dbf7d96..4c656a3e 100644 --- a/frontend/components/dashboard/ScanTerminalDashboard.tsx +++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx @@ -967,8 +967,13 @@ function ScanTerminalScreen() { } } catch {} setProAccess(createEmptyAccess(false)); - } else if (event === "TOKEN_REFRESHED" || event === "SIGNED_IN") { + } else if ( + event === "INITIAL_SESSION" || + event === "TOKEN_REFRESHED" || + event === "SIGNED_IN" + ) { try { + if (!session?.access_token) return; const payload = await loadAuthProfile(session?.access_token); setProAccess((prev) => mergeAccessStateWithAuthPayload(prev, payload)); } catch {} diff --git a/frontend/components/dashboard/scan-terminal/__tests__/terminalAuthBootstrap.test.ts b/frontend/components/dashboard/scan-terminal/__tests__/terminalAuthBootstrap.test.ts index 59e2dbf1..9e4ddd6e 100644 --- a/frontend/components/dashboard/scan-terminal/__tests__/terminalAuthBootstrap.test.ts +++ b/frontend/components/dashboard/scan-terminal/__tests__/terminalAuthBootstrap.test.ts @@ -79,4 +79,43 @@ export async function runTests() { cookieOnlyResult.user_id === "cookie-user", "terminal auth bootstrap should accept an authenticated cookie profile immediately", ); + + const delayedBearerSession = deferred<{ data: { session: { access_token: string } } }>(); + let coldStartSettled = false; + const coldStartResultPromise = loadTerminalAuthProfile({ + hasSupabasePublicEnv: true, + getSession: () => delayedBearerSession.promise, + loadAuthProfile: (accessToken) => { + const token = String(accessToken || ""); + if (!token) { + return Promise.resolve({ + authenticated: true, + user_id: "cookie-user", + subscription_active: null, + degraded_auth_profile: true, + }); + } + return Promise.resolve({ + authenticated: true, + user_id: "bearer-paid-user", + subscription_active: true, + }); + }, + }); + coldStartResultPromise.then(() => { + coldStartSettled = true; + }); + await flushMicrotasks(); + assert( + coldStartSettled === false, + "terminal auth bootstrap must not show a cold-start paywall from a degraded cookie profile before bearer auth has a chance to confirm Pro", + ); + + delayedBearerSession.resolve({ data: { session: { access_token: "paid-token" } } }); + const coldStartResult = await coldStartResultPromise; + assert( + coldStartResult.user_id === "bearer-paid-user" && + coldStartResult.subscription_active === true, + "terminal auth bootstrap should prefer the bearer-confirmed active Pro profile over a degraded cookie profile", + ); } diff --git a/frontend/components/dashboard/scan-terminal/__tests__/terminalGridPolicy.test.ts b/frontend/components/dashboard/scan-terminal/__tests__/terminalGridPolicy.test.ts index a5096c40..da5afed4 100644 --- a/frontend/components/dashboard/scan-terminal/__tests__/terminalGridPolicy.test.ts +++ b/frontend/components/dashboard/scan-terminal/__tests__/terminalGridPolicy.test.ts @@ -76,4 +76,10 @@ export function runTests() { signedOutBlock.indexOf("setProAccess(createEmptyAccess(false))"), "terminal auth listener must re-check the current Supabase session before clearing access on SIGNED_OUT events", ); + assert( + dashboardSource.includes('event === "INITIAL_SESSION"') && + dashboardSource.indexOf('event === "INITIAL_SESSION"') < + dashboardSource.indexOf('event === "TOKEN_REFRESHED"'), + "terminal auth listener must hydrate access from Supabase INITIAL_SESSION events during first navigation from the landing page", + ); } diff --git a/frontend/components/dashboard/scan-terminal/terminal-auth-bootstrap.ts b/frontend/components/dashboard/scan-terminal/terminal-auth-bootstrap.ts index c5832f74..9b80afb1 100644 --- a/frontend/components/dashboard/scan-terminal/terminal-auth-bootstrap.ts +++ b/frontend/components/dashboard/scan-terminal/terminal-auth-bootstrap.ts @@ -41,6 +41,12 @@ function firstKnownProfile(cookieResult: SettledProfile, bearerResult: SettledPr }; } +function canResolveProfileImmediately( + payload: TerminalAuthProfilePayload | null, +): payload is TerminalAuthProfilePayload { + return payload?.authenticated === true && payload.subscription_active === true; +} + export async function loadTerminalAuthProfile({ getSession, hasSupabasePublicEnv, @@ -56,7 +62,7 @@ export async function loadTerminalAuthProfile({ }); const resolveIfAuthenticated = (payload: TerminalAuthProfilePayload | null) => { - if (!payload?.authenticated || resolvedAuthenticated) return; + if (!canResolveProfileImmediately(payload) || resolvedAuthenticated) return; resolvedAuthenticated = true; resolveAuthenticated?.(payload); };