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,26 +1,45 @@
import { z } from "zod";
// 环境变量定义B2 裁决gRPC 下游通信B8 裁决DownstreamClient 抽象)
// 端口分配见 docs/architecture/port-allocation.md §5
// iam 50052 / core-edu 50053 / content 50054 / data-ana 50055 / msg 50056 / ai 50058
// president §2.9TEACHER_BFF_DEV_MODE 控制越权防御 GuardP2 放行 / 生产拒绝)
const envSchema = z.object({
PORT: z.string().default("3003"),
IamServiceUrl: z.string().url().default("http://localhost:3002"),
ClassesServiceUrl: z.string().url().default("http://localhost:3001"),
CoreEduServiceUrl: z.string().url().default("http://localhost:3004"),
OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url().optional(),
LOG_LEVEL: z.string().default("info"),
NODE_ENV: z.string().default("development"),
PORT: z.string().default("3003"),
LOG_LEVEL: z.string().default("info"),
// GraphQL endpoint 路径B1 裁决P2 起直接 GraphQL
GRAPHQL_PATH: z.string().default("/graphql"),
// 越权防御开关president §2.9P2 DEV_MODE 放行 + 日志告警,生产拒绝)
// 生产环境必须显式设为 false
TEACHER_BFF_DEV_MODE: z
.string()
.default("true")
.transform((v) => v === "true")
.pipe(z.boolean()),
// Redis/readyz 探针 + 后续 DataLoader 缓存)
REDIS_URL: z.string().default("redis://localhost:6379"),
// 下游 gRPC targets按阶段启用未启用者可留空字符串
IAM_GRPC_TARGET: z.string().default("localhost:50052"),
CORE_EDU_GRPC_TARGET: z.string().default(""),
CONTENT_GRPC_TARGET: z.string().default(""),
DATA_ANA_GRPC_TARGET: z.string().default(""),
MSG_GRPC_TARGET: z.string().default(""),
AI_GRPC_TARGET: z.string().default(""),
// 可观测性OTEL_EXPORTER_OTLP_ENDPOINT 缺省则不启用 tracer
OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url().optional(),
OTEL_SERVICE_NAME: z.string().default("teacher-bff"),
});
export type Env = z.infer<typeof envSchema>;
export function loadEnv(): Env {
const result = envSchema.safeParse({
...process.env,
IamServiceUrl: process.env.IAM_SERVICE_URL || "http://localhost:3002",
ClassesServiceUrl:
process.env.CLASSES_SERVICE_URL || "http://localhost:3001",
CoreEduServiceUrl:
process.env.CORE_EDU_SERVICE_URL || "http://localhost:3004",
});
const result = envSchema.safeParse(process.env);
if (!result.success) {
throw new Error("Invalid env: " + JSON.stringify(result.error.flatten()));
}