Files
Edu/apps/parent-portal/next.config.js
SpecialX 7c8e0f5dea feat(parent-portal): 完成 P4-P6 全部任务 + ARB-022 §24.4 双 /v1 修正 + 413 测试通过
主要变更:
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 任务已完成,无剩余工作项。
2026-07-13 13:10:07 +08:00

94 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 GraphQLurql
// 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);