All files / config index.ts

0% Statements 0/95
0% Branches 0/1
0% Functions 0/1
0% Lines 0/95

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129                                                                                                                                                                                                                                                                 
import dotenv from 'dotenv';
import path from 'path';
import fs from 'fs';
 
// 使用 process.cwd() 获取项目根目录(server目录)
const projectRoot = process.cwd();
dotenv.config({ path: path.resolve(projectRoot, '.env') });
 
// 加载模型配置
const modelsConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'models.json'), 'utf-8'));
 
// 获取所有模型(扁平化)
function getAllModels() {
  const models: any[] = [];
  for (const [vendorKey, vendor] of Object.entries(modelsConfig.vendors) as [string, any][]) {
    for (const model of vendor.models) {
      models.push({
        ...model,
        vendor: vendorKey,
        vendorName: vendor.name,
        baseUrl: vendor.baseUrl,
        apiKey: vendor.apiKey,
        apiType: vendor.apiType,
      });
    }
  }
  return models;
}
 
// 获取启用的模型
function getEnabledModels() {
  return getAllModels().filter((m: any) => m.enabled);
}
 
// 根据 ID 获取模型配置
function getModel(id: string) {
  return getAllModels().find((m: any) => m.id === id);
}
 
// 根据类型获取模型列表 (text, tts, image, video)
function getModelsByType(type: string) {
  return getAllModels().filter((m: any) => m.input?.includes(type) && m.enabled);
}
 
// 检查模型是否可切换(根据错误类型判断是否需要切换)
function shouldSwitchModel(error: any): boolean {
  if (!error) return false;
  const message = (error?.message || error?.error?.message || '').toLowerCase();
  const status = error?.status || error?.response?.status || 0;
 
  // 不可切换的错误:认证/权限/参数问题,换供应商也没用
  const nonSwitchablePatterns = [
    'invalid api key', 'invalid api-key', 'authentication', 'unauthorized',
    'invalid token', 'token expired',
    'permission denied', 'access denied',
    'invalid request', 'bad request',
    'invalidparameter', 'invalid_parameter',  // TTS API 参数错误(如文本过短)
  ];
  if (nonSwitchablePatterns.some(p => message.includes(p))) return false;
  if (status === 401) return false; // 认证失败
 
  // 可切换的错误:限流、余额不足、服务不可用、模型不存在
  const switchablePatterns = [
    'rate limit', 'rate_limit', 'too many requests', '请求过于频繁',
    'quota', 'balance', 'insufficient', 'usage limit',
    'model not found', 'model not support', 'does not exist', 'invalid model',
    'service unavailable', 'bad gateway', 'gateway timeout',
    'internal server error',
  ];
  if (switchablePatterns.some(p => message.includes(p))) return true;
 
  // HTTP 状态码判断
  if ([429, 502, 503, 504, 500].includes(status)) return true;
  if (status === 403) return true;  // 403 多数是配额/权限,换Key可能有效
  if (status === 404) return true;  // 模型不存在,换供应商
 
  // 状态码文本匹配(兜底)
  if (['429', '502', '503', '504'].some(c => message.includes(c))) return true;
 
  return false;
}
 
// 获取下一个可用模型(用于自动切换)
function getNextModel(currentModelId: string, type: string): string | null {
  const models = getModelsByType(type);
  const currentIndex = models.findIndex((m: any) => m.id === currentModelId);
  if (currentIndex === -1 || currentIndex >= models.length - 1) {
    return null; // 没有下一个模型
  }
  return models[currentIndex + 1].id;
}
 
export const config = {
  port: parseInt(process.env.PORT || process.env.SERVER_PORT || '3000', 10),
  nodeEnv: process.env.NODE_ENV || 'development',
 
  mongodb: {
    uri: process.env.MONGODB_URI || 'mongodb://localhost:27017/audio-book',
  },
 
  jwt: {
    // 临时硬编码,确保登录和验证一致
    secret: 'my-jwt-secret-key-2024',
    expiresIn: process.env.JWT_EXPIRES_IN || '7d',
  },
 
  // 模型配置(统一管理,所有 TTS/LLM 配置均从 models.json 读取)
  models: {
    vendors: modelsConfig.vendors,
    list: getAllModels(),
    enabled: getEnabledModels(),
    getModel,
    getModelsByType,
    shouldSwitchModel,
    getNextModel,
    textGeneration: {
      defaultModel: modelsConfig.textGeneration.defaultModel,
    },
    tts: {
      defaultModel: modelsConfig.tts.defaultModel,
      defaultVoice: modelsConfig.tts.defaultVoice,
    },
  },
 
  upload: {
    dir: path.join(process.cwd(), 'uploads'),
    maxSize: 50 * 1024 * 1024, // 50MB
  },
};