iyougame commited on
Commit
a6ded16
·
verified ·
1 Parent(s): 7bba343

Update worker.js

Browse files
Files changed (1) hide show
  1. worker.js +641 -185
worker.js CHANGED
@@ -161,9 +161,9 @@ function testBasicConfiguration() {
161
  return { passed, failed };
162
  }
163
 
164
- // 新增:测试函数 - 网络连接性
165
  async function testNetworkConnectivity() {
166
- log('info', '🌐 开始网络连接性测试...');
167
 
168
  const testUrls = [
169
  { name: '主站', url: 'https://toolbaz.com' },
@@ -173,40 +173,74 @@ async function testNetworkConnectivity() {
173
  ];
174
 
175
  const results = [];
 
 
176
 
177
- for (const test of testUrls) {
178
- try {
179
- const startTime = Date.now();
180
- const tempBrowser = await puppeteer.launch({ headless: true });
181
- const tempPage = await tempBrowser.newPage();
182
-
183
- await tempPage.goto(test.url, {
184
- waitUntil: 'networkidle2',
185
- timeout: 15000
186
- });
187
-
188
- const responseTime = Date.now() - startTime;
189
- const title = await tempPage.title();
190
-
191
- results.push({
192
- name: test.name,
193
- url: test.url,
194
- status: 'success',
195
- responseTime,
196
- title: title.substring(0, 50)
197
- });
198
-
199
- await tempBrowser.close();
200
- log('verbose', `✅ ${test.name}: ${responseTime}ms - ${title.substring(0, 30)}...`);
201
-
202
- } catch (error) {
203
- results.push({
204
- name: test.name,
205
- url: test.url,
206
- status: 'failed',
207
- error: error.message
208
- });
209
- log('error', `❌ ${test.name}: ${error.message}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  }
211
  }
212
 
@@ -261,19 +295,22 @@ async function testPromptPrefixFiltering() {
261
  return { results, passed: passCount, failed: results.length - passCount };
262
  }
263
 
264
- // 新增:测试函数 - 浏览器环境
265
  async function testBrowserEnvironment() {
266
  log('info', '🌍 开始浏览器环境测试...');
267
 
 
 
268
  try {
269
- const testBrowser = await puppeteer.launch({
270
- headless: true,
271
- args: ['--no-sandbox', '--disable-setuid-sandbox']
272
- });
273
- const testPage = await testBrowser.newPage();
 
274
 
275
  // 测试基本功能
276
- await testPage.goto('https://example.com', { waitUntil: 'networkidle2' });
277
 
278
  const browserInfo = await testPage.evaluate(() => {
279
  return {
@@ -301,8 +338,6 @@ async function testBrowserEnvironment() {
301
  }
302
  });
303
 
304
- await testBrowser.close();
305
-
306
  log('verbose', '✅ 浏览器环境测试通过');
307
  log('verbose', '📋 浏览器信息:', browserInfo);
308
  log('verbose', '📋 JavaScript功能:', jsTest);
@@ -312,117 +347,132 @@ async function testBrowserEnvironment() {
312
  } catch (error) {
313
  log('error', `❌ 浏览器环境测试失败: ${error.message}`);
314
  return { success: false, error: error.message };
 
 
 
 
 
315
  }
316
  }
317
 
318
- // 新增:测试函数 - 深度会话检测
319
  async function testSessionDetection() {
320
  log('info', '🔍 开始深度会话检测测试...');
321
 
322
- const tempBrowser = await puppeteer.launch({ headless: true });
323
- const tempPage = await tempBrowser.newPage();
324
 
325
  try {
326
- await tempPage.goto('https://toolbaz.com/writer/ai-writer', {
 
 
 
 
 
 
 
327
  waitUntil: 'networkidle2',
328
  timeout: 30000
329
  });
330
 
331
- // 增强的会话检测
332
- const sessionAnalysis = await tempPage.evaluate(() => {
333
  const analysis = {
334
  timestamp: Date.now(),
335
  url: window.location.href,
336
  title: document.title,
337
  elementsFound: [],
338
- forms: [],
339
  hiddenInputs: [],
340
- scripts: [],
341
  cookies: {},
342
  sessionStorage: {},
343
- localStorage: {},
344
- networkRequests: [],
345
- potentialSessions: []
346
  };
347
 
348
- // 分析所有元素
349
- const allElements = document.querySelectorAll('*');
350
  const sessionKeywords = ['session', 'sess', 'sid', 'token', 'csrf', 'auth'];
351
 
352
- allElements.forEach(elem => {
353
- const info = {
354
- tag: elem.tagName,
355
- id: elem.id || '',
356
- name: elem.name || '',
357
- className: elem.className || '',
358
- type: elem.type || '',
359
- value: elem.value || ''
360
- };
361
-
362
- const searchText = [info.id, info.name, info.className, info.value].join(' ').toLowerCase();
363
-
364
- if (sessionKeywords.some(keyword => searchText.includes(keyword))) {
365
- analysis.elementsFound.push(info);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  }
367
  });
368
 
369
- // 分析表单
370
- const forms = document.querySelectorAll('form');
371
- forms.forEach(form => {
372
- analysis.forms.push({
373
- action: form.action,
374
- method: form.method,
375
- id: form.id,
376
- className: form.className
377
- });
378
- });
379
-
380
  // 分析隐藏输入
381
  const hiddenInputs = document.querySelectorAll('input[type="hidden"]');
382
  hiddenInputs.forEach(input => {
383
- analysis.hiddenInputs.push({
384
- name: input.name,
385
- id: input.id,
386
- value: input.value.substring(0, 100) // 限制长度
387
- });
388
- });
389
-
390
- // 分析脚本
391
- const scripts = document.querySelectorAll('script');
392
- scripts.forEach(script => {
393
- if (script.src) {
394
- analysis.scripts.push(script.src);
395
  }
396
  });
397
 
398
- // 分析存储
399
- for (let i = 0; i < sessionStorage.length; i++) {
400
- const key = sessionStorage.key(i);
401
- analysis.sessionStorage[key] = sessionStorage.getItem(key);
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  }
403
 
404
- for (let i = 0; i < localStorage.length; i++) {
405
- const key = localStorage.key(i);
406
- analysis.localStorage[key] = localStorage.getItem(key);
 
 
 
 
 
 
 
407
  }
408
 
409
- // 分析cookies
410
- document.cookie.split(';').forEach(cookie => {
411
- const [name, value] = cookie.trim().split('=');
412
- if (name && value) {
413
- analysis.cookies[name] = value;
414
- }
415
- });
416
-
417
  return analysis;
418
  });
419
 
420
  log('verbose', `📊 会话分析结果:`);
421
  log('verbose', ` - 页面: ${sessionAnalysis.title}`);
422
  log('verbose', ` - 相关元素: ${sessionAnalysis.elementsFound.length}`);
423
- log('verbose', ` - 表单数量: ${sessionAnalysis.forms.length}`);
424
  log('verbose', ` - 隐藏输入: ${sessionAnalysis.hiddenInputs.length}`);
425
- log('verbose', ` - 脚本数量: ${sessionAnalysis.scripts.length}`);
426
  log('verbose', ` - Cookie数量: ${Object.keys(sessionAnalysis.cookies).length}`);
427
  log('verbose', ` - SessionStorage: ${Object.keys(sessionAnalysis.sessionStorage).length}`);
428
  log('verbose', ` - LocalStorage: ${Object.keys(sessionAnalysis.localStorage).length}`);
@@ -430,48 +480,63 @@ async function testSessionDetection() {
430
  if (sessionAnalysis.elementsFound.length > 0) {
431
  log('info', '🎯 找到潜在的会话相关元素:');
432
  sessionAnalysis.elementsFound.forEach(elem => {
433
- log('verbose', ` - ${elem.tag}#${elem.id}.${elem.className} (${elem.type}): ${elem.value.substring(0, 50)}`);
434
  });
435
  }
436
 
437
- await tempBrowser.close();
438
  return { success: true, analysis: sessionAnalysis };
439
 
440
  } catch (error) {
441
  log('error', `❌ 深度会话检测失败: ${error.message}`);
442
- await tempBrowser.close();
443
  return { success: false, error: error.message };
 
 
 
 
 
444
  }
445
  }
446
 
447
- // 新增:测试函数 - AJAX请求模拟
448
  async function testAjaxRequest() {
449
  log('info', '📡 开始AJAX请求模拟测试...');
450
 
451
- const tempBrowser = await puppeteer.launch({ headless: true });
452
- const tempPage = await tempBrowser.newPage();
453
 
454
  try {
455
- await tempPage.goto('https://toolbaz.com/writer/ai-writer', {
 
 
 
 
 
 
 
456
  waitUntil: 'networkidle2',
457
  timeout: 30000
458
  });
459
 
460
- // 设置请求监听
461
  const networkRequests = [];
462
- await tempPage.setRequestInterception(true);
463
- tempPage.on('request', request => {
464
- networkRequests.push({
465
- url: request.url(),
466
- method: request.method(),
467
- headers: request.headers(),
468
- postData: request.postData()
 
 
 
 
 
469
  });
470
- request.continue();
471
- });
 
472
 
473
  // 测试AJAX请求
474
- const ajaxTest = await tempPage.evaluate(async () => {
475
  return new Promise((resolve) => {
476
  const testData = {
477
  text: 'Hello, this is a test message',
@@ -485,7 +550,9 @@ async function testAjaxRequest() {
485
  method: 'POST',
486
  headers: {
487
  'Content-Type': 'application/x-www-form-urlencoded',
488
- 'Accept': '*/*'
 
 
489
  },
490
  body: new URLSearchParams(testData).toString()
491
  })
@@ -520,13 +587,16 @@ async function testAjaxRequest() {
520
 
521
  log('verbose', ` - 网络请求数量: ${networkRequests.length}`);
522
 
523
- await tempBrowser.close();
524
  return { success: ajaxTest.success, data: ajaxTest, networkRequests };
525
 
526
  } catch (error) {
527
  log('error', `❌ AJAX测试失败: ${error.message}`);
528
- await tempBrowser.close();
529
  return { success: false, error: error.message };
 
 
 
 
 
530
  }
531
  }
532
 
@@ -575,9 +645,9 @@ function showPerformanceStats() {
575
  log('info', ` - 平均响应时间: ${PERFORMANCE_STATS.averageResponseTime}ms`);
576
  }
577
 
578
- // 新增:综合测试函数
579
  async function runComprehensiveTests() {
580
- log('info', '🧪 开始综合测试套件...');
581
 
582
  const results = {
583
  configuration: null,
@@ -588,8 +658,10 @@ async function runComprehensiveTests() {
588
  overall: { passed: 0, failed: 0 }
589
  };
590
 
 
 
591
  try {
592
- // 1. 基本配置测试
593
  results.configuration = testBasicConfiguration();
594
  if (results.configuration.failed === 0) {
595
  results.overall.passed++;
@@ -597,7 +669,7 @@ async function runComprehensiveTests() {
597
  results.overall.failed++;
598
  }
599
 
600
- // 2. 网络连接性测试
601
  results.network = await testNetworkConnectivity();
602
  if (results.network.filter(r => r.status === 'success').length >= 3) {
603
  results.overall.passed++;
@@ -605,7 +677,7 @@ async function runComprehensiveTests() {
605
  results.overall.failed++;
606
  }
607
 
608
- // 3. 浏览器环境测试
609
  results.browser = await testBrowserEnvironment();
610
  if (results.browser.success) {
611
  results.overall.passed++;
@@ -613,7 +685,7 @@ async function runComprehensiveTests() {
613
  results.overall.failed++;
614
  }
615
 
616
- // 4. 会话检测测试
617
  results.session = await testSessionDetection();
618
  if (results.session.success) {
619
  results.overall.passed++;
@@ -621,7 +693,7 @@ async function runComprehensiveTests() {
621
  results.overall.failed++;
622
  }
623
 
624
- // 5. AJAX请求测试
625
  results.ajax = await testAjaxRequest();
626
  if (results.ajax.success) {
627
  results.overall.passed++;
@@ -629,7 +701,7 @@ async function runComprehensiveTests() {
629
  results.overall.failed++;
630
  }
631
 
632
- // 6. 前缀过滤测试
633
  results.prefixFiltering = await testPromptPrefixFiltering();
634
  if (results.prefixFiltering.failed === 0) {
635
  results.overall.passed++;
@@ -643,10 +715,23 @@ async function runComprehensiveTests() {
643
  log('warn', '⚠️ 部分测试失败,请检查相关配置');
644
  }
645
 
 
 
 
 
 
 
 
646
  return results;
647
 
648
  } catch (error) {
649
  log('error', `❌ 综合测试失败: ${error.message}`);
 
 
 
 
 
 
650
  return { ...results, error };
651
  }
652
  }
@@ -1875,69 +1960,413 @@ async function handleImageGenerations(req, res) {
1875
  });
1876
  }
1877
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1878
  async function handleModels(req, res) {
1879
  try {
1880
  log('info', '🔍 开始提取网站模型列表...');
1881
- const browser = await initBrowser();
1882
- const page = await browser.newPage();
1883
 
1884
- await page.goto('https://toolbaz.com/writer/ai-writer', { waitUntil: 'networkidle2', timeout: 30000 });
 
 
 
 
 
 
 
 
 
1885
 
1886
- // 等待并点击模型选择器
1887
- await page.waitForSelector('#selected-model', { timeout: 10000 });
1888
- await page.click('#selected-model');
1889
 
1890
- // 等待下拉列表出现
1891
- await page.waitForSelector('.model-dropdown .model-option', { timeout: 5000 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1892
 
1893
- // 提取所有模型选项
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1894
  const models = await page.evaluate(() => {
1895
- const modelOptions = document.querySelectorAll('.model-dropdown .model-option');
1896
  const modelList = [];
1897
 
1898
- modelOptions.forEach(option => {
1899
- const modelNameElement = option.querySelector('.model-name');
1900
- const modelIconElement = option.querySelector('.model-icon');
1901
-
1902
- if (modelNameElement) {
1903
- const name = modelNameElement.innerText.trim();
1904
- const icon = modelIconElement ? modelIconElement.src : '';
1905
- modelList.push({
1906
- id: name.toLowerCase().replace(/[^a-z0-9]/g, '-'),
1907
- name: name,
1908
- icon: icon,
1909
- object: 'model',
1910
- created: Math.floor(Date.now() / 1000),
1911
- owned_by: 'toolbaz'
1912
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1913
  }
1914
- });
1915
 
1916
  return modelList;
1917
  });
1918
 
1919
- await page.close();
 
 
1920
 
1921
- log('info', `✅ 成功提取 ${models.length} 个模型`);
1922
 
1923
- res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
1924
- res.end(JSON.stringify({
1925
- object: 'list',
1926
- data: models
1927
- }));
 
 
 
 
 
 
 
 
 
 
1928
 
1929
- } catch (error) {
1930
- log('error', '❌ 模型提取失败:', error.message);
1931
- // 降级到静态配置
1932
- const allModels = [...CONFIG.CHAT_MODELS, ...CONFIG.IMAGE_MODELS];
1933
- res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
1934
- res.end(JSON.stringify({
1935
- object: 'list',
1936
- data: allModels.map(id => ({ id, object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'toolbaz-2api' }))
1937
- }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1938
  }
1939
  }
1940
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1941
  // 新增:测试API端点
1942
  async function handleTests(req, res) {
1943
  if (!verifyAuth(req)) return sendError(res, 401, 'Unauthorized');
@@ -2003,7 +2432,11 @@ async function handleStatus(req, res) {
2003
  max_retries: CONFIG.MAX_RETRIES,
2004
  performance_monitoring: CONFIG.PERFORMANCE_MONITORING
2005
  },
2006
- browser: browser ? 'running' : 'stopped'
 
 
 
 
2007
  };
2008
 
2009
  res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
@@ -2015,6 +2448,27 @@ async function handleStatus(req, res) {
2015
  }
2016
  }
2017
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2018
  function handleUI(req, res) {
2019
  const html = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>${CONFIG.PROJECT_NAME}</title><style>:root{--bg:#121212;--panel:#1E1E1E;--border:#333;--text:#E0E0E0;--primary:#00FF9D;--secondary:#00BFA5;--error:#F44336;--success:#4CAF50;--warning:#FF9800}body{font-family:Consolas,monospace;background:var(--bg);color:var(--text);margin:0;height:100vh;display:flex;overflow:hidden;font-size:13px}.sidebar{width:320px;background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:10px;gap:10px}.main{flex:1;display:flex;flex-direction:column}.log-panel{height:40%;border-top:1px solid var(--border);background:#000;overflow-y:auto;padding:10px}.box{background:#252525;padding:15px;border-radius:6px;border:1px solid var(--border)}.label{font-size:11px;color:#888;display:block;margin-bottom:5px;text-transform:uppercase}input,select,textarea{width:100%;background:#333;border:1px solid #444;color:#fff;padding:8px;border-radius:4px;margin-bottom:10px;box-sizing:border-box;font-family:inherit}button{width:100%;padding:10px;background:var(--primary);border:none;border-radius:4px;font-weight:bold;cursor:pointer;color:#000;margin-bottom:5px;transition:all 0.2s}button:disabled{background:#555;cursor:not-allowed}button.secondary{background:#333;color:#fff;border:1px solid #555}button.danger{background:var(--error);color:#fff}button.warning{background:var(--warning);color:#000}.chat-window{flex:1;padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:15px}.msg{max-width:85%;padding:10px 15px;border-radius:6px;line-height:1.5;word-wrap:break-word}.msg.user{align-self:flex-end;background:#333;color:#fff}.msg.ai{align-self:flex-start;background:#1a1a1a;border:1px solid #333;width:100%}.msg.img img{max-width:100%;border-radius:4px}.log-entry{margin-bottom:8px;border-bottom:1px solid #222;padding-bottom:8px}.log-time{color:#666;margin-right:10px}.log-data{color:#ccc;white-space:pre-wrap;word-break:break-all;margin-top:4px;display:block}.tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:10px}.tab{flex:1;text-align:center;padding:8px;cursor:pointer;color:#888;border-bottom:2px solid transparent;transition:all 0.2s}.tab.active{color:var(--primary);border-bottom-color:var(--primary)}.status-item{display:flex;justify-content:space-between;margin-bottom:5px;font-size:12px}.status-value{color:var(--primary)}.test-result{margin:5px 0;padding:5px;border-radius:3px;font-size:11px}.test-pass{background:var(--success);color:#000}.test-fail{background:var(--error);color:#fff}.performance-grid{display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-top:10px}.perf-item{text-align:center;padding:5px;background:#333;border-radius:3px}.perf-value{font-size:16px;color:var(--primary)}.perf-label{font-size:10px;color:#888}</style></head><body><div class="sidebar"><div class="box"><h2 style="margin:0;color:var(--primary)">${CONFIG.PROJECT_NAME}</h2><span style="font-size:11px;color:#666">v${CONFIG.PROJECT_VERSION} | Enhanced Testing</span></div><div class="box"><div class="tabs"><div class="tab active" onclick="switchMode('chat')">聊天</div><div class="tab" onclick="switchMode('image')">绘图</div><div class="tab" onclick="switchMode('test')">测试</div></div><div id="chat-controls"><span class="label">Model</span><select id="chat-model">${CONFIG.CHAT_MODELS.map(m => `<option value="${m}">${m}</option>`).join('')}</select><span class="label">Prompt</span><textarea id="chat-prompt" rows="5" placeholder="输入内容...">你好,请介绍一下你自己</textarea></div><div id="image-controls" style="display:none"><span class="label">Model</span><select id="image-model">${CONFIG.IMAGE_MODELS.map(m => `<option value="${m}">${m}</option>`).join('')}</select><span class="label">Prompt</span><textarea id="image-prompt" rows="5" placeholder="输入图片描述...">A futuristic city at sunset</textarea></div><div id="test-controls" style="display:none"><span class="label">测试类型</span><select id=\"test-type\"><option value=\"comprehensive\">综合测试</option><option value=\"config\">配置测试</option><option value=\"network\">网络测试</option><option value=\"browser\">浏览器测试</option><option value=\"session\">会话测试</option><option value=\"ajax\">AJAX测试</option><option value=\"prefix\">前缀过滤测试</option></select><button class="warning" onclick="runTest()">🧪 运行测试</button></div><button id="btn-send" onclick="handleSend()">发送请求</button></div><div class="box"><div class="status-item"><span>服务状态:</span><span class="status-value" id="service-status">检查中...</span></div><div class="status-item"><span>浏览器:</span><span class="status-value" id="browser-status">未知</span></div><div class="status-item"><span>总请求:</span><span class="status-value" id="total-requests">0</span></div><div class="status-item"><span>成功率:</span><span class="status-value" id="success-rate">0%</span></div><div class="performance-grid"><div class="perf-item"><div class="perf-value" id="ajax-success">0</div><div class="perf-label">AJAX成功</div></div><div class="perf-item"><div class="perf-value" id="ui-fallback">0</div><div class="perf-label">UI回退</div></div><div class="perf-item"><div class="perf-value" id="avg-response">0ms</div><div class="perf-label">平均响应</div></div><div class="perf-item"><div class="perf-value" id="uptime">0m</div><div class="perf-label">运行时间</div></div></div></div><div class="box" style="margin-top:auto"><button class="secondary" onclick="copyLogs()">📋 复制日志</button><button class="secondary" onclick="clearLogs()">🗑️ 清空日志</button><button class="secondary" onclick="refreshStatus()">🔄 刷新状态</button></div></div><div class="main"><div class="chat-window" id="chat"><div class="msg ai">🎉 ${CONFIG.PROJECT_NAME} v${CONFIG.PROJECT_VERSION} 已就绪<br><br>✨ 新功能:<br>• 🧪 增强的测试套件<br>• 📊 性能监控和统计<br>• 🔍 深度调试信息<br>• 🔄 智能重试机制<br><br>请选择聊天或测试模式开始使用!</div></div><div class="log-panel" id="log-container"></div></div><script>const API_KEY="${CONFIG.API_MASTER_KEY}";const ORIGIN=window.location.origin;let currentMode='chat';let statusInterval;function switchMode(mode){currentMode=mode;document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));event.target.classList.add('active');document.getElementById('chat-controls').style.display=mode==='chat'?'block':'none';document.getElementById('image-controls').style.display=mode==='image'?'block':'none';document.getElementById('test-controls').style.display=mode==='test'?'block':'none';const sendBtn=document.getElementById('btn-send');sendBtn.style.display=mode==='test'?'none':'block';}function appendLog(msg,type='info'){const div=document.createElement('div');div.className='log-entry';let color='#ccc';switch(type){case 'error':color='#F44336';break;case 'warn':color='#FF9800';break;case 'success':color='#4CAF50';break;case 'info':color='#2196F3';break;}div.innerHTML=\`<span class="log-time">\${new Date().toLocaleTimeString()}</span><span class="log-data" style="color:\${color}">\${msg}</span>\`;document.getElementById('log-container').appendChild(div);div.scrollIntoView()}function clearLogs(){document.getElementById('log-container').innerHTML=''}function copyLogs(){const logs=Array.from(document.querySelectorAll('.log-entry')).map(e=>e.innerText).join('\\n');navigator.clipboard.writeText(logs).then(()=>appendLog('日志已复制到剪贴板','success'))}function appendMsg(role,content){const div=document.createElement('div');div.className=\`msg \${role}\`;div.innerHTML=content;document.getElementById('chat').appendChild(div);div.scrollIntoView({behavior:"smooth"});return div}async function handleSend(){const btn=document.getElementById('btn-send');btn.disabled=true;clearLogs();appendLog('🚀 开始请求...');if(currentMode==='chat'){const prompt=document.getElementById('chat-prompt').value;const model=document.getElementById('chat-model').value;if(!prompt){btn.disabled=false;return}appendMsg('user',prompt);const aiMsg=appendMsg('ai','...');let fullText='';try{appendLog('📡 发送到浏览器...');const res=await fetch(ORIGIN+'/v1/chat/completions',{method:'POST',headers:{'Authorization':'Bearer '+API_KEY,'Content-Type':'application/json'},body:JSON.stringify({model,messages:[{role:'user',content:prompt}],stream:true})});appendLog('✅ 接收响应...');const reader=res.body.getReader();const decoder=new TextDecoder();while(true){const{done,value}=await reader.read();if(done)break;const chunk=decoder.decode(value);const lines=chunk.split('\\n');for(const line of lines){if(line.startsWith('data: ')){const dataStr=line.slice(6);if(dataStr==='[DONE]')break;try{const json=JSON.parse(dataStr);if(json.choices&&json.choices[0].delta.content){fullText+=json.choices[0].delta.content;aiMsg.innerText=fullText}}catch(e){}}}}appendLog('🎉 完成!','success');refreshStatus();}catch(e){aiMsg.innerText+='\\n[错误]: '+e.message;appendLog('❌ 错误: '+e.message,'error')}}else{const prompt=document.getElementById('image-prompt').value;const model=document.getElementById('image-model').value;if(!prompt){btn.disabled=false;return}appendMsg('user',prompt);const aiMsg=appendMsg('ai','生成中...');try{appendLog('🎨 发送图片请求...');const res=await fetch(ORIGIN+'/v1/images/generations',{method:'POST',headers:{'Authorization':'Bearer '+API_KEY,'Content-Type':'application/json'},body:JSON.stringify({model,prompt})});const data=await res.json();if(data.error)throw new Error(data.error.message);const imgUrl=data.data[0].url;aiMsg.innerHTML=\`<img src="\${imgUrl}" onclick="window.open(this.src)">\`;aiMsg.className='msg ai img';appendLog('✅ 图片生成成功!','success');refreshStatus();}catch(e){aiMsg.innerText='生成失败: '+e.message;appendLog('❌ 错误: '+e.message,'error')}}btn.disabled=false}async function runTest(){const testType=document.getElementById('test-type').value;appendLog(\`🧪 开始运行\${testType}测试...\`,'info');const testMsg=appendMsg('ai','<div id="test-results">正在运行测试...</div>');try{const res=await fetch(ORIGIN+'/v1/tests?type='+testType,{headers:{'Authorization':'Bearer '+API_KEY}});const data=await res.json();let html='<h3>测试结果</h3>';if(data.overall){html+=\`<p><strong>综合测试:</strong> \${data.overall.passed}/\${data.overall.passed+data.overall.failed} 通过</p>\`;if(data.configuration){html+=\`<div class="test-result test-pass">配置测试: \${data.configuration.passed} 通过, \${data.configuration.failed} 失败</div>\`;}}if(data.success===false){html+=\`<div class="test-result test-fail">测试失败: \${data.error}</div>\`;}else if(data.success){html+=\`<div class="test-result test-pass">测试通过</div>\`;}if(data.analysis){html+=\`<p><strong>会话分析:</strong> 找到 \${data.analysis.elementsFound.length} 个相关元素</p>\`;}if(data.length>0){html+='<h4>详细结果:</h4><ul>';data.forEach(result=>{const status=result.status==='success'?'pass':'fail';html+=\`<li class="test-result test-\${status}">\${result.name}: \${result.status}</li>\`;});html+='</ul>';}testMsg.querySelector('#test-results').innerHTML=html;appendLog('测试完成','success');refreshStatus();}catch(e){testMsg.innerHTML='<div class="test-result test-fail">测试执行失败: '+e.message+'</div>';appendLog('测试执行失败: '+e.message,'error');}}async function refreshStatus(){try{const res=await fetch(ORIGIN+'/v1/status',{headers:{'Authorization':'Bearer '+API_KEY}});const data=await res.json();document.getElementById('service-status').textContent='运行中';document.getElementById('browser-status').textContent=data.browser||'未知';document.getElementById('total-requests').textContent=data.performance.totalRequests||0;const successRate=data.performance.totalRequests>0?Math.round(data.performance.successfulRequests/data.performance.totalRequests*100):0;document.getElementById('success-rate').textContent=successRate+'%';document.getElementById('ajax-success').textContent=data.performance.ajaxSuccess||0;document.getElementById('ui-fallback').textContent=data.performance.uiFallback||0;document.getElementById('avg-response').textContent=(data.performance.averageResponseTime||0)+'ms';document.getElementById('uptime').textContent=Math.floor(data.uptime/60)+'m';}catch(e){appendLog('状态刷新失败: '+e.message,'error');}}function init(){refreshStatus();statusInterval=setInterval(refreshStatus,30000);}init();</script></body></html>`;
2020
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
@@ -2048,6 +2502,7 @@ const server = http.createServer(async (req, res) => {
2048
  // 新增的测试和状态端点
2049
  if (url.pathname === '/v1/tests') return await handleTests(req, res);
2050
  if (url.pathname === '/v1/status') return await handleStatus(req, res);
 
2051
 
2052
  sendError(res, 404, 'Not Found');
2053
  });
@@ -2103,8 +2558,9 @@ server.listen(CONFIG.PORT, async () => {
2103
  log('info', ' - GET / : Web界面');
2104
  log('info', ' - POST /v1/chat/completions : 聊天API');
2105
  log('info', ' - POST /v1/images/generations : 图片生成API');
2106
- log('info', ' - GET /v1/models : 模型列表API');
2107
- log('info', ' - GET /v1/status : 服务状态API');
 
2108
  log('info', ' - GET /v1/tests : 测试API (?type=config|network|browser|session|ajax|comprehensive)');
2109
 
2110
  // 启动性能统计定时显示
 
161
  return { passed, failed };
162
  }
163
 
164
+ // 修改:测试函数 - 网络连接性(使用单个浏览器实例)
165
  async function testNetworkConnectivity() {
166
+ log('info', '🌐 开始网络连接性测试(使用单个浏览器实例)...');
167
 
168
  const testUrls = [
169
  { name: '主站', url: 'https://toolbaz.com' },
 
173
  ];
174
 
175
  const results = [];
176
+ let testBrowser = null;
177
+ let testPage = null;
178
 
179
+ try {
180
+ // 启动单个浏览器实例
181
+ testBrowser = await puppeteer.launch({
182
+ headless: true,
183
+ args: [
184
+ '--no-sandbox',
185
+ '--disable-setuid-sandbox',
186
+ '--disable-dev-shm-usage',
187
+ '--disable-gpu'
188
+ ]
189
+ });
190
+ testPage = await testBrowser.newPage();
191
+
192
+ // 设置更长的超时时间以应对网络问题
193
+ testPage.setDefaultTimeout(30000);
194
+
195
+ for (const test of testUrls) {
196
+ try {
197
+ const startTime = Date.now();
198
+
199
+ // 重用同一个页面进行测试
200
+ await testPage.goto(test.url, {
201
+ waitUntil: 'networkidle2',
202
+ timeout: 25000 // 增加到25秒
203
+ });
204
+
205
+ const responseTime = Date.now() - startTime;
206
+ const title = await testPage.title();
207
+
208
+ results.push({
209
+ name: test.name,
210
+ url: test.url,
211
+ status: 'success',
212
+ responseTime,
213
+ title: title.substring(0, 50)
214
+ });
215
+
216
+ log('verbose', `✅ ${test.name}: ${responseTime}ms - ${title.substring(0, 30)}...`);
217
+
218
+ // 在测试之间添加短暂延迟,避免过于频繁的请求
219
+ await new Promise(resolve => setTimeout(resolve, 1000));
220
+
221
+ } catch (error) {
222
+ results.push({
223
+ name: test.name,
224
+ url: test.url,
225
+ status: 'failed',
226
+ error: error.message
227
+ });
228
+ log('error', `❌ ${test.name}: ${error.message}`);
229
+
230
+ // 错误后也短暂延迟
231
+ await new Promise(resolve => setTimeout(resolve, 500));
232
+ }
233
+ }
234
+
235
+ } catch (error) {
236
+ log('error', `❌ 网络测试初始化失败: ${error.message}`);
237
+ } finally {
238
+ // 清理资源
239
+ if (testPage) {
240
+ await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
241
+ }
242
+ if (testBrowser) {
243
+ await testBrowser.close().catch(e => log('warn', `浏览器关闭失败: ${e.message}`));
244
  }
245
  }
246
 
 
295
  return { results, passed: passCount, failed: results.length - passCount };
296
  }
297
 
298
+ // 修改:测试函数 - 浏览器环境(使用全局浏览器实例)
299
  async function testBrowserEnvironment() {
300
  log('info', '🌍 开始浏览器环境测试...');
301
 
302
+ let testPage = null;
303
+
304
  try {
305
+ // 尝试使用现有的浏览器实例,如果没有则创建
306
+ const testBrowser = browser || await initBrowser();
307
+ testPage = await testBrowser.newPage();
308
+
309
+ // 设置更长的超时时间
310
+ testPage.setDefaultTimeout(20000);
311
 
312
  // 测试基本功能
313
+ await testPage.goto('https://example.com', { waitUntil: 'networkidle2', timeout: 15000 });
314
 
315
  const browserInfo = await testPage.evaluate(() => {
316
  return {
 
338
  }
339
  });
340
 
 
 
341
  log('verbose', '✅ 浏览器环境测试通过');
342
  log('verbose', '📋 浏览器信息:', browserInfo);
343
  log('verbose', '📋 JavaScript功能:', jsTest);
 
347
  } catch (error) {
348
  log('error', `❌ 浏览器环境测试失败: ${error.message}`);
349
  return { success: false, error: error.message };
350
+ } finally {
351
+ // 清理页面资源
352
+ if (testPage) {
353
+ await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
354
+ }
355
  }
356
  }
357
 
358
+ // 修改:测试函数 - 深度会话检测(使用全局浏览器实例)
359
  async function testSessionDetection() {
360
  log('info', '🔍 开始深度会话检测测试...');
361
 
362
+ let testPage = null;
 
363
 
364
  try {
365
+ // 使用现有的浏览器实例或创建新的
366
+ const testBrowser = browser || await initBrowser();
367
+ testPage = await testBrowser.newPage();
368
+
369
+ // 设置更长的超时时间
370
+ testPage.setDefaultTimeout(30000);
371
+
372
+ await testPage.goto('https://toolbaz.com/writer/ai-writer', {
373
  waitUntil: 'networkidle2',
374
  timeout: 30000
375
  });
376
 
377
+ // 简化的会话检测(减少资源消耗)
378
+ const sessionAnalysis = await testPage.evaluate(() => {
379
  const analysis = {
380
  timestamp: Date.now(),
381
  url: window.location.href,
382
  title: document.title,
383
  elementsFound: [],
 
384
  hiddenInputs: [],
 
385
  cookies: {},
386
  sessionStorage: {},
387
+ localStorage: {}
 
 
388
  };
389
 
390
+ // 专注于关键元素,避免过度遍历
 
391
  const sessionKeywords = ['session', 'sess', 'sid', 'token', 'csrf', 'auth'];
392
 
393
+ // 只检查可能包含会话信息的元素
394
+ const relevantSelectors = [
395
+ 'input[type="hidden"]',
396
+ 'input[name*="session"]',
397
+ 'input[name*="token"]',
398
+ 'input[id*="session"]',
399
+ 'input[id*="token"]',
400
+ '[data-session]',
401
+ '[data-token]'
402
+ ];
403
+
404
+ relevantSelectors.forEach(selector => {
405
+ try {
406
+ const elements = document.querySelectorAll(selector);
407
+ elements.forEach(elem => {
408
+ const info = {
409
+ tag: elem.tagName,
410
+ id: elem.id || '',
411
+ name: elem.name || '',
412
+ value: elem.value || ''
413
+ };
414
+
415
+ const searchText = [info.id, info.name, info.value].join(' ').toLowerCase();
416
+
417
+ if (sessionKeywords.some(keyword => searchText.includes(keyword))) {
418
+ analysis.elementsFound.push(info);
419
+ }
420
+ });
421
+ } catch (e) {
422
+ // 忽略选择器错误
423
  }
424
  });
425
 
 
 
 
 
 
 
 
 
 
 
 
426
  // 分析隐藏输入
427
  const hiddenInputs = document.querySelectorAll('input[type="hidden"]');
428
  hiddenInputs.forEach(input => {
429
+ if (input.name || input.value) {
430
+ analysis.hiddenInputs.push({
431
+ name: input.name,
432
+ id: input.id,
433
+ value: input.value.substring(0, 100)
434
+ });
 
 
 
 
 
 
435
  }
436
  });
437
 
438
+ // 简化存储分析
439
+ try {
440
+ for (let i = 0; i < Math.min(sessionStorage.length, 10); i++) {
441
+ const key = sessionStorage.key(i);
442
+ if (key && sessionKeywords.some(k => key.toLowerCase().includes(k))) {
443
+ analysis.sessionStorage[key] = sessionStorage.getItem(key);
444
+ }
445
+ }
446
+
447
+ for (let i = 0; i < Math.min(localStorage.length, 10); i++) {
448
+ const key = localStorage.key(i);
449
+ if (key && sessionKeywords.some(k => key.toLowerCase().includes(k))) {
450
+ analysis.localStorage[key] = localStorage.getItem(key);
451
+ }
452
+ }
453
+ } catch (e) {
454
+ // 忽略存储访问错误
455
  }
456
 
457
+ // 简化cookie分析
458
+ try {
459
+ document.cookie.split(';').forEach(cookie => {
460
+ const [name, value] = cookie.trim().split('=');
461
+ if (name && value && sessionKeywords.some(k => name.toLowerCase().includes(k))) {
462
+ analysis.cookies[name] = value;
463
+ }
464
+ });
465
+ } catch (e) {
466
+ // 忽略cookie访问错误
467
  }
468
 
 
 
 
 
 
 
 
 
469
  return analysis;
470
  });
471
 
472
  log('verbose', `📊 会话分析结果:`);
473
  log('verbose', ` - 页面: ${sessionAnalysis.title}`);
474
  log('verbose', ` - 相关元素: ${sessionAnalysis.elementsFound.length}`);
 
475
  log('verbose', ` - 隐藏输入: ${sessionAnalysis.hiddenInputs.length}`);
 
476
  log('verbose', ` - Cookie数量: ${Object.keys(sessionAnalysis.cookies).length}`);
477
  log('verbose', ` - SessionStorage: ${Object.keys(sessionAnalysis.sessionStorage).length}`);
478
  log('verbose', ` - LocalStorage: ${Object.keys(sessionAnalysis.localStorage).length}`);
 
480
  if (sessionAnalysis.elementsFound.length > 0) {
481
  log('info', '🎯 找到潜在的会话相关元素:');
482
  sessionAnalysis.elementsFound.forEach(elem => {
483
+ log('verbose', ` - ${elem.tag}#${elem.id} (${elem.name}): ${elem.value.substring(0, 50)}`);
484
  });
485
  }
486
 
 
487
  return { success: true, analysis: sessionAnalysis };
488
 
489
  } catch (error) {
490
  log('error', `❌ 深度会话检测失败: ${error.message}`);
 
491
  return { success: false, error: error.message };
492
+ } finally {
493
+ // 清理页面资源
494
+ if (testPage) {
495
+ await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
496
+ }
497
  }
498
  }
499
 
500
+ // 修改:测试函数 - AJAX请求模拟(使用全局浏览器实例)
501
  async function testAjaxRequest() {
502
  log('info', '📡 开始AJAX请求模拟测试...');
503
 
504
+ let testPage = null;
 
505
 
506
  try {
507
+ // 使用现有的浏览器实例或创建新的
508
+ const testBrowser = browser || await initBrowser();
509
+ testPage = await testBrowser.newPage();
510
+
511
+ // 设置更长的超时时间
512
+ testPage.setDefaultTimeout(30000);
513
+
514
+ await testPage.goto('https://toolbaz.com/writer/ai-writer', {
515
  waitUntil: 'networkidle2',
516
  timeout: 30000
517
  });
518
 
519
+ // 简化的请求监听
520
  const networkRequests = [];
521
+ try {
522
+ await testPage.setRequestInterception(true);
523
+ testPage.on('request', request => {
524
+ if (request.url().includes('writing.php')) {
525
+ networkRequests.push({
526
+ url: request.url(),
527
+ method: request.method(),
528
+ headers: request.headers(),
529
+ postData: request.postData()
530
+ });
531
+ }
532
+ request.continue();
533
  });
534
+ } catch (e) {
535
+ log('warn', `请求拦截设置失败,继续测试: ${e.message}`);
536
+ }
537
 
538
  // 测试AJAX请求
539
+ const ajaxTest = await testPage.evaluate(async () => {
540
  return new Promise((resolve) => {
541
  const testData = {
542
  text: 'Hello, this is a test message',
 
550
  method: 'POST',
551
  headers: {
552
  'Content-Type': 'application/x-www-form-urlencoded',
553
+ 'Accept': '*/*',
554
+ 'Origin': 'https://toolbaz.com',
555
+ 'Referer': 'https://toolbaz.com/writer/ai-writer'
556
  },
557
  body: new URLSearchParams(testData).toString()
558
  })
 
587
 
588
  log('verbose', ` - 网络请求数量: ${networkRequests.length}`);
589
 
 
590
  return { success: ajaxTest.success, data: ajaxTest, networkRequests };
591
 
592
  } catch (error) {
593
  log('error', `❌ AJAX测试失败: ${error.message}`);
 
594
  return { success: false, error: error.message };
595
+ } finally {
596
+ // 清理页面资源
597
+ if (testPage) {
598
+ await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
599
+ }
600
  }
601
  }
602
 
 
645
  log('info', ` - 平均响应时间: ${PERFORMANCE_STATS.averageResponseTime}ms`);
646
  }
647
 
648
+ // 修改:综合测试函数(优化浏览器资源管理)
649
  async function runComprehensiveTests() {
650
+ log('info', '🧪 开始综合测试套件(优化版)...');
651
 
652
  const results = {
653
  configuration: null,
 
658
  overall: { passed: 0, failed: 0 }
659
  };
660
 
661
+ let sharedBrowser = null;
662
+
663
  try {
664
+ // 1. 基本配置测试(不需要浏览器)
665
  results.configuration = testBasicConfiguration();
666
  if (results.configuration.failed === 0) {
667
  results.overall.passed++;
 
669
  results.overall.failed++;
670
  }
671
 
672
+ // 2. 网络连接性测试(已优化为使用单个浏览器)
673
  results.network = await testNetworkConnectivity();
674
  if (results.network.filter(r => r.status === 'success').length >= 3) {
675
  results.overall.passed++;
 
677
  results.overall.failed++;
678
  }
679
 
680
+ // 3. 浏览器环境测试(使用全局浏览器)
681
  results.browser = await testBrowserEnvironment();
682
  if (results.browser.success) {
683
  results.overall.passed++;
 
685
  results.overall.failed++;
686
  }
687
 
688
+ // 4. 会话检测测试(使用全局浏览器)
689
  results.session = await testSessionDetection();
690
  if (results.session.success) {
691
  results.overall.passed++;
 
693
  results.overall.failed++;
694
  }
695
 
696
+ // 5. AJAX请求测试(使用全局浏览器)
697
  results.ajax = await testAjaxRequest();
698
  if (results.ajax.success) {
699
  results.overall.passed++;
 
701
  results.overall.failed++;
702
  }
703
 
704
+ // 6. 前缀过滤测试(不需要浏览器)
705
  results.prefixFiltering = await testPromptPrefixFiltering();
706
  if (results.prefixFiltering.failed === 0) {
707
  results.overall.passed++;
 
715
  log('warn', '⚠️ 部分测试失败,请检查相关配置');
716
  }
717
 
718
+ // 添加资源使用统计
719
+ const browserInstance = browser || sharedBrowser;
720
+ if (browserInstance) {
721
+ const pages = await browserInstance.pages().catch(() => []);
722
+ log('info', `📊 浏览器资源统计: ${pages.length} 个页面`);
723
+ }
724
+
725
  return results;
726
 
727
  } catch (error) {
728
  log('error', `❌ 综合测试失败: ${error.message}`);
729
+
730
+ // 确保清理共享浏览器资源
731
+ if (sharedBrowser) {
732
+ await sharedBrowser.close().catch(e => log('warn', `共享浏览器关闭失败: ${e.message}`));
733
+ }
734
+
735
  return { ...results, error };
736
  }
737
  }
 
1960
  });
1961
  }
1962
 
1963
+ // 模型缓存(避免频繁重新提取)
1964
+ let modelCache = {
1965
+ data: null,
1966
+ timestamp: null,
1967
+ expiresAt: null,
1968
+ isValid: function() {
1969
+ return this.data && this.expiresAt && Date.now() < this.expiresAt;
1970
+ },
1971
+ set: function(models, ttlMinutes = 60) {
1972
+ this.data = models;
1973
+ this.timestamp = Date.now();
1974
+ this.expiresAt = Date.now() + (ttlMinutes * 60 * 1000);
1975
+ log('info', `🔐 模型列表已缓存,有效期 ${ttlMinutes} 分钟`);
1976
+ },
1977
+ clear: function() {
1978
+ this.data = null;
1979
+ this.timestamp = null;
1980
+ this.expiresAt = null;
1981
+ log('info', '🔐 模型缓存已清空');
1982
+ }
1983
+ };
1984
+
1985
  async function handleModels(req, res) {
1986
  try {
1987
  log('info', '🔍 开始提取网站模型列表...');
 
 
1988
 
1989
+ // 检查缓存
1990
+ if (modelCache.isValid()) {
1991
+ log('info', `✅ 使用缓存模型列表 (${modelCache.data.length} 个模型)`);
1992
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
1993
+ res.end(JSON.stringify({
1994
+ object: 'list',
1995
+ data: modelCache.data
1996
+ }));
1997
+ return;
1998
+ }
1999
 
2000
+ // 尝试多种方法获取模型列表
2001
+ let models = null;
2002
+ let extractionMethod = '';
2003
 
2004
+ // 方法1: 尝试访问网站获取最新模型
2005
+ try {
2006
+ log('info', '🌐 方法1: 从网站提取模型列表...');
2007
+ models = await extractModelsFromWebsite();
2008
+ extractionMethod = 'website';
2009
+ } catch (websiteError) {
2010
+ log('warn', `⚠️ 网站提取失败: ${websiteError.message}`);
2011
+
2012
+ // 方法2: 尝试AJAX方式获取模型
2013
+ try {
2014
+ log('info', '📡 方法2: 尝试AJAX方式获取模型...');
2015
+ models = await extractModelsViaAjax();
2016
+ extractionMethod = 'ajax';
2017
+ } catch (ajaxError) {
2018
+ log('warn', `⚠️ AJAX提取失败: ${ajaxError.message}`);
2019
+
2020
+ // 方法3: 使用增强的静态配置
2021
+ try {
2022
+ log('info', '🔧 方法3: 使用增强静态配置...');
2023
+ models = await getEnhancedStaticModels();
2024
+ extractionMethod = 'enhanced-static';
2025
+ } catch (staticError) {
2026
+ log('error', `❌ 所有方法都失败了,使用基础静态配置: ${staticError.message}`);
2027
+ models = getBasicStaticModels();
2028
+ extractionMethod = 'basic-static';
2029
+ }
2030
+ }
2031
+ }
2032
+
2033
+ // 更新缓存
2034
+ if (models && models.length > 0) {
2035
+ modelCache.set(models, 60); // 缓存60分钟
2036
+
2037
+ log('info', `✅ 成功提取 ${models.length} 个模型 (方法: ${extractionMethod})`);
2038
+
2039
+ // 记录提取到的模型(调试用)
2040
+ if (CONFIG.DEBUG_LEVEL === 'verbose') {
2041
+ models.forEach((model, index) => {
2042
+ log('verbose', ` ${index + 1}. ${model.name} (${model.id})`);
2043
+ });
2044
+ }
2045
+
2046
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
2047
+ res.end(JSON.stringify({
2048
+ object: 'list',
2049
+ data: models,
2050
+ metadata: {
2051
+ extraction_method: extractionMethod,
2052
+ extracted_at: new Date().toISOString(),
2053
+ total_models: models.length
2054
+ }
2055
+ }));
2056
+ } else {
2057
+ throw new Error('没有获取到任何模型数据');
2058
+ }
2059
 
2060
+ } catch (error) {
2061
+ log('error', `❌ 模型提取完全失败: ${error.message}`);
2062
+
2063
+ // 最后的兜底策略
2064
+ const fallbackModels = getBasicStaticModels();
2065
+
2066
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
2067
+ res.end(JSON.stringify({
2068
+ object: 'list',
2069
+ data: fallbackModels,
2070
+ metadata: {
2071
+ extraction_method: 'fallback',
2072
+ error: error.message,
2073
+ extracted_at: new Date().toISOString()
2074
+ }
2075
+ }));
2076
+ }
2077
+ }
2078
+
2079
+ // 新增:从网站提取模型的专用函数
2080
+ async function extractModelsFromWebsite() {
2081
+ const browser = await initBrowser();
2082
+ const page = await browser.newPage();
2083
+
2084
+ try {
2085
+ // 设置更长的超时时间
2086
+ page.setDefaultTimeout(30000);
2087
+
2088
+ log('info', '🌐 访问写作页面...');
2089
+ await page.goto('https://toolbaz.com/writer/ai-writer', {
2090
+ waitUntil: 'networkidle2',
2091
+ timeout: 30000
2092
+ });
2093
+
2094
+ // 等待页面加载
2095
+ await page.waitForTimeout(2000); // 给页面一些加载时间
2096
+
2097
+ log('info', '🔍 查找模型选择器...');
2098
+
2099
+ // 尝试多种选择器
2100
+ const modelSelectors = [
2101
+ '#selected-model',
2102
+ '.model-selector',
2103
+ '[data-model]',
2104
+ '.model-dropdown-trigger'
2105
+ ];
2106
+
2107
+ let selectorFound = null;
2108
+ for (const selector of modelSelectors) {
2109
+ try {
2110
+ await page.waitForSelector(selector, { timeout: 5000 });
2111
+ selectorFound = selector;
2112
+ log('info', `✅ 找到模型选择器: ${selector}`);
2113
+ break;
2114
+ } catch (e) {
2115
+ continue;
2116
+ }
2117
+ }
2118
+
2119
+ if (!selectorFound) {
2120
+ throw new Error('未找到模型选择器');
2121
+ }
2122
+
2123
+ // 尝试点击选择器
2124
+ try {
2125
+ await page.click(selectorFound);
2126
+ log('info', '✅ 成功点击模型选择器');
2127
+ } catch (e) {
2128
+ log('warn', `⚠️ 点击选择器失败,尝试直接查找模型: ${e.message}`);
2129
+ }
2130
+
2131
+ // 等待下拉列表或模型列表出现
2132
+ await page.waitForTimeout(1000);
2133
+
2134
+ // 查找模型选项
2135
  const models = await page.evaluate(() => {
 
2136
  const modelList = [];
2137
 
2138
+ // 尝试多种模型选项选择器
2139
+ const modelSelectors = [
2140
+ '.model-dropdown .model-option',
2141
+ '.model-list .model-item',
2142
+ '[data-model-option]',
2143
+ '.model-selector option',
2144
+ 'select[name="model"] option',
2145
+ '.ai-models .model',
2146
+ '.available-models .model-item'
2147
+ ];
2148
+
2149
+ for (const selector of modelSelectors) {
2150
+ try {
2151
+ const elements = document.querySelectorAll(selector);
2152
+ if (elements.length > 0) {
2153
+ log(`✅ 使用选择器 ${selector} 找到 ${elements.length} 个模型选项`);
2154
+
2155
+ elements.forEach((element, index) => {
2156
+ let name = '';
2157
+ let id = '';
2158
+
2159
+ // 尝试获取模型名称
2160
+ const nameElement = element.querySelector('.model-name') ||
2161
+ element.querySelector('.name') ||
2162
+ element.querySelector('span') ||
2163
+ element;
2164
+
2165
+ name = nameElement.innerText || nameElement.textContent ||
2166
+ nameElement.value || nameElement.getAttribute('data-model') || '';
2167
+
2168
+ name = name.trim();
2169
+
2170
+ if (name && name.length > 0) {
2171
+ id = name.toLowerCase()
2172
+ .replace(/[^a-z0-9\s]/g, '')
2173
+ .replace(/\s+/g, '-')
2174
+ .substring(0, 50);
2175
+
2176
+ modelList.push({
2177
+ id: id || `model-${index}`,
2178
+ name: name,
2179
+ icon: element.querySelector('.model-icon, .icon')?.src || '',
2180
+ object: 'model',
2181
+ created: Math.floor(Date.now() / 1000),
2182
+ owned_by: 'toolbaz'
2183
+ });
2184
+ }
2185
+ });
2186
+
2187
+ // 如果找到了模型,就不再尝试其他选择器
2188
+ if (modelList.length > 0) {
2189
+ break;
2190
+ }
2191
+ }
2192
+ } catch (e) {
2193
+ continue;
2194
  }
2195
+ }
2196
 
2197
  return modelList;
2198
  });
2199
 
2200
+ if (models.length === 0) {
2201
+ throw new Error('页面中没有找到模型选项');
2202
+ }
2203
 
2204
+ return models;
2205
 
2206
+ } finally {
2207
+ await page.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
2208
+ }
2209
+ }
2210
+
2211
+ // 新增:通过AJAX方式获取模型
2212
+ async function extractModelsViaAjax() {
2213
+ const browser = await initBrowser();
2214
+ const page = await browser.newPage();
2215
+
2216
+ try {
2217
+ await page.goto('https://toolbaz.com/writer/ai-writer', {
2218
+ waitUntil: 'networkidle2',
2219
+ timeout: 20000
2220
+ });
2221
 
2222
+ const models = await page.evaluate(async () => {
2223
+ return new Promise((resolve) => {
2224
+ // 尝试通过AJAX获取模型列表
2225
+ const modelEndpoints = [
2226
+ 'https://data.toolbaz.com/models.php',
2227
+ 'https://data.toolbaz.com/api/models',
2228
+ '/api/models',
2229
+ '/models'
2230
+ ];
2231
+
2232
+ let requestCount = 0;
2233
+
2234
+ const tryFetch = (url) => {
2235
+ return fetch(url, {
2236
+ method: 'GET',
2237
+ headers: {
2238
+ 'Accept': 'application/json',
2239
+ 'X-Requested-With': 'XMLHttpRequest',
2240
+ 'Referer': window.location.href
2241
+ }
2242
+ })
2243
+ .then(response => {
2244
+ if (response.ok) {
2245
+ return response.json();
2246
+ }
2247
+ throw new Error(`HTTP ${response.status}`);
2248
+ })
2249
+ .then(data => {
2250
+ if (Array.isArray(data) && data.length > 0) {
2251
+ return data.map((model, index) => ({
2252
+ id: model.id || model.name?.toLowerCase().replace(/[^a-z0-9]/g, '-') || `model-${index}`,
2253
+ name: model.name || model.displayName || `Model ${index}`,
2254
+ icon: model.icon || '',
2255
+ object: 'model',
2256
+ created: Math.floor(Date.now() / 1000),
2257
+ owned_by: 'toolbaz'
2258
+ }));
2259
+ }
2260
+ throw new Error('无效的模型数据');
2261
+ });
2262
+ };
2263
+
2264
+ // 尝试所有端点
2265
+ const promises = modelEndpoints.map(url =>
2266
+ tryFetch(url).catch(err => {
2267
+ requestCount++;
2268
+ if (requestCount === modelEndpoints.length) {
2269
+ throw new Error('所有AJAX端点都失败了');
2270
+ }
2271
+ return null;
2272
+ })
2273
+ );
2274
+
2275
+ Promise.all(promises)
2276
+ .then(results => {
2277
+ const validResults = results.filter(r => r !== null);
2278
+ if (validResults.length > 0) {
2279
+ resolve(validResults[0]); // 使用第一个成功的结果
2280
+ } else {
2281
+ throw new Error('没有成功的AJAX请求');
2282
+ }
2283
+ })
2284
+ .catch(() => resolve([])); // 返回空数组表示失败
2285
+ });
2286
+ });
2287
+
2288
+ if (models.length === 0) {
2289
+ throw new Error('AJAX方式未获取到模型');
2290
+ }
2291
+
2292
+ return models;
2293
+
2294
+ } finally {
2295
+ await page.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
2296
  }
2297
  }
2298
 
2299
+ // 新增:增强的静态模型配置
2300
+ async function getEnhancedStaticModels() {
2301
+ // 结合配置中的模型和常见的AI模型
2302
+ const enhancedModels = [
2303
+ // 聊天模型
2304
+ ...CONFIG.CHAT_MODELS.map(name => ({
2305
+ id: name.toLowerCase().replace(/[^a-z0-9]/g, '-'),
2306
+ name: name,
2307
+ object: 'model',
2308
+ created: Math.floor(Date.now() / 1000),
2309
+ owned_by: 'toolbaz-2api'
2310
+ })),
2311
+
2312
+ // 图片模型
2313
+ ...CONFIG.IMAGE_MODELS.map(name => ({
2314
+ id: name.toLowerCase().replace(/[^a-z0-9]/g, '-'),
2315
+ name: name,
2316
+ object: 'model',
2317
+ created: Math.floor(Date.now() / 1000),
2318
+ owned_by: 'toolbaz-2api'
2319
+ })),
2320
+
2321
+ // 额外的常见模型
2322
+ {
2323
+ id: 'gpt-4',
2324
+ name: 'GPT-4',
2325
+ object: 'model',
2326
+ created: Math.floor(Date.now() / 1000),
2327
+ owned_by: 'toolbaz-2api'
2328
+ },
2329
+ {
2330
+ id: 'gpt-3.5-turbo',
2331
+ name: 'GPT-3.5 Turbo',
2332
+ object: 'model',
2333
+ created: Math.floor(Date.now() / 1000),
2334
+ owned_by: 'toolbaz-2api'
2335
+ },
2336
+ {
2337
+ id: 'claude-3-sonnet',
2338
+ name: 'Claude 3 Sonnet',
2339
+ object: 'model',
2340
+ created: Math.floor(Date.now() / 1000),
2341
+ owned_by: 'toolbaz-2api'
2342
+ }
2343
+ ];
2344
+
2345
+ // 去重
2346
+ const uniqueModels = [];
2347
+ const seenIds = new Set();
2348
+
2349
+ for (const model of enhancedModels) {
2350
+ if (!seenIds.has(model.id)) {
2351
+ seenIds.add(model.id);
2352
+ uniqueModels.push(model);
2353
+ }
2354
+ }
2355
+
2356
+ return uniqueModels;
2357
+ }
2358
+
2359
+ // 新增:基础静态模型配置
2360
+ function getBasicStaticModels() {
2361
+ return [...CONFIG.CHAT_MODELS, ...CONFIG.IMAGE_MODELS].map(id => ({
2362
+ id: id.toLowerCase().replace(/[^a-z0-9]/g, '-'),
2363
+ name: id,
2364
+ object: 'model',
2365
+ created: Math.floor(Date.now() / 1000),
2366
+ owned_by: 'toolbaz-2api'
2367
+ }));
2368
+ }
2369
+
2370
  // 新增:测试API端点
2371
  async function handleTests(req, res) {
2372
  if (!verifyAuth(req)) return sendError(res, 401, 'Unauthorized');
 
2432
  max_retries: CONFIG.MAX_RETRIES,
2433
  performance_monitoring: CONFIG.PERFORMANCE_MONITORING
2434
  },
2435
+ browser: browser ? 'running' : 'stopped',
2436
+ cache: {
2437
+ models: modelCache.isValid() ? 'valid' : 'expired',
2438
+ credentials: credentialCache.isValid() ? 'valid' : 'expired'
2439
+ }
2440
  };
2441
 
2442
  res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
 
2448
  }
2449
  }
2450
 
2451
+ // 新增:模型刷新处理函数
2452
+ async function handleModelsRefresh(req, res) {
2453
+ if (!verifyAuth(req)) return sendError(res, 401, 'Unauthorized');
2454
+
2455
+ try {
2456
+ log('info', '🔄 收到模型刷新请求...');
2457
+
2458
+ // 清除模型缓存
2459
+ modelCache.clear();
2460
+
2461
+ // 强制重新提取模型
2462
+ const response = await handleModels(req, res);
2463
+
2464
+ log('info', '✅ 模型刷新完成');
2465
+
2466
+ } catch (error) {
2467
+ log('error', `❌ 模型刷新失败: ${error.message}`);
2468
+ sendError(res, 500, error.message);
2469
+ }
2470
+ }
2471
+
2472
  function handleUI(req, res) {
2473
  const html = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>${CONFIG.PROJECT_NAME}</title><style>:root{--bg:#121212;--panel:#1E1E1E;--border:#333;--text:#E0E0E0;--primary:#00FF9D;--secondary:#00BFA5;--error:#F44336;--success:#4CAF50;--warning:#FF9800}body{font-family:Consolas,monospace;background:var(--bg);color:var(--text);margin:0;height:100vh;display:flex;overflow:hidden;font-size:13px}.sidebar{width:320px;background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:10px;gap:10px}.main{flex:1;display:flex;flex-direction:column}.log-panel{height:40%;border-top:1px solid var(--border);background:#000;overflow-y:auto;padding:10px}.box{background:#252525;padding:15px;border-radius:6px;border:1px solid var(--border)}.label{font-size:11px;color:#888;display:block;margin-bottom:5px;text-transform:uppercase}input,select,textarea{width:100%;background:#333;border:1px solid #444;color:#fff;padding:8px;border-radius:4px;margin-bottom:10px;box-sizing:border-box;font-family:inherit}button{width:100%;padding:10px;background:var(--primary);border:none;border-radius:4px;font-weight:bold;cursor:pointer;color:#000;margin-bottom:5px;transition:all 0.2s}button:disabled{background:#555;cursor:not-allowed}button.secondary{background:#333;color:#fff;border:1px solid #555}button.danger{background:var(--error);color:#fff}button.warning{background:var(--warning);color:#000}.chat-window{flex:1;padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:15px}.msg{max-width:85%;padding:10px 15px;border-radius:6px;line-height:1.5;word-wrap:break-word}.msg.user{align-self:flex-end;background:#333;color:#fff}.msg.ai{align-self:flex-start;background:#1a1a1a;border:1px solid #333;width:100%}.msg.img img{max-width:100%;border-radius:4px}.log-entry{margin-bottom:8px;border-bottom:1px solid #222;padding-bottom:8px}.log-time{color:#666;margin-right:10px}.log-data{color:#ccc;white-space:pre-wrap;word-break:break-all;margin-top:4px;display:block}.tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:10px}.tab{flex:1;text-align:center;padding:8px;cursor:pointer;color:#888;border-bottom:2px solid transparent;transition:all 0.2s}.tab.active{color:var(--primary);border-bottom-color:var(--primary)}.status-item{display:flex;justify-content:space-between;margin-bottom:5px;font-size:12px}.status-value{color:var(--primary)}.test-result{margin:5px 0;padding:5px;border-radius:3px;font-size:11px}.test-pass{background:var(--success);color:#000}.test-fail{background:var(--error);color:#fff}.performance-grid{display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-top:10px}.perf-item{text-align:center;padding:5px;background:#333;border-radius:3px}.perf-value{font-size:16px;color:var(--primary)}.perf-label{font-size:10px;color:#888}</style></head><body><div class="sidebar"><div class="box"><h2 style="margin:0;color:var(--primary)">${CONFIG.PROJECT_NAME}</h2><span style="font-size:11px;color:#666">v${CONFIG.PROJECT_VERSION} | Enhanced Testing</span></div><div class="box"><div class="tabs"><div class="tab active" onclick="switchMode('chat')">聊天</div><div class="tab" onclick="switchMode('image')">绘图</div><div class="tab" onclick="switchMode('test')">测试</div></div><div id="chat-controls"><span class="label">Model</span><select id="chat-model">${CONFIG.CHAT_MODELS.map(m => `<option value="${m}">${m}</option>`).join('')}</select><span class="label">Prompt</span><textarea id="chat-prompt" rows="5" placeholder="输入内容...">你好,请介绍一下你自己</textarea></div><div id="image-controls" style="display:none"><span class="label">Model</span><select id="image-model">${CONFIG.IMAGE_MODELS.map(m => `<option value="${m}">${m}</option>`).join('')}</select><span class="label">Prompt</span><textarea id="image-prompt" rows="5" placeholder="输入图片描述...">A futuristic city at sunset</textarea></div><div id="test-controls" style="display:none"><span class="label">测试类型</span><select id=\"test-type\"><option value=\"comprehensive\">综合测试</option><option value=\"config\">配置测试</option><option value=\"network\">网络测试</option><option value=\"browser\">浏览器测试</option><option value=\"session\">会话测试</option><option value=\"ajax\">AJAX测试</option><option value=\"prefix\">前缀过滤测试</option></select><button class="warning" onclick="runTest()">🧪 运行测试</button></div><button id="btn-send" onclick="handleSend()">发送请求</button></div><div class="box"><div class="status-item"><span>服务状态:</span><span class="status-value" id="service-status">检查中...</span></div><div class="status-item"><span>浏览器:</span><span class="status-value" id="browser-status">未知</span></div><div class="status-item"><span>总请求:</span><span class="status-value" id="total-requests">0</span></div><div class="status-item"><span>成功率:</span><span class="status-value" id="success-rate">0%</span></div><div class="performance-grid"><div class="perf-item"><div class="perf-value" id="ajax-success">0</div><div class="perf-label">AJAX成功</div></div><div class="perf-item"><div class="perf-value" id="ui-fallback">0</div><div class="perf-label">UI回退</div></div><div class="perf-item"><div class="perf-value" id="avg-response">0ms</div><div class="perf-label">平均响应</div></div><div class="perf-item"><div class="perf-value" id="uptime">0m</div><div class="perf-label">运行时间</div></div></div></div><div class="box" style="margin-top:auto"><button class="secondary" onclick="copyLogs()">📋 复制日志</button><button class="secondary" onclick="clearLogs()">🗑️ 清空日志</button><button class="secondary" onclick="refreshStatus()">🔄 刷新状态</button></div></div><div class="main"><div class="chat-window" id="chat"><div class="msg ai">🎉 ${CONFIG.PROJECT_NAME} v${CONFIG.PROJECT_VERSION} 已就绪<br><br>✨ 新功能:<br>• 🧪 增强的测试套件<br>• 📊 性能监控和统计<br>• 🔍 深度调试信息<br>• 🔄 智能重试机制<br><br>请选择聊天或测试模式开始使用!</div></div><div class="log-panel" id="log-container"></div></div><script>const API_KEY="${CONFIG.API_MASTER_KEY}";const ORIGIN=window.location.origin;let currentMode='chat';let statusInterval;function switchMode(mode){currentMode=mode;document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));event.target.classList.add('active');document.getElementById('chat-controls').style.display=mode==='chat'?'block':'none';document.getElementById('image-controls').style.display=mode==='image'?'block':'none';document.getElementById('test-controls').style.display=mode==='test'?'block':'none';const sendBtn=document.getElementById('btn-send');sendBtn.style.display=mode==='test'?'none':'block';}function appendLog(msg,type='info'){const div=document.createElement('div');div.className='log-entry';let color='#ccc';switch(type){case 'error':color='#F44336';break;case 'warn':color='#FF9800';break;case 'success':color='#4CAF50';break;case 'info':color='#2196F3';break;}div.innerHTML=\`<span class="log-time">\${new Date().toLocaleTimeString()}</span><span class="log-data" style="color:\${color}">\${msg}</span>\`;document.getElementById('log-container').appendChild(div);div.scrollIntoView()}function clearLogs(){document.getElementById('log-container').innerHTML=''}function copyLogs(){const logs=Array.from(document.querySelectorAll('.log-entry')).map(e=>e.innerText).join('\\n');navigator.clipboard.writeText(logs).then(()=>appendLog('日志已复制到剪贴板','success'))}function appendMsg(role,content){const div=document.createElement('div');div.className=\`msg \${role}\`;div.innerHTML=content;document.getElementById('chat').appendChild(div);div.scrollIntoView({behavior:"smooth"});return div}async function handleSend(){const btn=document.getElementById('btn-send');btn.disabled=true;clearLogs();appendLog('🚀 开始请求...');if(currentMode==='chat'){const prompt=document.getElementById('chat-prompt').value;const model=document.getElementById('chat-model').value;if(!prompt){btn.disabled=false;return}appendMsg('user',prompt);const aiMsg=appendMsg('ai','...');let fullText='';try{appendLog('📡 发送到浏览器...');const res=await fetch(ORIGIN+'/v1/chat/completions',{method:'POST',headers:{'Authorization':'Bearer '+API_KEY,'Content-Type':'application/json'},body:JSON.stringify({model,messages:[{role:'user',content:prompt}],stream:true})});appendLog('✅ 接收响应...');const reader=res.body.getReader();const decoder=new TextDecoder();while(true){const{done,value}=await reader.read();if(done)break;const chunk=decoder.decode(value);const lines=chunk.split('\\n');for(const line of lines){if(line.startsWith('data: ')){const dataStr=line.slice(6);if(dataStr==='[DONE]')break;try{const json=JSON.parse(dataStr);if(json.choices&&json.choices[0].delta.content){fullText+=json.choices[0].delta.content;aiMsg.innerText=fullText}}catch(e){}}}}appendLog('🎉 完成!','success');refreshStatus();}catch(e){aiMsg.innerText+='\\n[错误]: '+e.message;appendLog('❌ 错误: '+e.message,'error')}}else{const prompt=document.getElementById('image-prompt').value;const model=document.getElementById('image-model').value;if(!prompt){btn.disabled=false;return}appendMsg('user',prompt);const aiMsg=appendMsg('ai','生成中...');try{appendLog('🎨 发送图片请求...');const res=await fetch(ORIGIN+'/v1/images/generations',{method:'POST',headers:{'Authorization':'Bearer '+API_KEY,'Content-Type':'application/json'},body:JSON.stringify({model,prompt})});const data=await res.json();if(data.error)throw new Error(data.error.message);const imgUrl=data.data[0].url;aiMsg.innerHTML=\`<img src="\${imgUrl}" onclick="window.open(this.src)">\`;aiMsg.className='msg ai img';appendLog('✅ 图片生成成功!','success');refreshStatus();}catch(e){aiMsg.innerText='生成失败: '+e.message;appendLog('❌ 错误: '+e.message,'error')}}btn.disabled=false}async function runTest(){const testType=document.getElementById('test-type').value;appendLog(\`🧪 开始运行\${testType}测试...\`,'info');const testMsg=appendMsg('ai','<div id="test-results">正在运行测试...</div>');try{const res=await fetch(ORIGIN+'/v1/tests?type='+testType,{headers:{'Authorization':'Bearer '+API_KEY}});const data=await res.json();let html='<h3>测试结果</h3>';if(data.overall){html+=\`<p><strong>综合测试:</strong> \${data.overall.passed}/\${data.overall.passed+data.overall.failed} 通过</p>\`;if(data.configuration){html+=\`<div class="test-result test-pass">配置测试: \${data.configuration.passed} 通过, \${data.configuration.failed} 失败</div>\`;}}if(data.success===false){html+=\`<div class="test-result test-fail">测试失败: \${data.error}</div>\`;}else if(data.success){html+=\`<div class="test-result test-pass">测试通过</div>\`;}if(data.analysis){html+=\`<p><strong>会话分析:</strong> 找到 \${data.analysis.elementsFound.length} 个相关元素</p>\`;}if(data.length>0){html+='<h4>详细结果:</h4><ul>';data.forEach(result=>{const status=result.status==='success'?'pass':'fail';html+=\`<li class="test-result test-\${status}">\${result.name}: \${result.status}</li>\`;});html+='</ul>';}testMsg.querySelector('#test-results').innerHTML=html;appendLog('测试完成','success');refreshStatus();}catch(e){testMsg.innerHTML='<div class="test-result test-fail">测试执行失败: '+e.message+'</div>';appendLog('测试执行失败: '+e.message,'error');}}async function refreshStatus(){try{const res=await fetch(ORIGIN+'/v1/status',{headers:{'Authorization':'Bearer '+API_KEY}});const data=await res.json();document.getElementById('service-status').textContent='运行中';document.getElementById('browser-status').textContent=data.browser||'未知';document.getElementById('total-requests').textContent=data.performance.totalRequests||0;const successRate=data.performance.totalRequests>0?Math.round(data.performance.successfulRequests/data.performance.totalRequests*100):0;document.getElementById('success-rate').textContent=successRate+'%';document.getElementById('ajax-success').textContent=data.performance.ajaxSuccess||0;document.getElementById('ui-fallback').textContent=data.performance.uiFallback||0;document.getElementById('avg-response').textContent=(data.performance.averageResponseTime||0)+'ms';document.getElementById('uptime').textContent=Math.floor(data.uptime/60)+'m';}catch(e){appendLog('状态刷新失败: '+e.message,'error');}}function init(){refreshStatus();statusInterval=setInterval(refreshStatus,30000);}init();</script></body></html>`;
2474
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
 
2502
  // 新增的测试和状态端点
2503
  if (url.pathname === '/v1/tests') return await handleTests(req, res);
2504
  if (url.pathname === '/v1/status') return await handleStatus(req, res);
2505
+ if (url.pathname === '/v1/models/refresh') return await handleModelsRefresh(req, res);
2506
 
2507
  sendError(res, 404, 'Not Found');
2508
  });
 
2558
  log('info', ' - GET / : Web界面');
2559
  log('info', ' - POST /v1/chat/completions : 聊天API');
2560
  log('info', ' - POST /v1/images/generations : 图片生成API');
2561
+ log('info', ' - GET /v1/models : 模型列表API(带60分钟缓存)');
2562
+ log('info', ' - GET /v1/models/refresh : 强制刷新模型列表API');
2563
+ log('info', ' - GET /v1/status : 服务状态API(包含缓存状态)');
2564
  log('info', ' - GET /v1/tests : 测试API (?type=config|network|browser|session|ajax|comprehensive)');
2565
 
2566
  // 启动性能统计定时显示