v1 声称完成的下游工作经核查实际未完成: - api-gateway: /api/admin/graphql 路由未注册,go vet 编译失败 - teacher-bff: resolver 已完成但 schema 未同步(命名空间 vs 扁平) - iam: proto 缺 BatchGetUsers rpc 声明 v2 记录详细核查证据和修复要求
155 lines
5.3 KiB
JavaScript
155 lines
5.3 KiB
JavaScript
/**
|
||
* teacher-portal Next.js 配置
|
||
*
|
||
* 角色:MF Shell 宿主(ARB-002 §2.3)
|
||
* - exposes: AppShell + GraphQLProvider + 共享 hooks + 共享 UI 组件
|
||
* - shared: react/react-dom/urql/graphql/@edu/* 设为 singleton
|
||
* - remotes: P2 为空(NEXT_PUBLIC_MF_ENABLED=false),P3+ 逐步接入
|
||
* - i18n: next-intl(非路由式,cookie 驱动,zh-CN/en)
|
||
*
|
||
* 维护者:ai13(teacher-portal)
|
||
* 关联:ARB-002 §2.2 暴露清单、03-long-term-architecture.md §1.3 绞杀者模式
|
||
*/
|
||
|
||
/** @type {import('next').NextConfig} */
|
||
|
||
// next-intl 插件(非路由式:cookie 驱动,无 [locale] 路由段)
|
||
const createNextIntlPlugin = require("next-intl/plugin");
|
||
const withNextIntl = createNextIntlPlugin("./src/i18n.ts");
|
||
|
||
// NextFederationPlugin 动态 require,避免构建时硬依赖(MF 2.0 在 Next 14 App Router 下按需加载)
|
||
let NextFederationPlugin;
|
||
try {
|
||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||
NextFederationPlugin = require("@module-federation/nextjs-mf").NextFederationPlugin;
|
||
} catch {
|
||
// 依赖未安装时跳过 MF 配置(如纯 typecheck 环境)
|
||
NextFederationPlugin = null;
|
||
}
|
||
|
||
/**
|
||
* 构建 Remote 列表(P2 为空,P3+ 按 NEXT_PUBLIC_MF_ENABLED 启用)
|
||
*
|
||
* ARB-002 §2.3:P2 Remote 数量 = 0,NEXT_PUBLIC_MF_ENABLED=false 默认关
|
||
* P3 首个 Remote:student-portal(总裁 §1.3 step 4)
|
||
*/
|
||
function buildRemotes(isServer) {
|
||
if (process.env.NEXT_PUBLIC_MF_ENABLED !== "true") return {};
|
||
const chunkPath = isServer ? "ssr" : "chunks";
|
||
return {
|
||
student: `student_app@http://localhost:4001/_next/static/${chunkPath}/remoteEntry.js`,
|
||
parent: `parent_app@http://localhost:4002/_next/static/${chunkPath}/remoteEntry.js`,
|
||
admin: `admin_app@http://localhost:4003/_next/static/${chunkPath}/remoteEntry.js`,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* ARB-002 §2.2 MF Shell 暴露清单
|
||
*/
|
||
function buildExposes() {
|
||
return {
|
||
// 1. AppShell(布局 + 导航 + 认证守卫)
|
||
"./AppShell": "./src/components/AppShell.tsx",
|
||
// 2. GraphQLProvider(urql client 单例,总裁 §2.17 方案 A)
|
||
"./GraphQLProvider": "./src/app/providers.tsx",
|
||
// 3. 共享 hooks
|
||
"./useAuth": "./packages/hooks/src/use-auth.ts",
|
||
"./usePermission": "./packages/hooks/src/use-permission.ts",
|
||
"./useGraphQLClient": "./packages/hooks/src/use-graphql-client.ts",
|
||
// 4. 共享 UI 组件
|
||
"./ErrorBoundary": "./packages/ui-components/src/error-boundary.tsx",
|
||
"./Loading": "./packages/ui-components/src/loading.tsx",
|
||
"./Empty": "./packages/ui-components/src/empty.tsx",
|
||
"./RequirePermission": "./packages/ui-components/src/require-permission.tsx",
|
||
};
|
||
}
|
||
|
||
/**
|
||
* ARB-002 §2.2 MF shared singleton 配置
|
||
* 避免 Remote 多实例 + 缓存不一致
|
||
*/
|
||
function buildShared() {
|
||
return {
|
||
react: { singleton: true, requiredVersion: "^18.3.0" },
|
||
"react-dom": { singleton: true, requiredVersion: "^18.3.0" },
|
||
urql: { singleton: true, requiredVersion: "^2.2.0" },
|
||
graphql: { singleton: true, requiredVersion: "^16.8.0" },
|
||
"@edu/ui-tokens": { singleton: true },
|
||
"@edu/ui-components": { singleton: true },
|
||
"@edu/hooks": { singleton: true },
|
||
};
|
||
}
|
||
|
||
const nextConfig = {
|
||
reactStrictMode: true,
|
||
|
||
// typedRoutes 显式关闭(next-intl 非路由式不依赖路由类型)
|
||
experimental: {
|
||
typedRoutes: false,
|
||
},
|
||
|
||
// standalone 模式:Next.js 自动打包所有依赖到 .next/standalone
|
||
// 解决 pnpm symlink 在 Docker runtime 下无法解析 react/jsx-runtime 的问题
|
||
output: "standalone",
|
||
|
||
// ESM 模式:transpilePackages 对齐 NestJS ESM 规范
|
||
// @edu/hooks 等包使用 .js 后缀导入源码,需 transpile + extensionAlias
|
||
transpilePackages: [
|
||
"@edu/ui-components",
|
||
"@edu/ui-tokens",
|
||
"@edu/hooks",
|
||
"@edu/contracts",
|
||
"@edu/shared-ts",
|
||
],
|
||
|
||
// ARB-002:MF Shell 配置(exposes + shared,P2 无 remotes)
|
||
// 仅当 NEXT_PUBLIC_MF_ENABLED=true 时实例化 NextFederationPlugin
|
||
// (nextjs-mf 不支持 App Directory,P2 阶段 MF 未启用时跳过避免报错)
|
||
webpack(config, { isServer }) {
|
||
// ESM 包使用 .js 后缀导入源码(TS 文件),需映射 .js → .ts
|
||
config.resolve = config.resolve || {};
|
||
config.resolve.extensionAlias = {
|
||
...config.resolve.extensionAlias,
|
||
".js": [".ts", ".tsx", ".js"],
|
||
};
|
||
|
||
if (
|
||
NextFederationPlugin &&
|
||
process.env.NEXT_PUBLIC_MF_ENABLED === "true"
|
||
) {
|
||
config.plugins.push(
|
||
new NextFederationPlugin({
|
||
name: "teacher_app",
|
||
filename: "static/chunks/remoteEntry.js",
|
||
remotes: buildRemotes(isServer),
|
||
exposes: buildExposes(),
|
||
shared: buildShared(),
|
||
extraOptions: {
|
||
exposePages: false,
|
||
},
|
||
}),
|
||
);
|
||
}
|
||
return config;
|
||
},
|
||
|
||
// 反向代理:/api/v1/* → api-gateway :8080
|
||
async rewrites() {
|
||
const gateway = process.env.API_GATEWAY_URL || "http://localhost:8080";
|
||
return [
|
||
{
|
||
source: "/api/v1/:path*",
|
||
destination: `${gateway}/api/v1/:path*`,
|
||
},
|
||
{
|
||
// 登录端点(非 GraphQL,认证前提,F12: JWT 存 localStorage)
|
||
// 映射到 iam REST controller 路径 /v1/iam/*(iam.controller.ts @Controller("v1/iam"))
|
||
source: "/api/auth/:path*",
|
||
destination: `${gateway}/api/v1/iam/:path*`,
|
||
},
|
||
];
|
||
},
|
||
};
|
||
|
||
module.exports = withNextIntl(nextConfig);
|