feat(teacher-bff): 完整实现 teacher-bff GraphQL 聚合层

包含 clients/graphql/middleware、health probes、shared-ts contracts 等
This commit is contained in:
SpecialX
2026-07-10 19:10:07 +08:00
parent b82593aac2
commit 99155a5ea1
37 changed files with 2862 additions and 324 deletions

View File

@@ -1,36 +1,62 @@
// teacher-bff 入口B1 裁决P2 起 GraphQL Yoga
// 优雅关闭顺序G9HTTP → Redis → gRPC → Tracer
import "reflect-metadata";
import { NestFactory } from "@nestjs/core";
import type { NestExpressApplication } from "@nestjs/platform-express";
import type { Application, Request, Response } from "express";
import { AppModule } from "./app.module.js";
import { GlobalErrorFilter } from "./shared/errors/global-error.filter.js";
import { initTracer, shutdownTracer } from "./shared/observability/tracer.js";
import { logger } from "./shared/observability/logger.js";
import { env } from "./config/env.js";
import { metricsRegistry } from "./shared/observability/metrics.js";
import type { Request, Response } from "express";
import { closeAllGrpcClients } from "./clients/grpc/grpc.factory.js";
async function bootstrap(): Promise<void> {
initTracer();
const app = await NestFactory.create(AppModule, {
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
logger: ["log", "error", "warn"],
});
app.useGlobalFilters(new GlobalErrorFilter());
app.enableShutdownHooks();
// Prometheus 指标端点:不鉴权,供 Prometheus 抓取
app.getHttpAdapter().get("/metrics", async (_req: Request, res: Response) => {
// Prometheus 指标端点:不鉴权,供 Prometheus 抓取
// 直接获取 Express 实例注册路由,避免 HttpAdapter 泛型类型不兼容
const expressApp = app.getHttpAdapter().getInstance() as Application;
expressApp.get("/metrics", async (_req: Request, res: Response) => {
res.set("Content-Type", metricsRegistry.contentType);
res.end(await metricsRegistry.metrics());
});
await app.listen(env.PORT);
logger.info({ port: env.PORT }, "Teacher BFF started");
logger.info(
{
port: env.PORT,
graphql: env.GRAPHQL_PATH,
devMode: env.TEACHER_BFF_DEV_MODE,
iamTarget: env.IAM_GRPC_TARGET,
},
"Teacher BFF started (GraphQL + gRPC downstream)",
);
process.on("SIGTERM", async () => {
await app.close();
await shutdownTracer();
});
// 优雅关闭G9HTTP → Redis → gRPC → Tracer
const shutdown = async (signal: string) => {
logger.info({ signal }, "Shutting down Teacher BFF");
try {
await app.close(); // 触发 OnModuleDestroyRedis quit
closeAllGrpcClients(); // 关闭所有 gRPC channel
await shutdownTracer();
logger.info("Teacher BFF shutdown complete");
} catch (err) {
logger.error({ err }, "Error during shutdown");
process.exit(1);
}
};
process.on("SIGTERM", () => void shutdown("SIGTERM"));
process.on("SIGINT", () => void shutdown("SIGINT"));
}
bootstrap().catch((err: unknown) => {