feat(portal-shell): add MSW mock layer with production bundle exclusion (P1-5)
MSW v2.7.0 fallback layer covering dashboard/users/exams/grades domains. NEXT_PUBLIC_MSW=1 enables browser Service Worker + SSR route handler mock responses without backend. Production build excludes all mock data via Turbopack resolveAlias redirecting @/mocks to empty stub. Acceptance: build bundle (client+server) verified clean of mock strings; typecheck/lint/vitest (231 tests) all pass.
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
/**
|
||||
* GraphQL 同域代理(P0-3,ARCHITECTURE.md §3.4 V3-A2 / §4 / §5.2)
|
||||
* GraphQL 同域代理(P0-3 + P1-5,ARCHITECTURE.md §3.4 V3-A2/V3-A7 / §4 / §5.2 / §10 P1-5)
|
||||
*
|
||||
* 浏览器 Apollo Client 一律走同域 `/api/graphql`:
|
||||
* Browser → /api/graphql (本 Route Handler) → apollo-router :3000
|
||||
*
|
||||
* MSW 兜底层(P1-5):
|
||||
* - NEXT_PUBLIC_MSW=1 时,SSR 端 Apollo Client 也走 /api/graphql(见 apollo-client.ts)
|
||||
* - 本 Route Handler 检测 MSW 开关,开启时直接返回 mock 数据(不连接后端)
|
||||
* - Mock 数据来自 mocks/graphql-data.ts,与 MSW browser worker 共用
|
||||
* - 生产构建 NEXT_PUBLIC_MSW 不为 "1",此分支被 tree-shake 移除
|
||||
*
|
||||
* 职责:
|
||||
* 1. 从 httpOnly cookie `edu_session` 取 JWT,注入 `Authorization: Bearer`
|
||||
* 2. 透传 body(APQ hash 或 query)与 Apollo 相关头
|
||||
@@ -16,10 +22,13 @@
|
||||
*
|
||||
* 验收命令:
|
||||
* DevTools Network 面板无 `localhost:3000` 直连;所有 GraphQL 请求走 `/api/graphql`
|
||||
* NEXT_PUBLIC_MSW=1 pnpm dev → 仪表盘/users 有数据,无需后端
|
||||
*/
|
||||
import type { NextRequest } from "next/server";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { graphqlResponse } from "@/mocks/graphql-data";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const runtime = "nodejs";
|
||||
|
||||
@@ -30,6 +39,8 @@ const UPSTREAM_URL =
|
||||
process.env.NEXT_PUBLIC_APOLLO_ROUTER_URL ||
|
||||
"http://localhost:3000/graphql";
|
||||
|
||||
const MSW_ENABLED = process.env.NEXT_PUBLIC_MSW === "1";
|
||||
|
||||
/**
|
||||
* 从 Cookie 头解析指定 cookie 值。
|
||||
*/
|
||||
@@ -44,6 +55,16 @@ function readCookie(cookieHeader: string | null, name: string): string | null {
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest): Promise<NextResponse> {
|
||||
// P1-5 MSW 兜底层:开启时直接返回 mock 数据,不连接后端
|
||||
if (MSW_ENABLED) {
|
||||
const body = (await req.json().catch(() => ({}))) as {
|
||||
operationName?: string;
|
||||
};
|
||||
return NextResponse.json(graphqlResponse(body.operationName), {
|
||||
headers: { "Cache-Control": "no-store" },
|
||||
});
|
||||
}
|
||||
|
||||
const cookieHeader = req.headers.get("cookie");
|
||||
const token = readCookie(cookieHeader, SESSION_COOKIE);
|
||||
|
||||
@@ -117,6 +138,7 @@ export async function GET(): Promise<NextResponse> {
|
||||
ok: true,
|
||||
proxy: "/api/graphql",
|
||||
upstream: UPSTREAM_URL,
|
||||
msw: MSW_ENABLED,
|
||||
},
|
||||
{ status: 200 },
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user