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,57 @@
/**
* config-service GraphQL 子图模块v2.1 M3
*
* Apollo Federation 2 子图,提供 4 类 Entity 查询:
* - PluginRegistry@key(fields: "pluginId")
* - RolePluginMapping
* - LayoutTemplate
* - UserLayoutOverride@key(fields: "userId")
*
* 强制约束:
* - @key 解析器必须使用 DataLoaderADR-035
* - RouterAuthGuard 校验 Router-Authorization HeaderADR-036
*/
import { Module } from "@nestjs/common";
import { GraphQLModule } from "@nestjs/graphql";
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
import { join } from "node:path";
import { GraphqlContext } from "@edu/shared-ts/federation";
import { ConfigModule } from "../config-config/config.module.js";
import { PluginResolver } from "./resolvers/plugin.resolver.js";
import { LayoutTemplateResolver } from "./resolvers/layout-template.resolver.js";
import { UserLayoutResolver } from "./resolvers/user-layout.resolver.js";
import { DataLoaderService } from "./dataloader.service.js";
@Module({
imports: [
ConfigModule,
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
// Federation 2 子图
autoSchemaFile: {
path: join(process.cwd(), "src/graphql/generated/schema.graphql"),
federation: 2,
},
// /graphql 端点Apollo Router 访问入口)
path: "/graphql",
// 禁用 playground生产环境通过 Router 访问)
playground: process.env.NODE_ENV === "development",
introspection: process.env.NODE_ENV === "development",
// Context 从 HTTP headers 构造
context: (ctx: {
req: { headers: Record<string, string | undefined> };
}) => ({
req: ctx.req,
graphqlContext: GraphqlContext.fromHeaders(ctx.req.headers),
}),
}),
],
providers: [
PluginResolver,
LayoutTemplateResolver,
UserLayoutResolver,
DataLoaderService,
],
exports: [DataLoaderService],
})
export class GraphqlModule {}