Files
Edu/services/teacher-bff/src/main.ts
SpecialX 99155a5ea1 feat(teacher-bff): 完整实现 teacher-bff GraphQL 聚合层
包含 clients/graphql/middleware、health probes、shared-ts contracts 等
2026-07-10 19:10:07 +08:00

66 lines
2.4 KiB
TypeScript
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-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 { closeAllGrpcClients } from "./clients/grpc/grpc.factory.js";
async function bootstrap(): Promise<void> {
initTracer();
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
logger: ["log", "error", "warn"],
});
app.useGlobalFilters(new GlobalErrorFilter());
app.enableShutdownHooks();
// 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,
graphql: env.GRAPHQL_PATH,
devMode: env.TEACHER_BFF_DEV_MODE,
iamTarget: env.IAM_GRPC_TARGET,
},
"Teacher BFF started (GraphQL + gRPC downstream)",
);
// 优雅关闭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) => {
logger.error({ err }, "Failed to start Teacher BFF");
process.exit(1);
});