1. teacher-portal 登录 JSON 解析错误修复
- 根因:next.config.js rewrites /api/auth/* → gateway /api/auth/*,
但 gateway 只在 /api/v1 路由组下注册代理,/api/auth/* 返回 404 纯文本
- 修复:rewrites 映射 /api/auth/* → /api/v1/iam/*(对齐 iam controller @Controller("v1/iam"))
2. msg / data-ana 端口冲突修复
- 根因:msg env.ts PORT 默认 3006,与 data-ana(config.py http_port=3006)冲突
- 修复:msg PORT 默认 3006→3007(对齐端口分配表 msg=3007, data-ana=3006)
3. Prometheus classes-service 3001 DOWN 修复
- 根因:classes 已合并入 core-edu(P3 裁决 C1),但 prometheus.yml 仍有 classes-service scrape 配置
- 修复:从 prometheus.yml 删除 classes-service job
4. iam outbox 表 aggregate_id 列长度修复
- 根因:iam_outbox.aggregate_id VARCHAR(32),但 UUID 是 36 字符,导致 "Data too long" 错误
- 修复:DDL 中 VARCHAR(32) → VARCHAR(36);ALTER TABLE 修复现有数据库
5. 补充未搭建的监控基础设施
- docker-compose.yml 新增 Alertmanager(9093)/ Loki(3100)/ Promtail(observability profile)
- prometheus.yml alertmanager target 更新为 edu-alertmanager:9093
验证:
- TS typecheck 19 项目全部通过
- 登录链路 teacher-portal → gateway → iam 验证通过
- Prometheus targets 不再有 classes-service
- 3 个新基础设施容器(alertmanager/loki/promtail)启动成功
AI identity: trae-main(基础设施修复会话)
141 lines
4.7 KiB
JavaScript
141 lines
4.7 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+ 逐步接入
|
||
*
|
||
* 维护者:ai13(teacher-portal)
|
||
* 关联:ARB-002 §2.2 暴露清单、03-long-term-architecture.md §1.3 绞杀者模式
|
||
*/
|
||
|
||
/** @type {import('next').NextConfig} */
|
||
|
||
// 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,
|
||
|
||
// 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 = nextConfig;
|