feat(config-service): split config-service from iam for plugin/layout config

- new NestJS service on port 3011/gRPC 50059 (ADR-026)
- owns 6 config_ tables (plugin/role-mapping/role-layout/layout-tpl/user-override/outbox)
- GraphQL Federation 2 subgraph with DataLoader + RouterAuthGuard
- gRPC ConfigService + admin REST CRUD + user REST API
- three-layer merge: registry.defaultProps + roleMapping.widget_props + userOverride.props
- Redis cache with 5min TTL
- registered in apollo-router supergraph + docker-compose + port-allocation

Implements M3 of v2.1 migration plan.
This commit is contained in:
SpecialX
2026-07-15 02:13:03 +08:00
parent 163bff6666
commit 1a5fa78fa6
44 changed files with 3538 additions and 33 deletions

View File

@@ -0,0 +1,69 @@
import {
Injectable,
Logger,
OnApplicationShutdown,
OnModuleInit,
} from "@nestjs/common";
import { closeDb } from "../../config/database.js";
import { closeRedis } from "../../config/redis.js";
import { disconnectKafkaProducer } from "../../config/kafka.js";
const SERVICE_NAME = "config-service";
/**
* 优雅停机服务。
*
* 关闭顺序:
* 1. HTTP/gRPC server 已由 NestJS app.close() 停止
* 2. Kafka producer 断开
* 3. Redis 断开
* 4. DB 连接池关闭(最后关闭)
*
* v2.1 ADR-032outbox 投递由 Debezium CDC 接管,本服务不持有 OutboxPublisher。
*/
@Injectable()
export class LifecycleService implements OnModuleInit, OnApplicationShutdown {
private readonly logger = new Logger(LifecycleService.name);
onModuleInit(): void {
this.logger.log(`service ${SERVICE_NAME} module initialized`);
}
async onApplicationShutdown(signal?: string): Promise<void> {
this.logger.log(
`service ${SERVICE_NAME} shutting down (signal=${signal ?? "unknown"})`,
);
// 1. Kafka producer
try {
await disconnectKafkaProducer();
this.logger.log("Kafka producer disconnected");
} catch (error) {
this.logger.error(
`Kafka producer disconnect failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
// 2. Redis
try {
await closeRedis();
this.logger.log("Redis connection closed");
} catch (error) {
this.logger.error(
`Redis close failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
// 3. DB最后关闭
try {
await closeDb();
this.logger.log("database connection closed");
} catch (error) {
this.logger.error(
`database close failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
this.logger.log(`service ${SERVICE_NAME} shutdown complete`);
}
}