From c09beb0d5424b328f1592f065a8eba4e7ac5b93e Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 18 May 2026 13:00:55 +0000 Subject: [PATCH] fix: preserve dev frontend auth cookies --- scripts/src/e2e.ts | 72 +++++++++++++++++++ .../backend-core/src/microservice_proxy.rs | 1 + .../microservices/k3sctl-adapter/src/index.ts | 2 +- 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/scripts/src/e2e.ts b/scripts/src/e2e.ts index 52e1a20b..0b6b3f22 100644 --- a/scripts/src/e2e.ts +++ b/scripts/src/e2e.ts @@ -114,6 +114,7 @@ const DATABASE_CHECK_NAMES = [ const FRONTEND_CHECK_NAMES = [ "frontend:login-provider-visible", "frontend:public-provider-info-visible", + "frontend:dev-auth-api-cookie", "frontend:sidebar-collapse", "frontend:mobile-nav-fixed-height", "frontend:mobile-content-top-aligned", @@ -1423,6 +1424,7 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 "frontend:overview-pgdata-visible", "frontend:no-naked-json-before-click", ]) || needPendingTask || needTaskHistory; + const needDevAuthApiCookie = wants("frontend:dev-auth-api-cookie"); const needRawProviderJson = wantsAny([ "frontend:public-provider-info-visible", "frontend:raw-json-explicit-button", @@ -1502,6 +1504,7 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 let mobileRailMin = 0; let mobileContentMetrics = { pageTop: 0, emptyTextOffset: 0 }; let bodyText = ""; + let devAuthApiMetrics: any = { checked: false }; let rawBlocksBefore = 0; let nakedJsonText = false; let pendingTaskText = ""; @@ -2895,7 +2898,76 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 && Number(headerIndexByNodeId.get(edge.source)) >= Number(headerIndexByNodeId.get(edge.target))) : []; const metNonlinearTextLower = metNonlinearInitialText.toLowerCase(); + if (needDevAuthApiCookie) { + const devFrontendUrl = `http://${config.network.publicHost}:${config.network.devFrontend.port}`; + const cookieJar = await page.context().cookies([devFrontendUrl]); + const authContext = await browser.newContext({ viewport: { width: 1440, height: 920 } }); + await authContext.addCookies(cookieJar); + const devPage = await authContext.newPage(); + devPage.on("console", (message) => { + if (message.type() === "error") consoleErrors.push(message.text()); + }); + devPage.on("pageerror", (error) => consoleErrors.push(error.message)); + devPage.on("dialog", (dialog) => dialog.accept()); + await devPage.goto(devFrontendUrl, { waitUntil: "domcontentloaded", timeout: 15000 }); + await devPage.waitForSelector('[data-testid="app-shell"]', { timeout: 10000 }).catch(() => undefined); + await devPage.waitForTimeout(1200); + const authenticatedDevResponse = await devPage.evaluate(async () => { + const response = await fetch("/api/microservices", { credentials: "same-origin" }); + const body = await response.json().catch(() => null); + return { + status: response.status, + ok: response.ok, + body, + }; + }); + const devUiState = await devPage.evaluate(() => { + const appShell = document.querySelector('[data-testid="app-shell"]'); + const connText = document.querySelector('[data-testid="conn-text"]')?.textContent || ""; + return { + appShellVisible: Boolean(appShell), + connText, + }; + }); + await authContext.close(); + const unauthContext = await browser.newContext({ viewport: { width: 1440, height: 920 } }); + const unauthPage = await unauthContext.newPage(); + await unauthPage.goto(devFrontendUrl, { waitUntil: "domcontentloaded", timeout: 15000 }); + const unauthenticatedDevResponse = await unauthPage.evaluate(async () => { + const response = await fetch("/api/microservices", { credentials: "same-origin" }); + const body = await response.json().catch(() => null); + return { + status: response.status, + ok: response.ok, + body, + }; + }); + await unauthContext.close(); + devAuthApiMetrics = { + checked: true, + devOrigin: devFrontendUrl, + productionSmoke: { + frontendUrl: urls.frontendUrl, + landedUrl, + publicFrontendReached, + }, + authenticated: authenticatedDevResponse, + ui: devUiState, + unauthenticated: unauthenticatedDevResponse, + }; + } addSelectedCheck(checks, options, "frontend:login-provider-visible", bodyText.includes(config.providerGateway.id) && bodyText.includes(config.providerGateway.name) && bodyText.includes("核心在线"), { screenshotPath }); + addSelectedCheck(checks, options, "frontend:dev-auth-api-cookie", + devAuthApiMetrics.checked === true + && devAuthApiMetrics.authenticated?.ok === true + && devAuthApiMetrics.authenticated?.status !== 401 + && Array.isArray(devAuthApiMetrics.authenticated?.body?.microservices) + && devAuthApiMetrics.ui?.appShellVisible === true + && String(devAuthApiMetrics.ui?.connText || "").includes("核心在线") + && devAuthApiMetrics.productionSmoke?.publicFrontendReached === true + && devAuthApiMetrics.unauthenticated?.status === 401 + && String(devAuthApiMetrics.unauthenticated?.body?.error || "") === "authentication required", + { devAuthApiMetrics }); addSelectedCheck(checks, options, "frontend:public-provider-info-visible", publicFrontendReached && bodyText.includes(config.providerGateway.id) && bodyText.includes(config.providerGateway.name) && rawText.includes('"status": "online"') && rawText.includes(`"providerId": "${config.providerGateway.id}"`), { frontendUrl: urls.frontendUrl, landedUrl, providerId: config.providerGateway.id, rawTextPreview: rawText.slice(0, 400) }); addSelectedCheck(checks, options, "frontend:sidebar-collapse", railWidthBefore >= 160 && railWidthCollapsed <= 70, { railWidthBefore, railWidthCollapsed }); addSelectedCheck(checks, options, "frontend:mobile-nav-fixed-height", mobileRailHeights.length > 0 && mobileRailMax - mobileRailMin <= 1 && mobileRailMax <= 44, { mobileRailHeights }); diff --git a/src/components/backend-core/src/microservice_proxy.rs b/src/components/backend-core/src/microservice_proxy.rs index 1cc13d24..8df03656 100644 --- a/src/components/backend-core/src/microservice_proxy.rs +++ b/src/components/backend-core/src/microservice_proxy.rs @@ -22,6 +22,7 @@ const PROVIDER_HTTP_TUNNEL_MAX_ATTEMPTS: i32 = 3; const FORWARD_REQUEST_HEADERS: &[&str] = &[ "accept", "content-type", + "cookie", "range", "x-auth", "x-requested-with", diff --git a/src/components/microservices/k3sctl-adapter/src/index.ts b/src/components/microservices/k3sctl-adapter/src/index.ts index 23fd38f2..1000dfac 100644 --- a/src/components/microservices/k3sctl-adapter/src/index.ts +++ b/src/components/microservices/k3sctl-adapter/src/index.ts @@ -1229,7 +1229,7 @@ async function serviceDiagnostics(service: ManagedService): Promise function forwardHeaders(request: Request): Headers { const headers = new Headers(); - for (const name of ["accept", "content-type", "x-requested-with"]) { + for (const name of ["accept", "content-type", "cookie", "x-requested-with"]) { const value = request.headers.get(name); if (value !== null) headers.set(name, value); }