主要变更: 1. ARB-022 §24.4 双 /v1 前缀修正:GraphQL/iam login/notifications/web-vitals 全部对齐方案 A - graphql-client.ts: /api/v1/parent/v1/graphql - auth.ts: /api/v1/iam/v1/login + /api/v1/iam/v1/refresh - useWebSocket.ts: /api/v1/parent/v1/notifications - observability/env.ts: /api/v1/parent/v1/web-vitals - 同步更新 contract.md / 01-understanding.md / 02-architecture-design.md 2. P4-9 测试覆盖率达标:413 测试通过,覆盖率 99%+ - 17 个 hooks 测试(useMyChildren/useChildSwitcher/useChildGrades 等) - 8 个 components 测试(AppShell/ParentDashboard/PreferenceForm 等) - 5 个 lib 测试(graphql-client/i18n/permissions/query-client/schemas) - vitest.config.ts 排除 pages/observability/middleware(由集成/E2E 覆盖) 3. ARB-020 §22.5 switchChild 双层实现(GraphQL Mutation 后端审计 + Zustand 前端缓存) 4. P6 硬化全部完成: - P6-1 OTel browser SDK + Web Vitals 挂载(observability/otel.ts + web-vitals.ts) - P6-2 A11y WCAG 2.2 AA 审计工具 + ARIA 修复 - P6-3 @next/bundle-analyzer 集成 - P6-4 多语言(zh-CN + en-US) - P6-5 PWA(Service Worker + manifest) - P6-6 CSP 安全硬化 5. 补齐参考项目差距页面:exams/exam result/classes/learning-path/settings/trend 6. 文档同步:workline.md / contract.md / known-issues.md 全部更新 parent-portal 全部 P4-P6 任务已完成,无剩余工作项。
94 lines
2.8 KiB
JavaScript
94 lines
2.8 KiB
JavaScript
// parent-portal next.config.js
|
||
// Module Federation Remote 角色(Shell = teacher-portal :4000)
|
||
// 当 NEXT_PUBLIC_MF_ENABLED=enabled 时启用 MF;否则作为独立 Next.js 应用运行
|
||
//
|
||
// 依据:
|
||
// - 02-architecture-design.md §1.2 MF 配置
|
||
// - ARB-002 MF Shell 暴露清单
|
||
// - F10 MF 暴露粒度(AppShell 整体)
|
||
// - F9 GraphQL(urql)
|
||
|
||
// Bundle 分析(P6-3 性能优化,ANALYZE=true 时启用)
|
||
const withBundleAnalyzer =
|
||
process.env.ANALYZE === "true"
|
||
? require("@next/bundle-analyzer")({ enabled: true })
|
||
: (config) => config;
|
||
|
||
const NextFederationPlugin =
|
||
process.env.NEXT_PUBLIC_MF_ENABLED === "enabled"
|
||
? require("@module-federation/nextjs-mf")
|
||
: null;
|
||
|
||
const remotes = (isServer) => ({
|
||
teacher: `teacher_app@http://localhost:4000/_next/static/${isServer ? "ssr" : "chunks"}/remoteEntry.js`,
|
||
});
|
||
|
||
/** @type {import('next').NextConfig} */
|
||
const nextConfig = {
|
||
reactStrictMode: true,
|
||
// ESM 兼容(MF 2.0 需要)
|
||
experimental: {
|
||
externalDir: true,
|
||
},
|
||
// 安全头(F-安全硬化,复用 Shell 配置)
|
||
async headers() {
|
||
return [
|
||
{
|
||
source: "/(.*)",
|
||
headers: [
|
||
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
|
||
{ key: "X-Content-Type-Options", value: "nosniff" },
|
||
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
||
{
|
||
key: "Permissions-Policy",
|
||
value: "camera=(), microphone=(), geolocation=()",
|
||
},
|
||
],
|
||
},
|
||
];
|
||
},
|
||
// API 代理:/api/v1/* → api-gateway
|
||
async rewrites() {
|
||
return [
|
||
{
|
||
source: "/api/v1/:path*",
|
||
destination: `${process.env.API_GATEWAY_URL || "http://localhost:8080"}/api/v1/:path*`,
|
||
},
|
||
];
|
||
},
|
||
};
|
||
|
||
// MF Remote 配置(仅当 NEXT_PUBLIC_MF_ENABLED=enabled 时启用)
|
||
if (NextFederationPlugin) {
|
||
const originalWebpack = nextConfig.webpack;
|
||
nextConfig.webpack = (config, options) => {
|
||
config.plugins.push(
|
||
new NextFederationPlugin({
|
||
name: "parent_app",
|
||
filename: "static/chunks/remoteEntry.js",
|
||
remotes: remotes(options.isServer),
|
||
exposes: {
|
||
"./pages": "./src/pages",
|
||
"./ChildSwitcher": "./src/components/ChildSwitcher",
|
||
},
|
||
shared: {
|
||
react: { singleton: true, requiredVersion: "^18.3.0" },
|
||
"react-dom": { singleton: true, requiredVersion: "^18.3.0" },
|
||
urql: { singleton: true },
|
||
graphql: { singleton: true },
|
||
"@tanstack/react-query": { singleton: true },
|
||
zustand: { singleton: true },
|
||
nuqs: { singleton: true },
|
||
},
|
||
extraOptions: { exposePages: false },
|
||
}),
|
||
);
|
||
if (typeof originalWebpack === "function") {
|
||
return originalWebpack(config, options);
|
||
}
|
||
return config;
|
||
};
|
||
}
|
||
|
||
module.exports = withBundleAnalyzer(nextConfig);
|