CoolFace
Apppublic

Maple7F/build-server

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
unified-server.js1786 linesDownload Raw Back to root
1const express = require('express');
2const WebSocket = require('ws');
3const http = require('http');
4const { EventEmitter } = require('events');
5const fs = require('fs');
6const path = require('path');
7const { firefox } = require('playwright');
8const os = require('os');
9
10
11// ===================================================================================
12// 认证源管理模块 (已升级以支持动态管理)
13// ===================================================================================
14
15class AuthSource {
16  constructor(logger) {
17    this.logger = logger;
18    this.authMode = 'file'; // 默认模式
19    this.initialIndices = []; // 启动时发现的索引
20    this.runtimeAuths = new Map(); // 用于动态添加的账号
21
22    if (process.env.AUTH_JSON_1) {
23      this.authMode = 'env';
24      this.logger.info('[认证] 检测到 AUTH_JSON_1 环境变量,切换到环境变量认证模式。');
25    } else {
26      this.logger.info('[认证] 未检测到环境变量认证,将使用 "auth/" 目录下的文件。');
27    }
28
29    this._discoverAvailableIndices();
30
31    if (this.getAvailableIndices().length === 0) {
32      this.logger.error(`[认证] 致命错误:在 '${this.authMode}' 模式下未找到任何有效的认证源。`);
33      throw new Error("未找到有效的认证源。");
34    }
35  }
36
37  _discoverAvailableIndices() {
38    let indices = [];
39    if (this.authMode === 'env') {
40      const regex = /^AUTH_JSON_(\d+)$/;
41      for (const key in process.env) {
42        const match = key.match(regex);
43        // 修正:正确解析捕获组 (match[1]) 而不是整个匹配对象
44        if (match && match[1]) {
45          indices.push(parseInt(match[1], 10));
46        }
47      }
48    } else { // 'file' 模式
49      const authDir = path.join(__dirname, 'auth');
50      if (!fs.existsSync(authDir)) {
51        this.logger.warn('[认证] "auth/" 目录不存在。');
52        this.initialIndices = [];
53        return;
54      }
55      try {
56        const files = fs.readdirSync(authDir);
57        const authFiles = files.filter(file => /^auth-\d+\.json$/.test(file));
58        // 修正:正确解析文件名中的捕获组 (match[1])
59        indices = authFiles.map(file => {
60            const match = file.match(/^auth-(\d+)\.json$/);
61            return parseInt(match[1], 10);
62        });
63      } catch (error) {
64        this.logger.error(`[认证] 扫描 "auth/" 目录失败: ${error.message}`);
65        this.initialIndices = [];
66        return;
67      }
68    }
69    this.initialIndices = [...new Set(indices)].sort((a, b) => a - b);
70    this.logger.info(`[认证] 在 '${this.authMode}' 模式下,检测到 ${this.initialIndices.length} 个认证源。`);
71    if (this.initialIndices.length > 0) {
72      this.logger.info(`[认证] 可用初始索引: [${this.initialIndices.join(', ')}]`);
73    }
74  }
75
76  getAvailableIndices() {
77    const runtimeIndices = Array.from(this.runtimeAuths.keys());
78    const allIndices = [...new Set([...this.initialIndices, ...runtimeIndices])].sort((a, b) => a - b);
79    return allIndices;
80  }
81  
82  // 新增方法:为仪表盘获取详细信息
83  getAccountDetails() {
84      const allIndices = this.getAvailableIndices();
85      return allIndices.map(index => ({
86          index,
87          source: this.runtimeAuths.has(index) ? 'temporary' : this.authMode
88      }));
89  }
90
91
92  getFirstAvailableIndex() {
93    const indices = this.getAvailableIndices();
94    return indices.length > 0 ? indices[0] : null;
95  }
96
97  getAuth(index) {
98    if (!this.getAvailableIndices().includes(index)) {
99      this.logger.error(`[认证] 请求了无效或不存在的认证索引: ${index}`);
100      return null;
101    }
102    
103    // 优先使用运行时(临时)的认证信息
104    if (this.runtimeAuths.has(index)) {
105        this.logger.info(`[认证] 使用索引 ${index} 的临时认证源。`);
106        return this.runtimeAuths.get(index);
107    }
108
109    let jsonString;
110    let sourceDescription;
111
112    if (this.authMode === 'env') {
113      jsonString = process.env[`AUTH_JSON_${index}`];
114      sourceDescription = `环境变量 AUTH_JSON_${index}`;
115    } else {
116      const authFilePath = path.join(__dirname, 'auth', `auth-${index}.json`);
117      sourceDescription = `文件 ${authFilePath}`;
118      if (!fs.existsSync(authFilePath)) {
119        this.logger.error(`[认证] ${sourceDescription} 在读取时突然消失。`);
120        return null;
121      }
122      try {
123        jsonString = fs.readFileSync(authFilePath, 'utf-8');
124      } catch (e) {
125        this.logger.error(`[认证] 读取 ${sourceDescription} 失败: ${e.message}`);
126        return null;
127      }
128    }
129
130    try {
131      return JSON.parse(jsonString);
132    } catch (e) {
133      this.logger.error(`[认证] 解析来自 ${sourceDescription} 的JSON内容失败: ${e.message}`);
134      return null;
135    }
136  }
137  
138  // 新增方法:动态添加账号
139  addAccount(index, authData) {
140      if (typeof index !== 'number' || index <= 0) {
141          return { success: false, message: "索引必须是一个正数。" };
142      }
143      if (this.initialIndices.includes(index)) {
144          return { success: false, message: `索引 ${index} 已作为永久账号存在。` };
145      }
146      try {
147          // 验证 authData 是否为有效的JSON对象
148          if(typeof authData !== 'object' || authData === null) {
149              throw new Error("提供的数据不是一个有效的对象。");
150          }
151          this.runtimeAuths.set(index, authData);
152          this.logger.info(`[认证] 成功添加索引为 ${index} 的临时账号。`);
153          return { success: true, message: `账号 ${index} 已临时添加。` };
154      } catch (e) {
155          this.logger.error(`[认证] 添加临时账号 ${index} 失败: ${e.message}`);
156          return { success: false, message: `添加账号失败: ${e.message}` };
157      }
158  }
159
160  // 新增方法:动态删除账号
161  removeAccount(index) {
162      if (!this.runtimeAuths.has(index)) {
163          return { success: false, message: `索引 ${index} 不是一个临时账号,无法移除。` };
164      }
165      this.runtimeAuths.delete(index);
166      this.logger.info(`[认证] 成功移除索引为 ${index} 的临时账号。`);
167      return { success: true, message: `账号 ${index} 已移除。` };
168  }
169}
170
171
172// ===================================================================================
173// 浏览器管理模块
174// ===================================================================================
175
176class BrowserManager {
177  constructor(logger, config, authSource) {
178    this.logger = logger;
179    this.config = config;
180    this.authSource = authSource;
181    this.browser = null;
182    this.context = null;
183    this.page = null;
184    this.currentAuthIndex = 0;
185    this.scriptFileName = 'dark-browser.js';
186
187    if (this.config.browserExecutablePath) {
188      this.browserExecutablePath = this.config.browserExecutablePath;
189      this.logger.info(`[系统] 使用环境变量 CAMOUFOX_EXECUTABLE_PATH 指定的浏览器路径。`);
190    } else {
191      const platform = os.platform();
192      if (platform === 'win32') {
193        this.browserExecutablePath = path.join(__dirname, 'camoufox', 'camoufox.exe');
194        this.logger.info(`[系统] 检测到操作系统: Windows. 将使用 'camoufox' 目录下的浏览器。`);
195      } else if (platform === 'linux') {
196        this.browserExecutablePath = path.join(__dirname, 'camoufox-linux', 'camoufox');
197        this.logger.info(`[系统] 检测到操作系统: Linux. 将使用 'camoufox-linux' 目录下的浏览器。`);
198      } else {
199        this.logger.error(`[系统] 不支持的操作系统: ${platform}.`);
200        throw new Error(`不支持的操作系统: ${platform}`);
201      }
202    }
203  }
204
205  async launchBrowser(authIndex) {
206    if (this.browser) {
207      this.logger.warn('尝试启动一个已在运行的浏览器实例,操作已取消。');
208      return;
209    }
210
211    const sourceDescription = this.authSource.authMode === 'env' ? `环境变量 AUTH_JSON_${authIndex}` : `文件 auth-${authIndex}.json`;
212    this.logger.info('==================================================');
213    this.logger.info(`🚀 [浏览器] 准备启动浏览器`);
214    this.logger.info(`   • 认证源: ${sourceDescription}`);
215    this.logger.info(`   • 浏览器路径: ${this.browserExecutablePath}`);
216    this.logger.info('==================================================');
217
218    if (!fs.existsSync(this.browserExecutablePath)) {
219      this.logger.error(`❌ [浏览器] 找不到浏览器可执行文件: ${this.browserExecutablePath}`);
220      throw new Error(`找不到浏览器可执行文件路径: ${this.browserExecutablePath}`);
221    }
222
223    const storageStateObject = this.authSource.getAuth(authIndex);
224    if (!storageStateObject) {
225      this.logger.error(`❌ [浏览器] 无法获取或解析索引为 ${authIndex} 的认证信息。`);
226      throw new Error(`获取或解析索引 ${authIndex} 的认证源失败。`);
227    }
228
229    if (storageStateObject.cookies && Array.isArray(storageStateObject.cookies)) {
230      let fixedCount = 0;
231      const validSameSiteValues = ['Lax', 'Strict', 'None'];
232      storageStateObject.cookies.forEach(cookie => {
233        if (!validSameSiteValues.includes(cookie.sameSite)) {
234          this.logger.warn(`[认证] 发现无效的 sameSite 值: '${cookie.sameSite}',正在自动修正为 'None'。`);
235          cookie.sameSite = 'None';
236          fixedCount++;
237        }
238      });
239      if (fixedCount > 0) {
240        this.logger.info(`[认证] 自动修正了 ${fixedCount} 个无效的 Cookie 'sameSite' 属性。`);
241      }
242    }
243
244    let buildScriptContent;
245    try {
246      const scriptFilePath = path.join(__dirname, this.scriptFileName);
247      if(fs.existsSync(scriptFilePath)){
248        buildScriptContent = fs.readFileSync(scriptFilePath, 'utf-8');
249        this.logger.info(`✅ [浏览器] 成功读取注入脚本 "${this.scriptFileName}"`);
250      } else {
251        this.logger.warn(`[浏览器] 未找到注入脚本 "${this.scriptFileName}"。将无注入继续运行。`);
252        buildScriptContent = "console.log('dark-browser.js not found, running without injection.');";
253      }
254    } catch (error) {
255      this.logger.error(`❌ [浏览器] 无法读取注入脚本 "${this.scriptFileName}"!`);
256      throw error;
257    }
258
259    try {
260      this.browser = await firefox.launch({
261        headless: true,
262        executablePath: this.browserExecutablePath,
263      });
264      this.browser.on('disconnected', () => {
265        this.logger.error('❌ [浏览器] 浏览器意外断开连接!服务器可能需要重启。');
266        this.browser = null; this.context = null; this.page = null;
267      });
268      this.context = await this.browser.newContext({
269        storageState: storageStateObject,
270        viewport: { width: 1280, height: 720 },
271      });
272      this.page = await this.context.newPage();
273      this.logger.info(`[浏览器] 正在加载账号 ${authIndex} 并访问目标网页...`);
274      const targetUrl = 'https://aistudio.google.com/u/0/apps/bundled/blank?showPreview=true&showCode=true&showAssistant=true';
275      await this.page.goto(targetUrl, { timeout: 120000, waitUntil: 'networkidle' });
276      this.logger.info('[浏览器] 网页加载完成,正在注入客户端脚本...');
277
278      const editorContainerLocator = this.page.locator('div.monaco-editor').first();
279
280      this.logger.info('[浏览器] 等待编辑器出现,最长120秒...');
281      await editorContainerLocator.waitFor({ state: 'visible', timeout: 120000 });
282      this.logger.info('[浏览器] 编辑器已出现,准备粘贴脚本。');
283      
284      this.logger.info('[浏览器] 等待5秒,之后将在页面下方执行一次模拟点击以确保页面激活...');
285      await this.page.waitForTimeout(5000);
286
287      const viewport = this.page.viewportSize();
288      if (viewport) {
289        const clickX = viewport.width / 2;
290        const clickY = viewport.height - 120;
291        this.logger.info(`[浏览器] 在页面底部中心位置 (x≈${Math.round(clickX)}, y=${clickY}) 执行点击。`);
292        await this.page.mouse.click(clickX, clickY);
293      } else {
294        this.logger.warn('[浏览器] 无法获取视窗大小,跳过页面底部模拟点击。');
295      }
296
297      await editorContainerLocator.click({ timeout: 120000 });
298      await this.page.evaluate(text => navigator.clipboard.writeText(text), buildScriptContent);
299      const isMac = os.platform() === 'darwin';
300      const pasteKey = isMac ? 'Meta+V' : 'Control+V';
301      await this.page.keyboard.press(pasteKey);
302      this.logger.info('[浏览器] 脚本已粘贴。浏览器端初始化完成。');
303
304
305      this.currentAuthIndex = authIndex;
306      this.logger.info('==================================================');
307      this.logger.info(`✅ [浏览器] 账号 ${authIndex} 初始化成功!`);
308      this.logger.info('✅ [浏览器] 浏览器客户端已准备就绪。');
309      this.logger.info('==================================================');
310    } catch (error) {
311      this.logger.error(`❌ [浏览器] 账号 ${authIndex} 初始化失败: ${error.message}`);
312      if (this.browser) {
313        await this.browser.close();
314        this.browser = null;
315      }
316      throw error;
317    }
318  }
319
320  async closeBrowser() {
321    if (this.browser) {
322      this.logger.info('[浏览器] 正在关闭当前浏览器实例...');
323      await this.browser.close();
324      this.browser = null; this.context = null; this.page = null;
325      this.logger.info('[浏览器] 浏览器已关闭。');
326    }
327  }
328
329  async switchAccount(newAuthIndex) {
330    this.logger.info(`🔄 [浏览器] 开始账号切换: 从 ${this.currentAuthIndex} 到 ${newAuthIndex}`);
331    await this.closeBrowser();
332    await this.launchBrowser(newAuthIndex);
333    this.logger.info(`✅ [浏览器] 账号切换完成,当前账号: ${this.currentAuthIndex}`);
334  }
335}
336
337// ===================================================================================
338// 代理服务模块
339// ===================================================================================
340
341class LoggingService {
342  constructor(serviceName = 'ProxyServer') {
343    this.serviceName = serviceName;
344  }
345
346  _getFormattedTime() {
347    // 使用 toLocaleTimeString 并指定 en-GB 区域来保证输出为 HH:mm:ss 格式
348    return new Date().toLocaleTimeString('en-GB', { hour12: false });
349  }
350
351  // 用于 ERROR, WARN, DEBUG 等带有级别标签的日志
352  _formatMessage(level, message) {
353    const time = this._getFormattedTime();
354    return `[${level}] ${time} [${this.serviceName}] - ${message}`;
355  }
356  
357  // info 级别使用特殊格式,不显示 [INFO]
358  info(message) { 
359    const time = this._getFormattedTime();
360    console.log(`${time} [${this.serviceName}] - ${message}`); 
361  }
362
363  error(message) { 
364    console.error(this._formatMessage('ERROR', message)); 
365  }
366
367  warn(message) { 
368    console.warn(this._formatMessage('WARN', message)); 
369  }
370  
371  debug(message) { 
372    if(process.env.DEBUG_MODE === 'true') {
373        console.debug(this._formatMessage('DEBUG', message));
374    }
375  }
376}
377
378class MessageQueue extends EventEmitter {
379  constructor(timeoutMs = 1200000) {
380    super();
381    this.messages = [];
382    this.waitingResolvers = [];
383    this.defaultTimeout = timeoutMs;
384    this.closed = false;
385  }
386  enqueue(message) {
387    if (this.closed) return;
388    if (this.waitingResolvers.length > 0) {
389      const resolver = this.waitingResolvers.shift();
390      resolver.resolve(message);
391    } else {
392      this.messages.push(message);
393    }
394  }
395  async dequeue(timeoutMs = this.defaultTimeout) {
396    if (this.closed) {
397      throw new Error('队列已关闭');
398    }
399    return new Promise((resolve, reject) => {
400      if (this.messages.length > 0) {
401        resolve(this.messages.shift());
402        return;
403      }
404      const resolver = { resolve, reject };
405      this.waitingResolvers.push(resolver);
406      const timeoutId = setTimeout(() => {
407        const index = this.waitingResolvers.indexOf(resolver);
408        if (index !== -1) {
409          this.waitingResolvers.splice(index, 1);
410          reject(new Error('队列超时'));
411        }
412      }, timeoutMs);
413      resolver.timeoutId = timeoutId;
414    });
415  }
416  close() {
417    this.closed = true;
418    this.waitingResolvers.forEach(resolver => {
419      clearTimeout(resolver.timeoutId);
420      resolver.reject(new Error('队列已关闭'));
421    });
422    this.waitingResolvers = [];
423    this.messages = [];
424  }
425}
426
427class ConnectionRegistry extends EventEmitter {
428  constructor(logger) {
429    super();
430    this.logger = logger;
431    this.connections = new Set();
432    this.messageQueues = new Map();
433  }
434  addConnection(websocket, clientInfo) {
435    this.connections.add(websocket);
436    this.logger.info(`[服务器] 内部WebSocket客户端已连接 (来自: ${clientInfo.address})`);
437    websocket.on('message', (data) => this._handleIncomingMessage(data.toString()));
438    websocket.on('close', () => this._removeConnection(websocket));
439    websocket.on('error', (error) => this.logger.error(`[服务器] 内部WebSocket连接错误: ${error.message}`));
440    this.emit('connectionAdded', websocket);
441  }
442  _removeConnection(websocket) {
443    this.connections.delete(websocket);
444    this.logger.warn('[服务器] 内部WebSocket客户端连接断开');
445    this.messageQueues.forEach(queue => queue.close());
446    this.messageQueues.clear();
447    this.emit('connectionRemoved', websocket);
448  }
449  _handleIncomingMessage(messageData) {
450    try {
451      const parsedMessage = JSON.parse(messageData);
452      const requestId = parsedMessage.request_id;
453      if (!requestId) {
454        this.logger.warn('[服务器] 收到无效消息:缺少request_id');
455        return;
456      }
457      const queue = this.messageQueues.get(requestId);
458      if (queue) {
459        this._routeMessage(parsedMessage, queue);
460      }
461    } catch (error) {
462      this.logger.error('[服务器] 解析内部WebSocket消息失败');
463    }
464  }
465  _routeMessage(message, queue) {
466    const { event_type } = message;
467    switch (event_type) {
468      case 'response_headers': case 'chunk': case 'error':
469        queue.enqueue(message);
470        break;
471      case 'stream_close':
472        queue.enqueue({ type: 'STREAM_END' });
473        break;
474      default:
475        this.logger.warn(`[服务器] 未知的内部事件类型: ${event_type}`);
476    }
477  }
478  hasActiveConnections() { return this.connections.size > 0; }
479  getFirstConnection() { return this.connections.values().next().value; }
480  createMessageQueue(requestId) {
481    const queue = new MessageQueue();
482    this.messageQueues.set(requestId, queue);
483    return queue;
484  }
485  removeMessageQueue(requestId) {
486    const queue = this.messageQueues.get(requestId);
487    if (queue) {
488      queue.close();
489      this.messageQueues.delete(requestId);
490    }
491  }
492}
493
494class RequestHandler {
495  constructor(serverSystem, connectionRegistry, logger, browserManager, config, authSource) {
496    this.serverSystem = serverSystem;
497    this.connectionRegistry = connectionRegistry;
498    this.logger = logger;
499    this.browserManager = browserManager;
500    this.config = config;
501    this.authSource = authSource;
502    this.maxRetries = this.config.maxRetries;
503    this.retryDelay = this.config.retryDelay;
504    this.failureCount = 0;
505    this.isAuthSwitching = false;
506  }
507
508  get currentAuthIndex() {
509    return this.browserManager.currentAuthIndex;
510  }
511
512  _getNextAuthIndex() {
513    const available = this.authSource.getAvailableIndices();
514    if (available.length === 0) return null;
515    if (available.length === 1) return available[0];
516
517    const currentIndexInArray = available.indexOf(this.currentAuthIndex);
518
519    if (currentIndexInArray === -1) {
520      this.logger.warn(`[认证] 当前索引 ${this.currentAuthIndex} 不在可用列表中,将切换到第一个可用索引。`);
521      return available[0];
522    }
523
524    const nextIndexInArray = (currentIndexInArray + 1) % available.length;
525    return available[nextIndexInArray];
526  }
527
528  async _switchToNextAuth() {
529    if (this.isAuthSwitching) {
530      this.logger.info('🔄 [认证] 正在切换账号,跳过重复切换');
531      return;
532    }
533
534    this.isAuthSwitching = true;
535    const nextAuthIndex = this._getNextAuthIndex();
536    const totalAuthCount = this.authSource.getAvailableIndices().length;
537
538    if (nextAuthIndex === null) {
539      this.logger.error('🔴 [认证] 无法切换账号,因为没有可用的认证源!');
540      this.isAuthSwitching = false;
541      throw new Error('没有可用的认证源可以切换。');
542    }
543
544    this.logger.info('==================================================');
545    this.logger.info(`🔄 [认证] 开始账号切换流程`);
546    this.logger.info(`   • 失败次数: ${this.failureCount}/${this.config.failureThreshold > 0 ? this.config.failureThreshold : 'N/A'}`);
547    this.logger.info(`   • 当前账号索引: ${this.currentAuthIndex}`);
548    this.logger.info(`   • 目标账号索引: ${nextAuthIndex}`);
549    this.logger.info(`   • 可用账号总数: ${totalAuthCount}`);
550    this.logger.info('==================================================');
551
552    try {
553      await this.browserManager.switchAccount(nextAuthIndex);
554      this.failureCount = 0;
555      this.logger.info('==================================================');
556      this.logger.info(`✅ [认证] 成功切换到账号索引 ${this.currentAuthIndex}`);
557      this.logger.info(`✅ [认证] 失败计数已重置为0`);
558      this.logger.info('==================================================');
559    } catch (error) {
560      this.logger.error('==================================================');
561      this.logger.error(`❌ [认证] 切换账号失败: ${error.message}`);
562      this.logger.error('==================================================');
563      throw error;
564    } finally {
565      this.isAuthSwitching = false;
566    }
567  }
568
569  _parseAndCorrectErrorDetails(errorDetails) {
570    const correctedDetails = { ...errorDetails };
571    this.logger.debug(`[错误解析器] 原始错误详情: status=${correctedDetails.status}, message="${correctedDetails.message}"`);
572
573    if (correctedDetails.message && typeof correctedDetails.message === 'string') {
574      const regex = /(?:HTTP|status code)\s+(\d{3})/;
575      const match = correctedDetails.message.match(regex);
576
577      if (match && match[1]) {
578        const parsedStatus = parseInt(match[1], 10);
579        if (parsedStatus >= 400 && parsedStatus <= 599) {
580          if (correctedDetails.status !== parsedStatus) {
581            this.logger.warn(`[错误解析器] 修正了错误状态码!原始: ${correctedDetails.status}, 从消息中解析得到: ${parsedStatus}`);
582            correctedDetails.status = parsedStatus;
583          } else {
584            this.logger.debug(`[错误解析器] 解析的状态码 (${parsedStatus}) 与原始状态码一致,无需修正。`);
585          }
586        }
587      }
588    }
589    return correctedDetails;
590  }
591
592  async _handleRequestFailureAndSwitch(errorDetails, res) {
593    const correctedDetails = { ...errorDetails };
594    if (correctedDetails.message && typeof correctedDetails.message === 'string') {
595      const regex = /(?:HTTP|status code)\s*(\d{3})|"code"\s*:\s*(\d{3})/;
596      const match = correctedDetails.message.match(regex);
597      const parsedStatusString = match ? (match[1] || match[2]) : null;
598
599      if (parsedStatusString) {
600        const parsedStatus = parseInt(parsedStatusString, 10);
601        if (parsedStatus >= 400 && parsedStatus <= 599 && correctedDetails.status !== parsedStatus) {
602          this.logger.warn(`[认证] 修正了错误状态码!原始: ${correctedDetails.status}, 从消息中解析得到: ${parsedStatus}`);
603          correctedDetails.status = parsedStatus;
604        }
605      }
606    }
607
608    const isImmediateSwitch = this.config.immediateSwitchStatusCodes.includes(correctedDetails.status);
609
610    if (isImmediateSwitch) {
611      this.logger.warn(`🔴 [认证] 收到状态码 ${correctedDetails.status} (已修正),触发立即切换账号...`);
612      if (res) this._sendErrorChunkToClient(res, `收到状态码 ${correctedDetails.status},正在尝试切换账号...`);
613      try {
614        await this._switchToNextAuth();
615        if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
616      } catch (switchError) {
617        this.logger.error(`🔴 [认证] 账号切换失败: ${switchError.message}`);
618        if (res) this._sendErrorChunkToClient(res, `切换账号失败: ${switchError.message}`);
619      }
620      return;
621    }
622
623    if (this.config.failureThreshold > 0) {
624      this.failureCount++;
625      this.logger.warn(`⚠️ [认证] 请求失败 - 失败计数: ${this.failureCount}/${this.config.failureThreshold} (当前账号索引: ${this.currentAuthIndex}, 状态码: ${correctedDetails.status})`);
626      if (this.failureCount >= this.config.failureThreshold) {
627        this.logger.warn(`🔴 [认证] 达到失败阈值!准备切换账号...`);
628        if (res) this._sendErrorChunkToClient(res, `连续失败${this.failureCount}次,正在尝试切换账号...`);
629        try {
630          await this._switchToNextAuth();
631          if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
632        } catch (switchError) {
633          this.logger.error(`🔴 [认证] 账号切换失败: ${switchError.message}`);
634          if (res) this._sendErrorChunkToClient(res, `切换账号失败: ${switchError.message}`);
635        }
636      }
637    } else {
638      this.logger.warn(`[认证] 请求失败 (状态码: ${correctedDetails.status})。基于计数的自动切换已禁用 (failureThreshold=0)`);
639    }
640  }
641
642  _getModelFromRequest(req) {
643      let body = req.body;
644      
645      if (Buffer.isBuffer(body)) {
646          try {
647              body = JSON.parse(body.toString('utf-8'));
648          } catch (e) { body = {}; }
649      } else if (typeof body === 'string') {
650          try {
651              body = JSON.parse(body);
652          } catch(e) { body = {}; }
653      }
654
655      if (body && typeof body === 'object') {
656          if (body.model) return body.model;
657          if (body.generation_config && body.generation_config.model) return body.generation_config.model;
658      }
659      
660      const match = req.path.match(/\/models\/([^/:]+)/);
661      if (match && match[1]) {
662          return match[1];
663      }
664      return 'unknown_model';
665  }
666
667  async processRequest(req, res) {
668    // 提前获取模型名称和当前账号
669    const modelName = this._getModelFromRequest(req);
670    const currentAccount = this.currentAuthIndex;
671
672    // 新增的合并日志行,报告路径、账号和模型
673    this.logger.info(`[请求] ${req.method} ${req.path} | 账号: ${currentAccount} | 模型: 🤖 ${modelName}`);
674    
675    // --- 升级的统计逻辑 ---
676    this.serverSystem.stats.totalCalls++;
677    if (this.serverSystem.stats.accountCalls[currentAccount]) {
678        this.serverSystem.stats.accountCalls[currentAccount].total = (this.serverSystem.stats.accountCalls[currentAccount].total || 0) + 1;
679        this.serverSystem.stats.accountCalls[currentAccount].models[modelName] = (this.serverSystem.stats.accountCalls[currentAccount].models[modelName] || 0) + 1;
680    } else {
681        this.serverSystem.stats.accountCalls[currentAccount] = {
682            total: 1,
683            models: { [modelName]: 1 }
684        };
685    }
686    
687    if (!this.connectionRegistry.hasActiveConnections()) {
688      return this._sendErrorResponse(res, 503, '没有可用的浏览器连接');
689    }
690    const requestId = this._generateRequestId();
691    const proxyRequest = this._buildProxyRequest(req, requestId);
692    const messageQueue = this.connectionRegistry.createMessageQueue(requestId);
693    try {
694      if (this.serverSystem.streamingMode === 'fake') {
695        await this._handlePseudoStreamResponse(proxyRequest, messageQueue, req, res);
696      } else {
697        await this._handleRealStreamResponse(proxyRequest, messageQueue, res);
698      }
699    } catch (error) {
700      this._handleRequestError(error, res);
701    } finally {
702      this.connectionRegistry.removeMessageQueue(requestId);
703    }
704  }
705  _generateRequestId() { return `${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; }
706  _buildProxyRequest(req, requestId) {
707    let requestBodyString;
708    if (typeof req.body === 'object' && req.body !== null) {
709      requestBodyString = JSON.stringify(req.body);
710    } else if (typeof req.body === 'string') {
711      requestBodyString = req.body;
712    } else if (Buffer.isBuffer(req.body)) {
713      requestBodyString = req.body.toString('utf-8');
714    } else {
715      requestBodyString = '';
716    }
717
718    return {
719      path: req.path, method: req.method, headers: req.headers, query_params: req.query,
720      body: requestBodyString,
721      request_id: requestId, streaming_mode: this.serverSystem.streamingMode
722    };
723  }
724  _forwardRequest(proxyRequest) {
725    const connection = this.connectionRegistry.getFirstConnection();
726    if (connection) {
727      connection.send(JSON.stringify(proxyRequest));
728    } else {
729      throw new Error("无法转发请求:没有可用的WebSocket连接。");
730    }
731  }
732  _sendErrorChunkToClient(res, errorMessage) {
733    const errorPayload = {
734      error: { message: `[代理系统提示] ${errorMessage}`, type: 'proxy_error', code: 'proxy_error' }
735    };
736    const chunk = `data: ${JSON.stringify(errorPayload)}\n\n`;
737    if (res && !res.writableEnded) {
738      res.write(chunk);
739      this.logger.info(`[请求] 已向客户端发送标准错误信号: ${errorMessage}`);
740    }
741  }
742
743  _getKeepAliveChunk(req) {
744    if (req.path.includes('chat/completions')) {
745      const payload = { id: `chatcmpl-${this._generateRequestId()}`, object: "chat.completion.chunk", created: Math.floor(Date.now() / 1000), model: "gpt-4", choices: [{ index: 0, delta: {}, finish_reason: null }] };
746      return `data: ${JSON.stringify(payload)}\n\n`;
747    }
748    if (req.path.includes('generateContent') || req.path.includes('streamGenerateContent')) {
749      const payload = { candidates: [{ content: { parts: [{ text: "" }], role: "model" }, finishReason: null, index: 0, safetyRatings: [] }] };
750      return `data: ${JSON.stringify(payload)}\n\n`;
751    }
752    return 'data: {}\n\n';
753  }
754
755  async _handlePseudoStreamResponse(proxyRequest, messageQueue, req, res) {
756    const originalPath = req.path;
757    const isStreamRequest = originalPath.includes(':stream');
758
759    this.logger.info(`[请求] 假流式处理流程启动,路径: "${originalPath}",判定为: ${isStreamRequest ? '流式请求' : '非流式请求'}`);
760
761    let connectionMaintainer = null;
762
763    if (isStreamRequest) {
764      res.status(200).set({
765        'Content-Type': 'text/event-stream',
766        'Cache-Control': 'no-cache',
767        'Connection': 'keep-alive'
768      });
769      const keepAliveChunk = this._getKeepAliveChunk(req);
770      connectionMaintainer = setInterval(() => { if (!res.writableEnded) res.write(keepAliveChunk); }, 2000);
771    }
772
773    try {
774      let lastMessage, requestFailed = false;
775      for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
776        this.logger.info(`[请求] 请求尝试 #${attempt}/${this.maxRetries}...`);
777        this._forwardRequest(proxyRequest);
778        lastMessage = await messageQueue.dequeue();
779
780        if (lastMessage.event_type === 'error' && lastMessage.status >= 400 && lastMessage.status <= 599) {
781          const correctedMessage = this._parseAndCorrectErrorDetails(lastMessage);
782          await this._handleRequestFailureAndSwitch(correctedMessage, isStreamRequest ? res : null);
783
784          const errorText = `收到 ${correctedMessage.status} 错误。${attempt < this.maxRetries ? `将在 ${this.retryDelay / 1000}秒后重试...` : '已达到最大重试次数。'}`;
785          this.logger.warn(`[请求] ${errorText}`);
786
787          if (isStreamRequest) {
788            this._sendErrorChunkToClient(res, errorText);
789          }
790
791          if (attempt < this.maxRetries) {
792            await new Promise(resolve => setTimeout(resolve, this.retryDelay));
793            continue;
794          }
795          requestFailed = true;
796        }
797        break;
798      }
799
800      if (lastMessage.event_type === 'error' || requestFailed) {
801        const finalError = this._parseAndCorrectErrorDetails(lastMessage);
802        if (!res.headersSent) {
803          this._sendErrorResponse(res, finalError.status, `请求失败: ${finalError.message}`);
804        } else {
805          this._sendErrorChunkToClient(res, `请求最终失败 (状态码: ${finalError.status}): ${finalError.message}`);
806        }
807        return;
808      }
809
810      if (this.failureCount > 0) {
811        this.logger.info(`✅ [认证] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
812      }
813      this.failureCount = 0;
814
815      const dataMessage = await messageQueue.dequeue();
816      const endMessage = await messageQueue.dequeue();
817      if (endMessage.type !== 'STREAM_END') this.logger.warn('[请求] 未收到预期的流结束信号。');
818
819      if (isStreamRequest) {
820        if (dataMessage.data) {
821          res.write(`data: ${dataMessage.data}\n\n`);
822        }
823        res.write('data: [DONE]\n\n');
824        this.logger.info('[请求] 已将完整响应作为模拟SSE事件发送。');
825      } else {
826        this.logger.info('[请求] 准备发送 application/json 响应。');
827        if (dataMessage.data) {
828          try {
829            const jsonData = JSON.parse(dataMessage.data);
830            res.status(200).json(jsonData);
831          } catch (e) {
832            this.logger.error(`[请求] 无法将来自浏览器的响应解析为JSON: ${e.message}`);
833            this._sendErrorResponse(res, 500, '代理内部错误:无法解析来自后端的响应。');
834          }
835        } else {
836          this._sendErrorResponse(res, 500, '代理内部错误:后端未返回有效数据。');
837        }
838      }
839
840    } catch (error) {
841      this.logger.error(`[请求] 假流式处理期间发生意外错误: ${error.message}`);
842      if (!res.headersSent) {
843        this._handleRequestError(error, res);
844      } else {
845        this._sendErrorChunkToClient(res, `处理失败: ${error.message}`);
846      }
847    } finally {
848      if (connectionMaintainer) clearInterval(connectionMaintainer);
849      if (!res.writableEnded) res.end();
850      this.logger.info('[请求] 假流式响应处理结束。');
851    }
852  }
853
854  async _handleRealStreamResponse(proxyRequest, messageQueue, res) {
855    let headerMessage, requestFailed = false;
856    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
857      this.logger.info(`[请求] 请求尝试 #${attempt}/${this.maxRetries}...`);
858      this._forwardRequest(proxyRequest);
859      headerMessage = await messageQueue.dequeue();
860      if (headerMessage.event_type === 'error' && headerMessage.status >= 400 && headerMessage.status <= 599) {
861
862        const correctedMessage = this._parseAndCorrectErrorDetails(headerMessage);
863        await this._handleRequestFailureAndSwitch(correctedMessage, null);
864        this.logger.warn(`[请求] 收到 ${correctedMessage.status} 错误,将在 ${this.retryDelay / 1000}秒后重试...`);
865
866        if (attempt < this.maxRetries) {
867          await new Promise(resolve => setTimeout(resolve, this.retryDelay));
868          continue;
869        }
870        requestFailed = true;
871      }
872      break;
873    }
874    if (headerMessage.event_type === 'error' || requestFailed) {
875      const finalError = this._parseAndCorrectErrorDetails(headerMessage);
876      return this._sendErrorResponse(res, finalError.status, finalError.message);
877    }
878    if (this.failureCount > 0) {
879      this.logger.info(`✅ [认证] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
880    }
881    this.failureCount = 0;
882    this._setResponseHeaders(res, headerMessage);
883    this.logger.info('[请求] 已向客户端发送真实响应头,开始流式传输...');
884    try {
885      while (true) {
886        const dataMessage = await messageQueue.dequeue(30000);
887        if (dataMessage.type === 'STREAM_END') { this.logger.info('[请求] 收到流结束信号。'); break; }
888        if (dataMessage.data) res.write(dataMessage.data);
889      }
890    } catch (error) {
891      if (error.message !== '队列超时') throw error;
892      this.logger.warn('[请求] 真流式响应超时,可能流已正常结束。');
893    } finally {
894      if (!res.writableEnded) res.end();
895      this.logger.info('[请求] 真流式响应连接已关闭。');
896    }
897  }
898
899  _setResponseHeaders(res, headerMessage) {
900    res.status(headerMessage.status || 200);
901    const headers = headerMessage.headers || {};
902    Object.entries(headers).forEach(([name, value]) => {
903      if (name.toLowerCase() !== 'content-length') res.set(name, value);
904    });
905  }
906  _handleRequestError(error, res) {
907    if (res.headersSent) {
908      this.logger.error(`[请求] 请求处理错误 (头已发送): ${error.message}`);
909      if (this.serverSystem.streamingMode === 'fake') this._sendErrorChunkToClient(res, `处理失败: ${error.message}`);
910      if (!res.writableEnded) res.end();
911    } else {
912      this.logger.error(`[请求] 请求处理错误: ${error.message}`);
913      const status = error.message.includes('超时') ? 504 : 500;
914      this._sendErrorResponse(res, status, `代理错误: ${error.message}`);
915    }
916  }
917  _sendErrorResponse(res, status, message) {
918    if (!res.headersSent) res.status(status || 500).type('text/plain').send(message);
919  }
920}
921
922class ProxyServerSystem extends EventEmitter {
923  constructor() {
924    super();
925    this.logger = new LoggingService('ProxySystem');
926    this._loadConfiguration();
927    this.streamingMode = this.config.streamingMode;
928    
929    // 升级后的统计结构
930    this.stats = {
931        totalCalls: 0,
932        accountCalls: {} // e.g., { "1": { total: 10, models: { "gemini-pro": 5, "gpt-4": 5 } } }
933    };
934
935    this.authSource = new AuthSource(this.logger);
936    this.browserManager = new BrowserManager(this.logger, this.config, this.authSource);
937    this.connectionRegistry = new ConnectionRegistry(this.logger);
938    this.requestHandler = new RequestHandler(this, this.connectionRegistry, this.logger, this.browserManager, this.config, this.authSource);
939
940    this.httpServer = null;
941    this.wsServer = null;
942  }
943
944  _loadConfiguration() {
945    let config = {
946      httpPort: 8889, host: '0.0.0.0', wsPort: 9998, streamingMode: 'real',
947      failureThreshold: 0,
948      maxRetries: 3, retryDelay: 2000, browserExecutablePath: null,
949      apiKeys: [],
950      immediateSwitchStatusCodes: [],
951      initialAuthIndex: null,
952      debugMode: false,
953    };
954
955    const configPath = path.join(__dirname, 'config.json');
956    try {
957      if (fs.existsSync(configPath)) {
958        const fileConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
959        config = { ...config, ...fileConfig };
960        this.logger.info('[系统] 已从 config.json 加载配置。');
961      }
962    } catch (error) {
963      this.logger.warn(`[系统] 无法读取或解析 config.json: ${error.message}`);
964    }
965
966    if (process.env.PORT) config.httpPort = parseInt(process.env.PORT, 10) || config.httpPort;
967    if (process.env.HOST) config.host = process.env.HOST;
968    if (process.env.STREAMING_MODE) config.streamingMode = process.env.STREAMING_MODE;
969    if (process.env.FAILURE_THRESHOLD) config.failureThreshold = parseInt(process.env.FAILURE_THRESHOLD, 10) || config.failureThreshold;
970    if (process.env.MAX_RETRIES) config.maxRetries = parseInt(process.env.MAX_RETRIES, 10) || config.maxRetries;
971    if (process.env.RETRY_DELAY) config.retryDelay = parseInt(process.env.RETRY_DELAY, 10) || config.retryDelay;
972    if (process.env.CAMOUFOX_EXECUTABLE_PATH) config.browserExecutablePath = process.env.CAMOUFOX_EXECUTABLE_PATH;
973    if (process.env.API_KEYS) {
974      config.apiKeys = process.env.API_KEYS.split(',');
975    }
976    if (process.env.DEBUG_MODE) {
977      config.debugMode = process.env.DEBUG_MODE === 'true';
978    }
979    if (process.env.INITIAL_AUTH_INDEX) {
980      const envIndex = parseInt(process.env.INITIAL_AUTH_INDEX, 10);
981      if (!isNaN(envIndex) && envIndex > 0) {
982        config.initialAuthIndex = envIndex;
983      }
984    }
985
986    let rawCodes = process.env.IMMEDIATE_SWITCH_STATUS_CODES;
987    let codesSource = '环境变量';
988
989    if (!rawCodes && config.immediateSwitchStatusCodes && Array.isArray(config.immediateSwitchStatusCodes)) {
990      rawCodes = config.immediateSwitchStatusCodes.join(',');
991      codesSource = 'config.json 文件';
992    }
993
994    if (rawCodes && typeof rawCodes === 'string') {
995      config.immediateSwitchStatusCodes = rawCodes
996        .split(',')
997        .map(code => parseInt(String(code).trim(), 10))
998        .filter(code => !isNaN(code) && code >= 400 && code <= 599);
999      if (config.immediateSwitchStatusCodes.length > 0) {
1000        this.logger.info(`[系统] 已从 ${codesSource} 加载“立即切换状态码”。`);
1001      }
1002    } else {
1003      config.immediateSwitchStatusCodes = [];
1004    }
1005
1006    if (Array.isArray(config.apiKeys)) {
1007      config.apiKeys = config.apiKeys.map(k => String(k).trim()).filter(k => k);
1008    } else {
1009      config.apiKeys = [];
1010    }
1011
1012    this.config = config;
1013    this.logger.info('================ [ 生效配置 ] ================');
1014    this.logger.info(`  HTTP 服务端口: ${this.config.httpPort}`);
1015    this.logger.info(`  监听地址: ${this.config.host}`);
1016    this.logger.info(`  流式模式: ${this.config.streamingMode}`);
1017    this.logger.info(`  调试模式: ${this.config.debugMode ? '已开启' : '已关闭'}`);
1018    if (this.config.initialAuthIndex) {
1019      this.logger.info(`  指定初始认证索引: ${this.config.initialAuthIndex}`);
1020    }
1021    this.logger.info(`  失败计数切换: ${this.config.failureThreshold > 0 ? `连续 ${this.config.failureThreshold} 次失败后切换` : '已禁用'}`);
1022    this.logger.info(`  立即切换状态码: ${this.config.immediateSwitchStatusCodes.length > 0 ? this.config.immediateSwitchStatusCodes.join(', ') : '已禁用'}`);
1023    this.logger.info(`  单次请求最大重试: ${this.config.maxRetries}次`);
1024    this.logger.info(`  重试间隔: ${this.config.retryDelay}ms`);
1025    if (this.config.apiKeys && this.config.apiKeys.length > 0) {
1026      this.logger.info(`  API 密钥认证: 已启用 (${this.config.apiKeys.length} 个密钥)`);
1027    } else {
1028      this.logger.info(`  API 密钥认证: 已禁用`);
1029    }
1030    this.logger.info('=============================================================');
1031  }
1032
1033  async start() {
1034    try {
1035      // 初始化统计对象
1036      this.authSource.getAvailableIndices().forEach(index => {
1037          this.stats.accountCalls[index] = { total: 0, models: {} };
1038      });
1039      
1040      let startupIndex = this.authSource.getFirstAvailableIndex();
1041      const suggestedIndex = this.config.initialAuthIndex;
1042
1043      if (suggestedIndex) {
1044        if (this.authSource.getAvailableIndices().includes(suggestedIndex)) {
1045          this.logger.info(`[系统] 使用配置中指定的有效启动索引: ${suggestedIndex}`);
1046          startupIndex = suggestedIndex;
1047        } else {
1048          this.logger.warn(`[系统] 配置中指定的启动索引 ${suggestedIndex} 无效或不存在,将使用第一个可用索引: ${startupIndex}`);
1049        }
1050      } else {
1051        this.logger.info(`[系统] 未指定启动索引,将自动使用第一个可用索引: ${startupIndex}`);
1052      }
1053
1054      await this.browserManager.launchBrowser(startupIndex);
1055      await this._startHttpServer();
1056      await this._startWebSocketServer();
1057      this.logger.info(`[系统] 代理服务器系统启动完成。`);
1058      this.emit('started');
1059    } catch (error) {
1060      this.logger.error(`[系统] 启动失败: ${error.message}`);
1061      this.emit('error', error);
1062      process.exit(1); // 启动失败时退出
1063    }
1064  }
1065
1066  _createDebugLogMiddleware() {
1067    return (req, res, next) => {
1068      if (!this.config.debugMode) {
1069        return next();
1070      }
1071
1072      const requestId = this.requestHandler._generateRequestId();
1073      const log = this.logger.info.bind(this.logger);
1074
1075      log(`\n\n--- [调试] 开始处理入站请求 (${requestId}) ---`);
1076      log(`[调试][${requestId}] 客户端 IP: ${req.ip}`);
1077      log(`[调试][${requestId}] 方法: ${req.method}`);
1078      log(`[调试][${requestId}] URL: ${req.originalUrl}`);
1079      log(`[调试][${requestId}] 请求头: ${JSON.stringify(req.headers, null, 2)}`);
1080
1081      let bodyContent = '无或空';
1082      if (req.body) {
1083        if (Buffer.isBuffer(req.body) && req.body.length > 0) {
1084            try {
1085                bodyContent = JSON.stringify(JSON.parse(req.body.toString('utf-8')), null, 2);
1086            } catch (e) {
1087                bodyContent = `[无法解析为JSON的Buffer, 大小: ${req.body.length} 字节]`;
1088            }
1089        } else if (typeof req.body === 'object' && Object.keys(req.body).length > 0) {
1090           bodyContent = JSON.stringify(req.body, null, 2);
1091        }
1092      }
1093
1094      log(`[调试][${requestId}] 请求体:\n${bodyContent}`);
1095      log(`--- [调试] 结束处理入站请求 (${requestId}) ---\n\n`);
1096
1097      next();
1098    };
1099  }
1100
1101
1102  _createAuthMiddleware() {
1103    return (req, res, next) => {
1104      const serverApiKeys = this.config.apiKeys;
1105      if (!serverApiKeys || serverApiKeys.length === 0) {
1106        return next();
1107      }
1108
1109      let clientKey = null;
1110      let keySource = null;
1111
1112      const headers = req.headers;
1113      const xGoogApiKey = headers['x-goog-api-key'] || headers['x_goog_api_key'];
1114      const xApiKey = headers['x-api-key'] || headers['x_api_key'];
1115      const authHeader = headers.authorization;
1116
1117      if (xGoogApiKey) {
1118        clientKey = xGoogApiKey;
1119        keySource = 'x-goog-api-key 请求头';
1120      } else if (authHeader && authHeader.startsWith('Bearer ')) {
1121        clientKey = authHeader.substring(7);
1122        keySource = 'Authorization 请求头';
1123      } else if (xApiKey) {
1124        clientKey = xApiKey;
1125        keySource = 'X-API-Key 请求头';
1126      } else if (req.query.key) {
1127        clientKey = req.query.key;
1128        keySource = '查询参数';
1129      }
1130
1131      if (clientKey) {
1132        if (serverApiKeys.includes(clientKey)) {
1133          if (this.config.debugMode) {
1134              this.logger.debug(`[认证][调试] 在 '${keySource}' 中找到API密钥,验证通过。`);
1135          }
1136          if (keySource === '查询参数') {
1137            delete req.query.key;
1138          }
1139          return next();
1140        } else {
1141          if (this.config.debugMode) {
1142            this.logger.warn(`[认证][调试] 拒绝请求: 无效的API密钥。IP: ${req.ip}, 路径: ${req.path}`);
1143            this.logger.debug(`[认证][调试] 来源: ${keySource}`);
1144            this.logger.debug(`[认证][调试] 提供的错误密钥: '${clientKey}'`);
1145            this.logger.debug(`[认证][调试] 已加载的有效密钥: [${serverApiKeys.join(', ')}]`);
1146          } else {
1147            this.logger.warn(`[认证] 拒绝请求: 无效的API密钥。IP: ${req.ip}, 路径: ${req.path}`);
1148          }
1149          return res.status(401).json({ error: { message: "提供了无效的API密钥。" } });
1150        }
1151      }
1152
1153      this.logger.warn(`[认证] 拒绝受保护的请求: 缺少API密钥。IP: ${req.ip}, 路径: ${req.path}`);
1154      
1155      if (this.config.debugMode) {
1156        this.logger.debug(`[认证][调试] 未在任何标准位置找到API密钥。`);
1157        this.logger.debug(`[认证][调试] 搜索的请求头: ${JSON.stringify(headers, null, 2)}`);
1158        this.logger.debug(`[认证][调试] 搜索的查询参数: ${JSON.stringify(req.query)}`);
1159        this.logger.debug(`[认证][调试] 已加载的有效密钥: [${serverApiKeys.join(', ')}]`);
1160      }
1161
1162      return res.status(401).json({ error: { message: "访问被拒绝。未在请求头或查询参数中找到有效的API密钥。" } });
1163    };
1164  }
1165
1166  async _startHttpServer() {
1167    const app = this._createExpressApp();
1168    this.httpServer = http.createServer(app);
1169    return new Promise((resolve) => {
1170      this.httpServer.listen(this.config.httpPort, this.config.host, () => {
1171        this.logger.info(`[系统] HTTP服务器已在 http://${this.config.host}:${this.config.httpPort} 上监听`);
1172        this.logger.info(`[系统] 仪表盘可在 http://${this.config.host}:${this.config.httpPort}/dashboard 访问`);
1173        resolve();
1174      });
1175    });
1176  }
1177
1178  // [可复制并覆盖]
1179// 请用此版本完整替换您文件中的 _createExpressApp 方法
1180  _createExpressApp() {
1181    const app = express();
1182    app.use(express.json({ limit: '100mb' }));
1183    app.use(express.raw({ type: '*/*', limit: '100mb' }));
1184    app.use((req, res, next) => {
1185        if (req.is('application/json') && typeof req.body === 'object' && !Buffer.isBuffer(req.body)) {
1186          // Already parsed correctly by express.json()
1187        } else if (Buffer.isBuffer(req.body)) {
1188          const bodyStr = req.body.toString('utf-8');
1189          if (bodyStr) {
1190            try {
1191              req.body = JSON.parse(bodyStr);
1192            } catch (e) {
1193              // Not JSON, leave as buffer.
1194            }
1195          }
1196        }
1197        next();
1198    });
1199    
1200    app.use(this._createDebugLogMiddleware());

Showing the first 1,200 of 1786 lines. Download the file for the rest.