holyterra commited on
Commit
aff0190
·
verified ·
1 Parent(s): a7a65b3

Upload unified-server.js

Browse files
Files changed (1) hide show
  1. unified-server.js +109 -29
unified-server.js CHANGED
@@ -219,13 +219,13 @@ class BrowserManager {
219
  this.page = await this.context.newPage();
220
  this.logger.info(`[Browser] 正在加载账户 ${authIndex} 并访问目标网页...`);
221
  const targetUrl = 'https://aistudio.google.com/u/0/apps/bundled/blank?showPreview=true&showCode=true&showAssistant=true';
222
- await this.page.goto(targetUrl, { timeout: 60000, waitUntil: 'networkidle' });
223
  this.logger.info('[Browser] 网页加载完成,正在注入客户端脚本...');
224
 
225
  const editorContainerLocator = this.page.locator('div.monaco-editor').first();
226
 
227
- this.logger.info('[Browser] 等待编辑器出现,最长60秒...');
228
- await editorContainerLocator.waitFor({ state: 'visible', timeout: 60000 });
229
  this.logger.info('[Browser] 编辑器已出现,准备粘贴脚本。');
230
 
231
  // --- START: 新增的点击逻辑 ---
@@ -243,7 +243,7 @@ class BrowserManager {
243
  }
244
  // --- END: 新增的点击逻辑 ---
245
 
246
- await editorContainerLocator.click({ timeout: 60000 });
247
  await this.page.evaluate(text => navigator.clipboard.writeText(text), buildScriptContent);
248
  const isMac = os.platform() === 'darwin';
249
  const pasteKey = isMac ? 'Meta+V' : 'Control+V';
@@ -302,7 +302,7 @@ class LoggingService {
302
  }
303
 
304
  class MessageQueue extends EventEmitter {
305
- constructor(timeoutMs = 600000) {
306
  super();
307
  this.messages = [];
308
  this.waitingResolvers = [];
@@ -469,7 +469,8 @@ class RequestHandler {
469
  if (nextAuthIndex === null) {
470
  this.logger.error('🔴 [Auth] 无法切换账号,因为没有可用的认证源!');
471
  this.isAuthSwitching = false;
472
- return;
 
473
  }
474
 
475
  this.logger.info('==================================================');
@@ -497,12 +498,45 @@ class RequestHandler {
497
  }
498
  }
499
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  async _handleRequestFailureAndSwitch(errorDetails, res) {
501
- const isImmediateSwitch = this.config.immediateSwitchStatusCodes.includes(errorDetails.status);
 
 
 
 
 
502
 
503
  if (isImmediateSwitch) {
504
- this.logger.warn(`🔴 [Auth] 收到状态码 ${errorDetails.status},触发立即切换账号...`);
505
- if (res) this._sendErrorChunkToClient(res, `收到状态码 ${errorDetails.status},正在尝试切换账号...`);
506
  try {
507
  await this._switchToNextAuth();
508
  if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
@@ -512,23 +546,24 @@ class RequestHandler {
512
  }
513
  return; // End here after immediate switch attempt
514
  }
515
-
 
516
  if (this.config.failureThreshold > 0) {
517
- this.failureCount++;
518
- this.logger.warn(`⚠️ [Auth] 请求失败 - 失败计数: ${this.failureCount}/${this.config.failureThreshold} (当前账号索引: ${this.currentAuthIndex})`);
519
- if (this.failureCount >= this.config.failureThreshold) {
520
- this.logger.warn(`🔴 [Auth] 达到失败阈值!准备切换账号...`);
521
- if (res) this._sendErrorChunkToClient(res, `连续失败${this.failureCount}次,正在尝试切换账号...`);
522
- try {
523
- await this._switchToNextAuth();
524
- if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
525
- } catch (switchError) {
526
- this.logger.error(`🔴 [Auth] 账号切换失败: ${switchError.message}`);
527
- if (res) this._sendErrorChunkToClient(res, `切换账号失败: ${switchError.message}`);
 
528
  }
529
- }
530
  } else {
531
- this.logger.warn(`[Auth] 请求失败 (状态码: ${errorDetails.status})。基于计数的自动切换已禁用 (failureThreshold=0)`);
532
  }
533
  }
534
 
@@ -595,8 +630,13 @@ class RequestHandler {
595
  this._forwardRequest(proxyRequest);
596
  lastMessage = await messageQueue.dequeue();
597
  if (lastMessage.event_type === 'error' && lastMessage.status >= 400 && lastMessage.status <= 599) {
598
- await this._handleRequestFailureAndSwitch(lastMessage, res);
599
- const errorText = `收到 ${lastMessage.status} 错误。${attempt < this.maxRetries ? `将在 ${this.retryDelay / 1000}秒后重试...` : '已达到最大重试次数。'}`;
 
 
 
 
 
600
  this._sendErrorChunkToClient(res, errorText);
601
  if (attempt < this.maxRetries) {
602
  await new Promise(resolve => setTimeout(resolve, this.retryDelay));
@@ -606,9 +646,14 @@ class RequestHandler {
606
  }
607
  break;
608
  }
 
609
  if (lastMessage.event_type === 'error' || requestFailed) {
610
- throw new Error(lastMessage.message || '请求失败');
 
 
611
  }
 
 
612
  if (this.failureCount > 0) {
613
  this.logger.info(`✅ [Auth] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
614
  }
@@ -633,8 +678,13 @@ class RequestHandler {
633
  this._forwardRequest(proxyRequest);
634
  headerMessage = await messageQueue.dequeue();
635
  if (headerMessage.event_type === 'error' && headerMessage.status >= 400 && headerMessage.status <= 599) {
636
- await this._handleRequestFailureAndSwitch(headerMessage, null); // `res` is not available for streaming chunks here
637
- this.logger.warn(`[Request] 收到 ${headerMessage.status} 错误,将在 ${this.retryDelay / 1000}秒后重试...`);
 
 
 
 
 
638
  if (attempt < this.maxRetries) {
639
  await new Promise(resolve => setTimeout(resolve, this.retryDelay));
640
  continue;
@@ -644,7 +694,11 @@ class RequestHandler {
644
  break;
645
  }
646
  if (headerMessage.event_type === 'error' || requestFailed) {
647
- return this._sendErrorResponse(res, headerMessage.status, headerMessage.message);
 
 
 
 
648
  }
649
  if (this.failureCount > 0) {
650
  this.logger.info(`✅ [Auth] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
@@ -941,6 +995,32 @@ class ProxyServerSystem extends EventEmitter {
941
  });
942
  });
943
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
944
  app.use(this._createAuthMiddleware());
945
 
946
  app.all(/(.*)/, (req, res) => {
 
219
  this.page = await this.context.newPage();
220
  this.logger.info(`[Browser] 正在加载账户 ${authIndex} 并访问目标网页...`);
221
  const targetUrl = 'https://aistudio.google.com/u/0/apps/bundled/blank?showPreview=true&showCode=true&showAssistant=true';
222
+ await this.page.goto(targetUrl, { timeout: 120000, waitUntil: 'networkidle' });
223
  this.logger.info('[Browser] 网页加载完成,正在注入客户端脚本...');
224
 
225
  const editorContainerLocator = this.page.locator('div.monaco-editor').first();
226
 
227
+ this.logger.info('[Browser] 等待编辑器出现,最长120秒...');
228
+ await editorContainerLocator.waitFor({ state: 'visible', timeout: 120000 });
229
  this.logger.info('[Browser] 编辑器已出现,准备粘贴脚本。');
230
 
231
  // --- START: 新增的点击逻辑 ---
 
243
  }
244
  // --- END: 新增的点击逻辑 ---
245
 
246
+ await editorContainerLocator.click({ timeout: 120000 });
247
  await this.page.evaluate(text => navigator.clipboard.writeText(text), buildScriptContent);
248
  const isMac = os.platform() === 'darwin';
249
  const pasteKey = isMac ? 'Meta+V' : 'Control+V';
 
302
  }
303
 
304
  class MessageQueue extends EventEmitter {
305
+ constructor(timeoutMs = 1200000) {
306
  super();
307
  this.messages = [];
308
  this.waitingResolvers = [];
 
469
  if (nextAuthIndex === null) {
470
  this.logger.error('🔴 [Auth] 无法切换账号,因为没有可用的认证源!');
471
  this.isAuthSwitching = false;
472
+ // 抛出错误以便调用者可以捕获它
473
+ throw new Error('No available authentication sources to switch to.');
474
  }
475
 
476
  this.logger.info('==================================================');
 
498
  }
499
  }
500
 
501
+ // NEW: Error parsing and correction utility
502
+ _parseAndCorrectErrorDetails(errorDetails) {
503
+ // 创建一个副本以避免修改原始对象
504
+ const correctedDetails = { ...errorDetails };
505
+ this.logger.debug(`[ErrorParser] 原始错误详情: status=${correctedDetails.status}, message="${correctedDetails.message}"`);
506
+
507
+ // 只有在错误消息存在时才尝试解析
508
+ if (correctedDetails.message && typeof correctedDetails.message === 'string') {
509
+ // 正则表达式匹配 "HTTP xxx" 或 "status code xxx" 等模式
510
+ const regex = /(?:HTTP|status code)\s+(\d{3})/;
511
+ const match = correctedDetails.message.match(regex);
512
+
513
+ if (match && match[1]) {
514
+ const parsedStatus = parseInt(match[1], 10);
515
+ // 确保解析出的状态码是有效的 HTTP 错误码
516
+ if (parsedStatus >= 400 && parsedStatus <= 599) {
517
+ if (correctedDetails.status !== parsedStatus) {
518
+ this.logger.warn(`[ErrorParser] 修正了错误状态码!原始: ${correctedDetails.status}, 从消息中解析得到: ${parsedStatus}`);
519
+ correctedDetails.status = parsedStatus; // 使用解析出的更准确的状态码
520
+ } else {
521
+ this.logger.debug(`[ErrorParser] 解析的状态码 (${parsedStatus}) 与原始状态码一致,无需修正。`);
522
+ }
523
+ }
524
+ }
525
+ }
526
+ return correctedDetails;
527
+ }
528
+
529
  async _handleRequestFailureAndSwitch(errorDetails, res) {
530
+ // --- START: MODIFICATION ---
531
+ const correctedErrorDetails = this._parseAndCorrectErrorDetails(errorDetails);
532
+ // --- END: MODIFICATION ---
533
+
534
+ // 使用修正后的错误详情进行判断
535
+ const isImmediateSwitch = this.config.immediateSwitchStatusCodes.includes(correctedErrorDetails.status);
536
 
537
  if (isImmediateSwitch) {
538
+ this.logger.warn(`🔴 [Auth] 收到状态码 ${correctedErrorDetails.status} (已修正),触发立即切换账号...`);
539
+ if (res) this._sendErrorChunkToClient(res, `收到状态码 ${correctedErrorDetails.status},正在尝试切换账号...`);
540
  try {
541
  await this._switchToNextAuth();
542
  if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
 
546
  }
547
  return; // End here after immediate switch attempt
548
  }
549
+
550
+ // 使用 correctedErrorDetails.status
551
  if (this.config.failureThreshold > 0) {
552
+ this.failureCount++;
553
+ this.logger.warn(`⚠️ [Auth] 请求失败 - 失败计数: ${this.failureCount}/${this.config.failureThreshold} (当前账号索引: ${this.currentAuthIndex}, 状态码: ${correctedErrorDetails.status})`);
554
+ if (this.failureCount >= this.config.failureThreshold) {
555
+ this.logger.warn(`🔴 [Auth] 达到失败阈值!准备切换账号...`);
556
+ if (res) this._sendErrorChunkToClient(res, `连续失败${this.failureCount}次,正在尝试切换账号...`);
557
+ try {
558
+ await this._switchToNextAuth();
559
+ if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
560
+ } catch (switchError) {
561
+ this.logger.error(`🔴 [Auth] 账号切换失败: ${switchError.message}`);
562
+ if (res) this._sendErrorChunkToClient(res, `切换账号失败: ${switchError.message}`);
563
+ }
564
  }
 
565
  } else {
566
+ this.logger.warn(`[Auth] 请求失败 (状态码: ${correctedErrorDetails.status})。基于计数的自动切换已禁用 (failureThreshold=0)`);
567
  }
568
  }
569
 
 
630
  this._forwardRequest(proxyRequest);
631
  lastMessage = await messageQueue.dequeue();
632
  if (lastMessage.event_type === 'error' && lastMessage.status >= 400 && lastMessage.status <= 599) {
633
+
634
+ // --- START: MODIFICATION ---
635
+ const correctedMessage = this._parseAndCorrectErrorDetails(lastMessage);
636
+ await this._handleRequestFailureAndSwitch(correctedMessage, res);
637
+ const errorText = `收到 ${correctedMessage.status} 错误。${attempt < this.maxRetries ? `将在 ${this.retryDelay / 1000}秒后重试...` : '已达到最大重试次数。'}`;
638
+ // --- END: MODIFICATION ---
639
+
640
  this._sendErrorChunkToClient(res, errorText);
641
  if (attempt < this.maxRetries) {
642
  await new Promise(resolve => setTimeout(resolve, this.retryDelay));
 
646
  }
647
  break;
648
  }
649
+ // --- START: MODIFICATION ---
650
  if (lastMessage.event_type === 'error' || requestFailed) {
651
+ const finalError = this._parseAndCorrectErrorDetails(lastMessage);
652
+ // 抛出错误,以便被外层 catch 块捕获,并使用修正后的信息
653
+ throw new Error(`请求失败 (状态码: ${finalError.status}): ${finalError.message}`);
654
  }
655
+ // --- END: MODIFICATION ---
656
+
657
  if (this.failureCount > 0) {
658
  this.logger.info(`✅ [Auth] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
659
  }
 
678
  this._forwardRequest(proxyRequest);
679
  headerMessage = await messageQueue.dequeue();
680
  if (headerMessage.event_type === 'error' && headerMessage.status >= 400 && headerMessage.status <= 599) {
681
+
682
+ // --- START: MODIFICATION ---
683
+ const correctedMessage = this._parseAndCorrectErrorDetails(headerMessage);
684
+ await this._handleRequestFailureAndSwitch(correctedMessage, null); // res is not available
685
+ this.logger.warn(`[Request] 收到 ${correctedMessage.status} 错误,将在 ${this.retryDelay / 1000}秒后重试...`);
686
+ // --- END: MODIFICATION ---
687
+
688
  if (attempt < this.maxRetries) {
689
  await new Promise(resolve => setTimeout(resolve, this.retryDelay));
690
  continue;
 
694
  break;
695
  }
696
  if (headerMessage.event_type === 'error' || requestFailed) {
697
+ // --- START: MODIFICATION ---
698
+ const finalError = this._parseAndCorrectErrorDetails(headerMessage);
699
+ // 使用修正后的状态码和消息返回给客户端
700
+ return this._sendErrorResponse(res, finalError.status, finalError.message);
701
+ // --- END: MODIFICATION ---
702
  }
703
  if (this.failureCount > 0) {
704
  this.logger.info(`✅ [Auth] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
 
995
  });
996
  });
997
 
998
+ // --- 新增的 /switch 端点 ---
999
+ app.get('/switch', async (req, res) => {
1000
+ this.logger.info('[Admin] 接到 /switch 请求,手动触发账号切换。');
1001
+
1002
+ if (this.requestHandler.isAuthSwitching) {
1003
+ const msg = '账号切换已在进行中,请稍后。';
1004
+ this.logger.warn(`[Admin] /switch 请求被拒绝: ${msg}`);
1005
+ return res.status(429).send(msg);
1006
+ }
1007
+
1008
+ const oldIndex = this.requestHandler.currentAuthIndex;
1009
+
1010
+ try {
1011
+ await this.requestHandler._switchToNextAuth();
1012
+ const newIndex = this.requestHandler.currentAuthIndex;
1013
+
1014
+ const message = `成功将账号从索引 ${oldIndex} 切换到 ${newIndex}。`;
1015
+ this.logger.info(`[Admin] 手动切换成功。 ${message}`);
1016
+ res.status(200).send(message);
1017
+ } catch (error) {
1018
+ const errorMessage = `切换账号失败: ${error.message}`;
1019
+ this.logger.error(`[Admin] 手动切换失败。错误: ${errorMessage}`);
1020
+ res.status(500).send(errorMessage);
1021
+ }
1022
+ });
1023
+
1024
  app.use(this._createAuthMiddleware());
1025
 
1026
  app.all(/(.*)/, (req, res) => {