50 lines
1.8 KiB
TypeScript
50 lines
1.8 KiB
TypeScript
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<typeof envSchema>;
|
||
|
||
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();
|