database.ts 导出 db 常量替代 getDb() env.ts JWT_SECRET 改 optional 并新增 DEV_MODE kafka.ts connectKafka 加 try/catch 不阻塞启动 main.ts 去全局前缀 connectKafka 改非阻塞 app.module 移除未用模块加 HealthModule controller 路由去前缀去 UseGuards 从 x-user-id 读身份 service datetime ISO 字符串转 Date 修复 drizzle 错误 修正相对 import 路径 health/lifecycle 改用 Drizzle 原生查询 新增 core-edu-init.sql 初始化 4 张表 端到端验证: exams/homework/grades 全部 201/200 Outbox 事件正确写入 core_edu_outbox 表
52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import { NestFactory } from "@nestjs/core";
|
|
import { AppModule } from "./app.module.js";
|
|
import { env } from "./config/env.js";
|
|
import { connectKafka, disconnectKafka } from "./config/kafka.js";
|
|
import { outboxPublisher } from "./shared/outbox/outbox.publisher.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";
|
|
|
|
async function bootstrap(): Promise<void> {
|
|
initTracer();
|
|
|
|
const app = await NestFactory.create(AppModule, { bufferLogs: true });
|
|
app.useGlobalFilters(new GlobalErrorFilter());
|
|
app.enableShutdownHooks();
|
|
|
|
// Connect Kafka producer/consumer before starting the outbox publisher.
|
|
// Non-blocking: if Kafka is unavailable, service still starts; outbox
|
|
// publisher will retry sends and messages stay pending until Kafka recovers.
|
|
void connectKafka();
|
|
|
|
// Start the transactional outbox publisher - polls pending messages
|
|
// and publishes them to Kafka topics defined in TOPIC_MAP.
|
|
await outboxPublisher.start();
|
|
|
|
await app.listen(env.PORT);
|
|
logger.info(
|
|
{ port: env.PORT, service: "core-edu" },
|
|
"CoreEdu service is listening",
|
|
);
|
|
|
|
process.on("SIGTERM", async () => {
|
|
logger.info("SIGTERM received, shutting down gracefully...");
|
|
await outboxPublisher.stop();
|
|
await disconnectKafka();
|
|
await shutdownTracer();
|
|
await app.close();
|
|
process.exit(0);
|
|
});
|
|
|
|
process.on("SIGINT", async () => {
|
|
logger.info("SIGINT received, shutting down gracefully...");
|
|
await outboxPublisher.stop();
|
|
await disconnectKafka();
|
|
await shutdownTracer();
|
|
await app.close();
|
|
process.exit(0);
|
|
});
|
|
}
|
|
|
|
void bootstrap();
|