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.9:TEACHER_BFF_DEV_MODE 控制越权防御 Guard(P2 放行 / 生产拒绝) const envSchema = z.object({ 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.9:P2 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; export function loadEnv(): Env { const result = envSchema.safeParse(process.env); if (!result.success) { throw new Error("Invalid env: " + JSON.stringify(result.error.flatten())); } return result.data; } export const env = loadEnv();