From 4f3545374bad0dc0a3564fad89cc0995d7ac9b17 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sat, 7 Feb 2026 02:31:52 +0800 Subject: [PATCH] fix(demo): fetch sandbox UUID and binding for guest --- src/app/api/auth/session/route.ts | 67 +++++++++++++++++++++++++++- src/app/api/sandbox/binding/route.ts | 19 +++++--- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/app/api/auth/session/route.ts b/src/app/api/auth/session/route.ts index 9a4e350..6b214f5 100644 --- a/src/app/api/auth/session/route.ts +++ b/src/app/api/auth/session/route.ts @@ -2,9 +2,11 @@ import { cookies } from 'next/headers' import { NextRequest, NextResponse } from 'next/server' import { SESSION_COOKIE_NAME, clearSessionCookie } from '@lib/authGateway' -import { getAccountServiceApiBaseUrl } from '@server/serviceConfig' +import { getAccountServiceApiBaseUrl, getAccountServiceBaseUrl } from '@server/serviceConfig' +import { buildInternalServiceHeaders, isServiceTokenConfigured } from '@server/internalServiceAuth' const ACCOUNT_API_BASE = getAccountServiceApiBaseUrl() +const ACCOUNT_BASE = getAccountServiceBaseUrl() type AccountUser = { id?: string @@ -40,6 +42,13 @@ type SessionResponse = { error?: string } +type SandboxGuestResponse = { + email?: string + proxyUuid?: string + proxyUuidExpiresAt?: string + error?: string +} + function normalizeRole(role: unknown): string { if (typeof role !== 'string') { return 'user' @@ -74,11 +83,65 @@ async function fetchSession(token: string) { } } +async function fetchSandboxGuest(): Promise { + if (!isServiceTokenConfigured()) { + return null + } + + try { + const response = await fetch(`${ACCOUNT_BASE}/api/internal/sandbox/guest`, { + method: 'GET', + headers: buildInternalServiceHeaders({ + Accept: 'application/json', + }), + cache: 'no-store', + }) + + if (!response.ok) { + return null + } + + const payload = (await response.json().catch(() => null)) as SandboxGuestResponse | null + const proxyUuid = typeof payload?.proxyUuid === 'string' ? payload.proxyUuid.trim() : '' + if (!proxyUuid) { + return null + } + + const proxyUuidExpiresAt = + typeof payload?.proxyUuidExpiresAt === 'string' && payload.proxyUuidExpiresAt.trim().length > 0 + ? payload.proxyUuidExpiresAt.trim() + : undefined + + // Shape this as a pseudo-session user for the Guest/Demo experience. + return { + id: proxyUuid, + uuid: proxyUuid, + proxyUuid, + proxyUuidExpiresAt, + name: 'Guest user', + username: 'guest', + email: 'sandbox@svc.plus', + role: 'guest', + groups: ['guest', 'sandbox'], + permissions: ['read'], + readOnly: true, + tenantId: 'guest-sandbox', + tenants: [{ id: 'guest-sandbox', name: 'Guest Sandbox', role: 'guest' }], + mfaEnabled: false, + mfaPending: false, + } + } catch (error) { + console.error('Sandbox guest session proxy failed', error) + return null + } +} + export async function GET(request: NextRequest) { void request const token = (await cookies()).get(SESSION_COOKIE_NAME)?.value if (!token) { - return NextResponse.json({ user: null }) + const sandboxGuest = await fetchSandboxGuest() + return NextResponse.json({ user: sandboxGuest }) } const { response, data } = await fetchSession(token) diff --git a/src/app/api/sandbox/binding/route.ts b/src/app/api/sandbox/binding/route.ts index b44da6b..4fa9993 100644 --- a/src/app/api/sandbox/binding/route.ts +++ b/src/app/api/sandbox/binding/route.ts @@ -4,6 +4,7 @@ import { NextRequest, NextResponse } from 'next/server' import { getAccountServiceApiBaseUrl } from '@server/serviceConfig' import { getAccountSession } from '@server/account/session' +import { buildInternalServiceHeaders, isServiceTokenConfigured } from '@server/internalServiceAuth' const ACCOUNT_API_BASE = getAccountServiceApiBaseUrl() @@ -13,17 +14,24 @@ type ErrorPayload = { export async function GET(request: NextRequest) { const session = await getAccountSession(request) - if (!session.token) { + const canUseInternalToken = isServiceTokenConfigured() + if (!session.token && !canUseInternalToken) { return NextResponse.json({ error: 'unauthenticated' }, { status: 401 }) } try { + const headers = session.token + ? new Headers({ + Authorization: `Bearer ${session.token}`, + Accept: 'application/json', + }) + : buildInternalServiceHeaders({ + Accept: 'application/json', + }) + const response = await fetch(`${ACCOUNT_API_BASE}/sandbox/binding`, { method: 'GET', - headers: { - Authorization: `Bearer ${session.token}`, - Accept: 'application/json', - }, + headers, cache: 'no-store', }) @@ -46,4 +54,3 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: 'upstream_unreachable' }, { status: 502 }) } } -