// server/utils/env.ts - 服务端环境变量读取与规范化 /** * 允许 `.env` 里保留类似 Python 旧项目的写法: * `OPENAI_API_BASE=https://example.com/v1 # 注释` * Nuxt/Node 读到的是整行值,所以这里主动裁掉行尾注释 */ const stripInlineComment = (value: string) => { return value.replace(/\s+#.*$/u, "").trim(); }; /** * 读取字符串环境变量 * * - 空字符串视为未配置,回退到 fallback * - 返回值统一去掉行尾注释,避免 baseURL、model 之类配置带上注释文本 */ const readString = (key: string, fallback = "") => { const value = process.env[key]; if (value === undefined || value === null || value.trim() === "") { return fallback; } return stripInlineComment(value); }; /** 读取可选字符串;空值统一转成 undefined,方便后续判断是否启用某能力 */ const readOptionalString = (key: string) => { const value = readString(key); return value || undefined; }; /** 兼容常见布尔写法,便于 Docker、面板和手写 `.env` 配置 */ const readBoolean = (key: string, fallback: boolean) => { const value = readString(key); if (!value) return fallback; return ["1", "true", "yes", "on"].includes(value.toLowerCase()); }; /** 读取整数配置;非法值不抛错,使用默认值保证服务能启动 */ const readInteger = (key: string, fallback: number) => { const value = Number.parseInt(readString(key), 10); return Number.isFinite(value) ? value : fallback; }; /** 读取小数配置,主要用于 OpenAI temperature */ const readNumber = (key: string, fallback: number) => { const value = Number.parseFloat(readString(key)); return Number.isFinite(value) ? value : fallback; }; /** OpenAI 兼容接口 baseURL 统一不带结尾斜杠,后续再拼 `/chat/completions` */ const normalizeBaseUrl = (url: string) => { return url.replace(/\/+$/u, ""); }; /** * 服务端环境配置的唯一出口 * * 业务代码不要直接读 `process.env`,这样可以把默认值、格式修正和安全边界 * 都收敛在这里,后续迁移部署平台时也只需要检查这个文件 */ export const serverEnv = { /** OpenAI 或兼容中转平台 API Key,仅服务端使用,不能返回给前端 */ openAiApiKey: readString("OPENAI_API_KEY"), /** OpenAI 兼容 API base,例如 `https://api.openai.com/v1` */ openAiApiBase: normalizeBaseUrl( readString("OPENAI_API_BASE", "https://api.openai.com/v1") ), /** Chat Completions 使用的模型名 */ openAiModel: readString("OPENAI_MODEL", "gpt-3.5-turbo"), /** 单次回答最大 token 数,沿用旧 Python 服务的配置语义 */ maxTokens: readInteger("MAX_TOKENS", 500), /** 模型采样温度,越低越稳定 */ temperature: readNumber("TEMPERATURE", 0.7), /** 是否启用内存缓存 */ enableCache: readBoolean("ENABLE_CACHE", true), /** 缓存过期时间,单位秒 */ cacheExpiration: readInteger("CACHE_EXPIRATION", 86_400), /** 预留日志级别配置,目前日志工具只负责安全输出 */ logLevel: readString("LOG_LEVEL", "INFO"), /** 单行 SSE data 的最大字节数,用于防止异常流无限堆内存 */ maxSseLineBytes: readInteger("OPENAI_STREAM_MAX_SSE_LINE_BYTES", 1_048_576) };