Files
Edu/apps/teacher-portal/next.config.js
SpecialX a6b0c54fba fix: 修复登录路由 + 端口冲突 + Prometheus 配置 + outbox 表结构 + 补充监控基础设施
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(基础设施修复会话)
2026-07-11 09:22:09 +08:00

141 lines
4.7 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.
/**
* 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=falseP3+ 逐步接入
*
* 维护者ai13teacher-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.3P2 Remote 数量 = 0NEXT_PUBLIC_MF_ENABLED=false 默认关
* P3 首个 Remotestudent-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. GraphQLProviderurql 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-002MF Shell 配置exposes + sharedP2 无 remotes
// 仅当 NEXT_PUBLIC_MF_ENABLED=true 时实例化 NextFederationPlugin
// nextjs-mf 不支持 App DirectoryP2 阶段 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;