Files
AIclinicalresearch/backend/src/config/env.ts
HaHafeng 8bba33ac89 feat(platform): Implement platform infrastructure with cloud-native support
- Add storage service (LocalAdapter + OSSAdapter stub)
- Add database connection pool with graceful shutdown
- Add logging system with winston (JSON format)
- Add environment config management
- Add async job queue (MemoryQueue + DatabaseQueue stub)
- Add cache service (MemoryCache + RedisCache stub)
- Add health check endpoints for SAE
- Add monitoring metrics for DB, memory, API

Key Features:
- Zero-code switching between local and cloud environments
- Adapter pattern for multi-environment support
- Backward compatible with legacy modules
- Ready for Aliyun Serverless deployment

Related: Platform Infrastructure Planning (docs/09-鏋舵瀯瀹炴柦/04-骞冲彴鍩虹璁炬柦瑙勫垝.md)
2025-11-17 08:31:23 +08:00

240 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import dotenv from 'dotenv'
import path from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
/**
* 云原生环境配置管理
*
* 设计原则:
* - ✅ 本地开发:从.env文件加载
* - ✅ 云端部署从SAE环境变量加载
* - ✅ 统一配置管理,避免散落各处
* - ✅ 启动时验证必需配置
*
* 环境变量优先级:
* 1. 系统环境变量(最高优先级,云端部署)
* 2. .env文件本地开发
* 3. 默认值(兜底)
*/
// 只在非生产环境加载.env文件
if (process.env.NODE_ENV !== 'production') {
dotenv.config({ path: path.join(__dirname, '../../.env') })
console.log('[Config] Loaded .env file for development')
}
export const config = {
// ==================== 应用配置 ====================
/** 服务端口 */
port: parseInt(process.env.PORT || '3001', 10),
/** 服务主机 */
host: process.env.HOST || '0.0.0.0',
/** 运行环境 */
nodeEnv: process.env.NODE_ENV || 'development',
/** 日志级别 */
logLevel: process.env.LOG_LEVEL || (process.env.NODE_ENV === 'production' ? 'info' : 'debug'),
/** 服务名称 */
serviceName: process.env.SERVICE_NAME || 'aiclinical-backend',
// ==================== 数据库配置 ====================
/** 数据库连接URL */
databaseUrl: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/ai_clinical',
/** RDS最大连接数云原生配置 */
dbMaxConnections: parseInt(process.env.DB_MAX_CONNECTIONS || '400', 10),
/** SAE最大实例数云原生配置 */
maxInstances: parseInt(process.env.MAX_INSTANCES || '20', 10),
// ==================== 存储配置(平台基础设施)====================
/** 存储类型local | oss */
storageType: process.env.STORAGE_TYPE || 'local',
/** 本地存储目录 */
localStorageDir: process.env.LOCAL_STORAGE_DIR || 'uploads',
/** 本地存储URL前缀 */
localStorageUrl: process.env.LOCAL_STORAGE_URL || 'http://localhost:3001/uploads',
/** 阿里云OSS地域 */
ossRegion: process.env.OSS_REGION,
/** 阿里云OSS Bucket名称 */
ossBucket: process.env.OSS_BUCKET,
/** 阿里云OSS AccessKey ID */
ossAccessKeyId: process.env.OSS_ACCESS_KEY_ID,
/** 阿里云OSS AccessKey Secret */
ossAccessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
// ==================== 缓存配置(平台基础设施)====================
/** 缓存类型memory | redis */
cacheType: process.env.CACHE_TYPE || 'memory',
/** Redis主机 */
redisHost: process.env.REDIS_HOST,
/** Redis端口 */
redisPort: parseInt(process.env.REDIS_PORT || '6379', 10),
/** Redis密码 */
redisPassword: process.env.REDIS_PASSWORD,
/** Redis数据库索引 */
redisDb: parseInt(process.env.REDIS_DB || '0', 10),
/** Redis URL兼容旧配置 */
redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',
// ==================== 任务队列配置(平台基础设施)====================
/** 任务队列类型memory | database */
queueType: process.env.QUEUE_TYPE || 'memory',
// ==================== 安全配置 ====================
/** JWT密钥 */
jwtSecret: process.env.JWT_SECRET || 'your-secret-key-change-in-production',
/** JWT过期时间 */
jwtExpiresIn: process.env.JWT_EXPIRES_IN || '7d',
/** CORS允许的源 */
corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:5173',
// ==================== LLM API配置 ====================
/** DeepSeek API Key */
deepseekApiKey: process.env.DEEPSEEK_API_KEY || '',
/** DeepSeek Base URL */
deepseekBaseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',
/** 通义千问 API Key */
dashscopeApiKey: process.env.DASHSCOPE_API_KEY || '',
/** Gemini API Key */
geminiApiKey: process.env.GEMINI_API_KEY || '',
/** CloseAI API Key代理OpenAI和Claude */
closeaiApiKey: process.env.CLOSEAI_API_KEY || '',
/** CloseAI OpenAI Base URL */
closeaiOpenaiBaseUrl: process.env.CLOSEAI_OPENAI_BASE_URL || 'https://api.openai-proxy.org/v1',
/** CloseAI Claude Base URL */
closeaiClaudeBaseUrl: process.env.CLOSEAI_CLAUDE_BASE_URL || 'https://api.openai-proxy.org/anthropic',
// ==================== Dify配置 ====================
/** Dify API Key */
difyApiKey: process.env.DIFY_API_KEY || '',
/** Dify API URL */
difyApiUrl: process.env.DIFY_API_URL || 'http://localhost/v1',
// ==================== 文件上传配置Legacy兼容====================
/** 文件上传大小限制 */
uploadMaxSize: parseInt(process.env.UPLOAD_MAX_SIZE || '10485760', 10), // 10MB
/** 文件上传目录Legacy兼容新模块使用storage */
uploadDir: process.env.UPLOAD_DIR || './uploads',
// ==================== 功能开关(平台基础设施)====================
/** 启用的模块列表(逗号分隔) */
enabledModules: process.env.ENABLED_MODULES?.split(',').map(m => m.trim()) || [],
}
/**
* 验证必需的环境变量
*
* 在应用启动时调用,确保关键配置存在
*/
export function validateEnv(): void {
const errors: string[] = []
const warnings: string[] = []
// ========== 必需配置验证 ==========
if (!process.env.DATABASE_URL) {
errors.push('DATABASE_URL is required')
}
// ========== 云原生配置验证 ==========
// 如果使用OSS验证OSS配置
if (config.storageType === 'oss') {
if (!config.ossRegion) errors.push('OSS_REGION is required when STORAGE_TYPE=oss')
if (!config.ossBucket) errors.push('OSS_BUCKET is required when STORAGE_TYPE=oss')
if (!config.ossAccessKeyId) errors.push('OSS_ACCESS_KEY_ID is required when STORAGE_TYPE=oss')
if (!config.ossAccessKeySecret) errors.push('OSS_ACCESS_KEY_SECRET is required when STORAGE_TYPE=oss')
}
// 如果使用Redis验证Redis配置
if (config.cacheType === 'redis') {
if (!config.redisHost && !config.redisUrl) {
warnings.push('REDIS_HOST or REDIS_URL should be set when CACHE_TYPE=redis')
}
}
// ========== 安全配置验证 ==========
if (config.nodeEnv === 'production') {
if (config.jwtSecret === 'your-secret-key-change-in-production') {
errors.push('JWT_SECRET must be changed in production')
}
}
// ========== LLM配置验证 ==========
if (!config.deepseekApiKey && !config.dashscopeApiKey && !config.closeaiApiKey) {
warnings.push(
'No LLM API keys configured. At least one of DEEPSEEK_API_KEY, DASHSCOPE_API_KEY, or CLOSEAI_API_KEY should be set.'
)
}
// ========== 输出验证结果 ==========
if (errors.length > 0) {
console.error('❌ [Config] Environment validation failed:')
errors.forEach(err => console.error(` - ${err}`))
throw new Error('Environment validation failed. Please check configuration.')
}
if (warnings.length > 0) {
console.warn('⚠️ [Config] Environment validation warnings:')
warnings.forEach(warn => console.warn(` - ${warn}`))
}
// 成功
if (errors.length === 0 && warnings.length === 0) {
console.log('✅ [Config] Environment validation passed')
}
// 输出关键配置(脱敏)
console.log('[Config] Application configuration:')
console.log(` - Environment: ${config.nodeEnv}`)
console.log(` - Port: ${config.port}`)
console.log(` - Storage: ${config.storageType}`)
console.log(` - Cache: ${config.cacheType}`)
console.log(` - Queue: ${config.queueType}`)
console.log(` - Log Level: ${config.logLevel}`)
if (config.enabledModules.length > 0) {
console.log(` - Enabled Modules: ${config.enabledModules.join(', ')}`)
}
}