#!/usr/bin/env node "use strict"; const { loginViaAuthForm } = require('./lib/browser-auth-login'); const assert = require("node:assert"); const { spawn } = require("node:child_process"); const { fetchWithTimeout, findFreePort, waitForGateway, } = require("./task114-rust-web-gateway-entry-smoke.js"); function buildFixtureEnv(port) { return { MNOTE_WEB_ALLOW_DEV_FIXTURES: "1", MNOTE_WEB_BIND: `127.0.0.1:${port}`, MNOTE_WEB_PUBLIC_BIND: "127.0.0.1:3000", MNOTE_WEB_QUERY_FIXTURES_JSON: JSON.stringify({ "documents:getMeta": { id: "doc_auth", workspace_id: "ws_demo", title: "认证验收页面", updated_at: "2026-05-04T00:00:00Z", can_edit: true, word_count: 3, character_count: 12, block_count: 1, }, "documents:getContent": { content: [{ id: "block_auth_1", type: "paragraph", content: [] }], revision: 1, conflict_detection_key: "doc_auth:1", pageSubtree: { rootNodeId: "doc_auth", outline: [] }, }, }), MNOTE_WEB_MUTATION_FIXTURES_JSON: JSON.stringify({ "workspaces:ensureDefaultWorkspace": { workspaces: [{ id: "ws_demo", name: "我的空间", type: "personal", memberCount: 1, isDefault: true }], activeWorkspaceId: "ws_demo", }, }), }; } function startGateway(port) { return spawn("cargo", ["run", "-q", "-p", "mnote-web", "--bin", "mnote-web"], { cwd: "/mnt/Data1T/mnote/rust", env: { ...process.env, ...buildFixtureEnv(port) }, stdio: ["ignore", "pipe", "pipe"], }); } async function validateAuthEntry(baseUrl) { const root = await fetchWithTimeout(`${baseUrl}/`); const rootText = await root.text(); assert.equal(root.status, 303, `/ 未登录应跳转 /auth 并保留 next: ${root.status} ${rootText.slice(0, 160)}`); assert.equal(root.headers.get("location"), "/auth?next=%2F"); assert.equal(root.headers.get("x-mnote-web-owner"), "mnote-web"); const auth = await fetchWithTimeout(`${baseUrl}/auth`); const authText = await auth.text(); assert.equal(auth.status, 200, `/auth 请求失败: ${auth.status}`); assert.equal(auth.headers.get("x-mnote-web-owner"), "mnote-web"); assert.equal(auth.headers.get("x-mnote-legacy-upstream"), null, "/auth 不应代理到 3100"); assert.match(authText, /data-mnote-shell="auth"/); assert.match(authText, /data-testid="mnote-auth-page"/); assert.match(authText, /账号登录/); assert.match(authText, /邮箱或用户名/); assert.match(authText, /name="account"[^>]*type="text"|type="text"[^>]*name="account"/); assert.match(authText, /data-auth-field="username" hidden/); assert.match(authText, /name="password"[^>]*type="password"|type="password"[^>]*name="password"/); assert.match(authText, /data-auth-mode="control-plane-session"/); assert.match(authText, /没有账号?注册/); assert.doesNotMatch(authText, /测试账号快速登录/); assert.doesNotMatch(authText, /data-auth-test-login/); assert.doesNotMatch(authText, /data-test-password/); assert.match(authText, /data-auth-submit/); assert.doesNotMatch(authText, /隐私政策|使用\s*Google|使用\s*GitHub|第三方快捷/iu); const authedAuth = await fetchWithTimeout(`${baseUrl}/auth`, { headers: { "x-mnote-actor-id": "user_real", "x-mnote-actor-type": "user", }, }); assert.equal(authedAuth.status, 303, `/auth 已登录应跳转 /: ${authedAuth.status}`); assert.equal(authedAuth.headers.get("location"), "/"); const authedRoot = await fetchWithTimeout(`${baseUrl}/`, { headers: { "x-mnote-actor-id": "user_real", "x-mnote-actor-type": "user", }, }); const authedRootText = await authedRoot.text(); assert.equal(authedRoot.status, 200, `/ 已登录根入口失败: ${authedRoot.status}`); assert.match(authedRootText, /data-mnote-shell="workspace"/); } async function main() { const external = (process.env.MNOTE_UI_BASE_URL || "").replace(/\/+$/, ""); let gateway = null; let baseUrl = external; if (!baseUrl) { const port = await findFreePort(); baseUrl = `http://127.0.0.1:${port}`; gateway = startGateway(port); await waitForGateway(baseUrl); } let stderr = ""; if (gateway) { gateway.stderr.on("data", (chunk) => { stderr += chunk.toString("utf8"); }); } try { await validateAuthEntry(baseUrl); console.log(JSON.stringify({ ok: true, baseUrl, task: "task159-auth-entry" }, null, 2)); } finally { if (gateway) { gateway.kill("SIGTERM"); setTimeout(() => gateway.kill("SIGKILL"), 2_000).unref(); if (stderr.trim()) { process.stderr.write(stderr.slice(-2000)); } } } } if (require.main === module) { main().catch((error) => { console.error(error); process.exit(1); }); }