toolbaz2api / worker.js
iyougame's picture
Update worker.js
64726ca verified
raw
history blame
115 kB
// =================================================================================
// 项目: toolbaz-2api (Puppeteer 版本)
// 版本: 2.1.3 (代号: Hybrid Optimization - 混合优化版)
// 日期: 2025-11-26
//
// [v2.1.3 核心变更 - 混合优化]
// 1. [混合认证] 集成Cloudflare Worker的TDF时间差计算,提高认证准确性
// 2. [响应修复] 修复AJAX成功响应被误判为失败的关键问题
// 3. [验证优化] 改进响应验证逻辑,减少误判,提高识别精度
// 4. [数据结构] 统一所有AJAX函数的返回数据结构,确保一致性
// 5. [智能回退] 优化UI回退机制,只在真正需要时启用
//
// [v2.1.2 历史变更]
// 1. [精准优化] 参考思路保持优势: 参考Cloudflare Worker的优化思路,但保持Docker版本的成功机制
// 2. [认证优化] 行为数据策略: 保持mM9wZ: [], kP8jY: []关键优化,避免机器人检测
// 3. [请求优化] 参考直接POST: 优化向writing.php的直接请求流程,减少中间环节
// 4. [时序优化] 智能延迟: 在关键步骤添加合理延迟,模拟真实用户行为
// 5. [错误处理] 增强容错: 改进错误处理和重试机制,提高成功率
// [v2.1.1 历史变更]
// 1. [新增] 全面的测试套件: 添加了大量测试函数和调试工具
// 2. [新增] 性能监控: 实时监控请求响应时间和成功率
// 3. [新增] 错误分析: 详细分析失败原因并提供解决方案建议
// 4. [新增] 会话验证: 多层次验证session有效性
// 5. [新增] 网络请求监控: 监控所有网络请求,分析问题
// 6. [增强] 更丰富的日志输出: 彩色日志、分级显示、结构化输出
// 7. [新增] 自动重试机制: 智能重试失败的请求
// 8. [新增] 缓存优化: 优化浏览器启动和页面加载
//
// [v2.1.0 核心变更]
// 1. [重大改进] 直接模拟AJAX请求: 新增 bypass 前端JavaScript的解决方案
// 通过 Puppeteer 直接向 https://data.toolbaz.com/writing.php 发送请求,避免网站自动添加前缀
// 2. [智能回退] 保持原有UI方法作为备用方案,确保100%可靠性
// 3. [会话管理] 自动检测和获取session_id,支持无session场景
// 4. [错误处理] 增强的错误处理和日志记录
// =================================================================================
import http from 'http';
import puppeteer from 'puppeteer';
const CONFIG = {
PROJECT_NAME: "toolbaz-2api",
PROJECT_VERSION: "2.1.2",
API_MASTER_KEY: "1",
PORT: 7860,
CHAT_MODELS: ["gemini-2.5-flash", "gemini-2.5-pro", "claude-sonnet-4", "gpt-5", "grok-4-fast", "gpt-oss-120b", "gemini-2.0-flash-thinking", "grok-4.1-fast", "Llama-4-Maverick", "deepseek-v3.1"],
IMAGE_MODELS: ["FLUX-1-schnell", "FLUX-1-dev", "FLUX-1-uncensored"],
DEFAULT_CHAT_MODEL: "gemini-2.5-flash",
// 新增测试配置
TESTING_MODE: true,
DEBUG_LEVEL: "verbose", // error, warn, info, debug, verbose
MAX_RETRIES: 3,
RETRY_DELAY: 1000,
PERFORMANCE_MONITORING: true,
};
let browser = null;
// 全局凭据缓存
let credentialCache = {
data: null,
timestamp: null,
expiresAt: null,
isValid: function() {
return this.data && this.expiresAt && Date.now() < this.expiresAt;
},
set: function(credentials, ttlMinutes = 30) {
this.data = credentials;
this.timestamp = Date.now();
this.expiresAt = Date.now() + (ttlMinutes * 60 * 1000);
log('info', `🔐 凭据已缓存,有效期 ${ttlMinutes} 分钟`);
},
clear: function() {
this.data = null;
this.timestamp = null;
this.expiresAt = null;
log('info', '🔐 凭据缓存已清空');
}
};
// 新增:性能监控和统计
const PERFORMANCE_STATS = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
ajaxSuccess: 0,
ajaxFailed: 0,
uiFallback: 0,
averageResponseTime: 0,
sessionDetectionStats: {},
lastResetTime: Date.now()
};
// 新增:彩色日志输出
const COLORS = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
bright_red: '\x1b[91m',
bright_green: '\x1b[92m',
bright_yellow: '\x1b[93m',
bright_blue: '\x1b[94m',
bright_magenta: '\x1b[95m',
bright_cyan: '\x1b[96m'
};
function log(level, message, data = null) {
const levels = { error: 0, warn: 1, info: 2, debug: 3, verbose: 4 };
const currentLevel = levels[CONFIG.DEBUG_LEVEL] || 2;
const messageLevel = levels[level] || 2;
if (messageLevel <= currentLevel) {
const timestamp = new Date().toISOString();
const coloredLevel = {
error: `${COLORS.bright_red}[ERROR]${COLORS.reset}`,
warn: `${COLORS.bright_yellow}[WARN]${COLORS.reset}`,
info: `${COLORS.bright_blue}[INFO]${COLORS.reset}`,
debug: `${COLORS.bright_cyan}[DEBUG]${COLORS.reset}`,
verbose: `${COLORS.white}[VERBOSE]${COLORS.reset}`
}[level] || `[${level.toUpperCase()}]`;
console.log(`${timestamp} ${coloredLevel} ${message}`);
if (data && level === 'verbose') {
console.log(`${COLORS.cyan}数据详情:${COLORS.reset}`, JSON.stringify(data, null, 2));
}
}
}
// 新增:测试函数 - 验证基本配置
function testBasicConfiguration() {
log('info', '🧪 开始基本配置测试...');
const tests = [
{ name: '项目名称', value: CONFIG.PROJECT_NAME, expected: 'toolbaz-2api' },
{ name: '端口配置', value: CONFIG.PORT, expected: 7860 },
{ name: 'API密钥', value: CONFIG.API_MASTER_KEY, expected: '1' },
{ name: '默认模型', value: CONFIG.DEFAULT_CHAT_MODEL, expected: 'gemini-2.5-flash' },
{ name: '聊天模型数量', value: CONFIG.CHAT_MODELS.length, min: 1 },
{ name: '图片模型数量', value: CONFIG.IMAGE_MODELS.length, min: 1 }
];
let passed = 0;
let failed = 0;
tests.forEach(test => {
const result = test.expected ? test.value === test.expected : test.value >= (test.min || 0);
if (result) {
log('verbose', `✅ ${test.name}: ${test.value}`);
passed++;
} else {
log('error', `❌ ${test.name}: 期望 ${test.expected || `>=${test.min || 0}`},实际 ${test.value}`);
failed++;
}
});
log('info', `📊 配置测试结果: ${passed} 通过, ${failed} 失败`);
return { passed, failed };
}
// 修改:测试函数 - 网络连接性(使用单个浏览器实例)
async function testNetworkConnectivity() {
log('info', '🌐 开始网络连接性测试(使用单个浏览器实例)...');
const testUrls = [
{ name: '主站', url: 'https://toolbaz.com' },
{ name: '写作页面', url: 'https://toolbaz.com/writer/ai-writer' },
{ name: '图片生成', url: 'https://toolbaz.com/image/ai-image-generator' },
{ name: 'API端点', url: 'https://data.toolbaz.com/writing.php' }
];
const results = [];
let testBrowser = null;
let testPage = null;
try {
// 启动单个浏览器实例
testBrowser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu'
]
});
testPage = await testBrowser.newPage();
// 设置更长的超时时间以应对网络问题
testPage.setDefaultTimeout(30000);
for (const test of testUrls) {
try {
const startTime = Date.now();
// 重用同一个页面进行测试
await testPage.goto(test.url, {
waitUntil: 'networkidle2',
timeout: 25000 // 增加到25秒
});
const responseTime = Date.now() - startTime;
const title = await testPage.title();
results.push({
name: test.name,
url: test.url,
status: 'success',
responseTime,
title: title.substring(0, 50)
});
log('verbose', `✅ ${test.name}: ${responseTime}ms - ${title.substring(0, 30)}...`);
// 在测试之间添加短暂延迟,避免过于频繁的请求
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
results.push({
name: test.name,
url: test.url,
status: 'failed',
error: error.message
});
log('error', `❌ ${test.name}: ${error.message}`);
// 错误后也短暂延迟
await new Promise(resolve => setTimeout(resolve, 500));
}
}
} catch (error) {
log('error', `❌ 网络测试初始化失败: ${error.message}`);
} finally {
// 清理资源
if (testPage) {
await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
}
if (testBrowser) {
await testBrowser.close().catch(e => log('warn', `浏览器关闭失败: ${e.message}`));
}
}
const successCount = results.filter(r => r.status === 'success').length;
log('info', `📊 网络测试结果: ${successCount}/${results.length} 连接成功`);
return results;
}
// 新增:测试函数 - 前缀过滤测试
async function testPromptPrefixFiltering() {
log('info', '🧹 开始前缀过滤测试...');
const testCases = [
{
input: 'Generate an original and engaging piece of writing on the following topic : 寫一個1萬字的小說ㅤ',
expected: '寫一個1萬字的小說ㅤ'
},
{
input: 'Write a code snippet that does the following in the specified language: Python function to sort array',
expected: 'Python function to sort array'
},
{
input: '正常文本,无需过滤',
expected: '正常文本,无需过滤'
}
];
const results = [];
for (const testCase of testCases) {
const filtered = cleanPromptPrefix(testCase.input);
const passed = filtered === testCase.expected;
results.push({
input: testCase.input,
expected: testCase.expected,
actual: filtered,
passed
});
if (passed) {
log('verbose', `✅ 前缀过滤测试通过: "${testCase.input}" -> "${filtered}"`);
} else {
log('error', `❌ 前缀过滤测试失败: "${testCase.input}" -> "${filtered}" (期望: "${testCase.expected}")`);
}
}
const passCount = results.filter(r => r.passed).length;
log('info', `📊 前缀过滤测试结果: ${passCount}/${results.length} 通过`);
return { results, passed: passCount, failed: results.length - passCount };
}
// 修改:测试函数 - 浏览器环境(使用全局浏览器实例)
async function testBrowserEnvironment() {
log('info', '🌍 开始浏览器环境测试...');
let testPage = null;
try {
// 尝试使用现有的浏览器实例,如果没有则创建
const testBrowser = browser || await initBrowser();
testPage = await testBrowser.newPage();
// 设置更长的超时时间
testPage.setDefaultTimeout(20000);
// 测试基本功能
await testPage.goto('https://example.com', { waitUntil: 'networkidle2', timeout: 15000 });
const browserInfo = await testPage.evaluate(() => {
return {
userAgent: navigator.userAgent,
language: navigator.language,
cookieEnabled: navigator.cookieEnabled,
localStorage: !!localStorage,
sessionStorage: !!sessionStorage,
platform: navigator.platform
};
});
// 测试JavaScript执行
const jsTest = await testPage.evaluate(() => {
try {
return {
fetch: typeof fetch !== 'undefined',
formData: typeof FormData !== 'undefined',
urlSearchParams: typeof URLSearchParams !== 'undefined',
promise: typeof Promise !== 'undefined',
async: typeof (async () => {}) === 'function'
};
} catch (e) {
return { error: e.message };
}
});
log('verbose', '✅ 浏览器环境测试通过');
log('verbose', '📋 浏览器信息:', browserInfo);
log('verbose', '📋 JavaScript功能:', jsTest);
return { success: true, browserInfo, jsTest };
} catch (error) {
log('error', `❌ 浏览器环境测试失败: ${error.message}`);
return { success: false, error: error.message };
} finally {
// 清理页面资源
if (testPage) {
await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
}
}
}
// 修改:测试函数 - 深度会话检测(使用全局浏览器实例)
async function testSessionDetection() {
log('info', '🔍 开始深度会话检测测试...');
let testPage = null;
try {
// 使用现有的浏览器实例或创建新的
const testBrowser = browser || await initBrowser();
testPage = await testBrowser.newPage();
// 设置更长的超时时间
testPage.setDefaultTimeout(30000);
await testPage.goto('https://toolbaz.com/writer/ai-writer', {
waitUntil: 'networkidle2',
timeout: 30000
});
// 简化的会话检测(减少资源消耗)
const sessionAnalysis = await testPage.evaluate(() => {
const analysis = {
timestamp: Date.now(),
url: window.location.href,
title: document.title,
elementsFound: [],
hiddenInputs: [],
cookies: {},
sessionStorage: {},
localStorage: {}
};
// 专注于关键元素,避免过度遍历
const sessionKeywords = ['session', 'sess', 'sid', 'token', 'csrf', 'auth'];
// 只检查可能包含会话信息的元素
const relevantSelectors = [
'input[type="hidden"]',
'input[name*="session"]',
'input[name*="token"]',
'input[id*="session"]',
'input[id*="token"]',
'[data-session]',
'[data-token]'
];
relevantSelectors.forEach(selector => {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(elem => {
const info = {
tag: elem.tagName,
id: elem.id || '',
name: elem.name || '',
value: elem.value || ''
};
const searchText = [info.id, info.name, info.value].join(' ').toLowerCase();
if (sessionKeywords.some(keyword => searchText.includes(keyword))) {
analysis.elementsFound.push(info);
}
});
} catch (e) {
// 忽略选择器错误
}
});
// 分析隐藏输入
const hiddenInputs = document.querySelectorAll('input[type="hidden"]');
hiddenInputs.forEach(input => {
if (input.name || input.value) {
analysis.hiddenInputs.push({
name: input.name,
id: input.id,
value: input.value.substring(0, 100)
});
}
});
// 简化存储分析
try {
for (let i = 0; i < Math.min(sessionStorage.length, 10); i++) {
const key = sessionStorage.key(i);
if (key && sessionKeywords.some(k => key.toLowerCase().includes(k))) {
analysis.sessionStorage[key] = sessionStorage.getItem(key);
}
}
for (let i = 0; i < Math.min(localStorage.length, 10); i++) {
const key = localStorage.key(i);
if (key && sessionKeywords.some(k => key.toLowerCase().includes(k))) {
analysis.localStorage[key] = localStorage.getItem(key);
}
}
} catch (e) {
// 忽略存储访问错误
}
// 简化cookie分析
try {
document.cookie.split(';').forEach(cookie => {
const [name, value] = cookie.trim().split('=');
if (name && value && sessionKeywords.some(k => name.toLowerCase().includes(k))) {
analysis.cookies[name] = value;
}
});
} catch (e) {
// 忽略cookie访问错误
}
return analysis;
});
log('verbose', `📊 会话分析结果:`);
log('verbose', ` - 页面: ${sessionAnalysis.title}`);
log('verbose', ` - 相关元素: ${sessionAnalysis.elementsFound.length}`);
log('verbose', ` - 隐藏输入: ${sessionAnalysis.hiddenInputs.length}`);
log('verbose', ` - Cookie数量: ${Object.keys(sessionAnalysis.cookies).length}`);
log('verbose', ` - SessionStorage: ${Object.keys(sessionAnalysis.sessionStorage).length}`);
log('verbose', ` - LocalStorage: ${Object.keys(sessionAnalysis.localStorage).length}`);
if (sessionAnalysis.elementsFound.length > 0) {
log('info', '🎯 找到潜在的会话相关元素:');
sessionAnalysis.elementsFound.forEach(elem => {
log('verbose', ` - ${elem.tag}#${elem.id} (${elem.name}): ${elem.value.substring(0, 50)}`);
});
}
return { success: true, analysis: sessionAnalysis };
} catch (error) {
log('error', `❌ 深度会话检测失败: ${error.message}`);
return { success: false, error: error.message };
} finally {
// 清理页面资源
if (testPage) {
await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
}
}
}
// 修改:测试函数 - AJAX请求模拟(使用全局浏览器实例)
async function testAjaxRequest() {
log('info', '📡 开始AJAX请求模拟测试...');
let testPage = null;
try {
// 使用现有的浏览器实例或创建新的
const testBrowser = browser || await initBrowser();
testPage = await testBrowser.newPage();
// 设置更长的超时时间
testPage.setDefaultTimeout(30000);
await testPage.goto('https://toolbaz.com/writer/ai-writer', {
waitUntil: 'networkidle2',
timeout: 30000
});
// 简化的请求监听
const networkRequests = [];
try {
await testPage.setRequestInterception(true);
testPage.on('request', request => {
if (request.url().includes('writing.php')) {
networkRequests.push({
url: request.url(),
method: request.method(),
headers: request.headers(),
postData: request.postData()
});
}
request.continue();
});
} catch (e) {
log('warn', `请求拦截设置失败,继续测试: ${e.message}`);
}
// 测试AJAX请求
const ajaxTest = await testPage.evaluate(async () => {
return new Promise((resolve) => {
const testData = {
text: 'Hello, this is a test message',
model: 'gemini-2.5-flash',
test: true
};
const startTime = Date.now();
fetch('https://data.toolbaz.com/writing.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': '*/*',
'Origin': 'https://toolbaz.com',
'Referer': 'https://toolbaz.com/writer/ai-writer'
},
body: new URLSearchParams(testData).toString()
})
.then(response => {
return {
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()),
url: response.url,
responseTime: Date.now() - startTime
};
})
.then(result => {
resolve({ success: true, result });
})
.catch(error => {
resolve({ success: false, error: error.message, responseTime: Date.now() - startTime });
});
});
});
log('verbose', '📊 AJAX测试结果:');
log('verbose', ` - 请求状态: ${ajaxTest.success ? '成功' : '失败'}`);
if (ajaxTest.success) {
log('verbose', ` - HTTP状态: ${ajaxTest.result.status}`);
log('verbose', ` - 响应时间: ${ajaxTest.result.responseTime}ms`);
log('verbose', ` - 响应URL: ${ajaxTest.result.url}`);
} else {
log('error', ` - 错误信息: ${ajaxTest.error}`);
}
log('verbose', ` - 网络请求数量: ${networkRequests.length}`);
return { success: ajaxTest.success, data: ajaxTest, networkRequests };
} catch (error) {
log('error', `❌ AJAX测试失败: ${error.message}`);
return { success: false, error: error.message };
} finally {
// 清理页面资源
if (testPage) {
await testPage.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
}
}
}
// 新增:性能统计更新
function updatePerformanceStats(type, success = true, responseTime = 0) {
if (!CONFIG.PERFORMANCE_MONITORING) return;
PERFORMANCE_STATS.totalRequests++;
if (success) {
PERFORMANCE_STATS.successfulRequests++;
if (type === 'ajax') {
PERFORMANCE_STATS.ajaxSuccess++;
}
} else {
PERFORMANCE_STATS.failedRequests++;
if (type === 'ajax') {
PERFORMANCE_STATS.ajaxFailed++;
} else if (type === 'ui') {
PERFORMANCE_STATS.uiFallback++;
}
}
if (responseTime > 0) {
const total = PERFORMANCE_STATS.averageResponseTime * (PERFORMANCE_STATS.totalRequests - 1) + responseTime;
PERFORMANCE_STATS.averageResponseTime = Math.round(total / PERFORMANCE_STATS.totalRequests);
}
}
// 新增:显示性能统计
function showPerformanceStats() {
if (!CONFIG.PERFORMANCE_MONITORING) return;
const uptime = Date.now() - PERFORMANCE_STATS.lastResetTime;
const uptimeMinutes = Math.round(uptime / 60000);
log('info', '📊 性能统计报告:');
log('info', ` - 运行时间: ${uptimeMinutes} 分钟`);
log('info', ` - 总请求数: ${PERFORMANCE_STATS.totalRequests}`);
log('info', ` - 成功请求: ${PERFORMANCE_STATS.successfulRequests}`);
log('info', ` - 失败请求: ${PERFORMANCE_STATS.failedRequests}`);
log('info', ` - 成功率: ${PERFORMANCE_STATS.totalRequests > 0 ? Math.round(PERFORMANCE_STATS.successfulRequests / PERFORMANCE_STATS.totalRequests * 100) : 0}%`);
log('info', ` - AJAX成功: ${PERFORMANCE_STATS.ajaxSuccess}`);
log('info', ` - AJAX失败: ${PERFORMANCE_STATS.ajaxFailed}`);
log('info', ` - UI回退: ${PERFORMANCE_STATS.uiFallback}`);
log('info', ` - 平均响应时间: ${PERFORMANCE_STATS.averageResponseTime}ms`);
}
// 修改:综合测试函数(优化浏览器资源管理)
async function runComprehensiveTests() {
log('info', '🧪 开始综合测试套件(优化版)...');
const results = {
configuration: null,
network: null,
browser: null,
session: null,
ajax: null,
overall: { passed: 0, failed: 0 }
};
let sharedBrowser = null;
try {
// 1. 基本配置测试(不需要浏览器)
results.configuration = testBasicConfiguration();
if (results.configuration.failed === 0) {
results.overall.passed++;
} else {
results.overall.failed++;
}
// 2. 网络连接性测试(已优化为使用单个浏览器)
results.network = await testNetworkConnectivity();
if (results.network.filter(r => r.status === 'success').length >= 3) {
results.overall.passed++;
} else {
results.overall.failed++;
}
// 3. 浏览器环境测试(使用全局浏览器)
results.browser = await testBrowserEnvironment();
if (results.browser.success) {
results.overall.passed++;
} else {
results.overall.failed++;
}
// 4. 会话检测测试(使用全局浏览器)
results.session = await testSessionDetection();
if (results.session.success) {
results.overall.passed++;
} else {
results.overall.failed++;
}
// 5. AJAX请求测试(使用全局浏览器)
results.ajax = await testAjaxRequest();
if (results.ajax.success) {
results.overall.passed++;
} else {
results.overall.failed++;
}
// 6. 前缀过滤测试(不需要浏览器)
results.prefixFiltering = await testPromptPrefixFiltering();
if (results.prefixFiltering.failed === 0) {
results.overall.passed++;
} else {
results.overall.failed++;
}
log('info', `🎉 综合测试完成: ${results.overall.passed}/6 测试通过`);
if (results.overall.failed > 0) {
log('warn', '⚠️ 部分测试失败,请检查相关配置');
}
// 添加资源使用统计
const browserInstance = browser || sharedBrowser;
if (browserInstance) {
const pages = await browserInstance.pages().catch(() => []);
log('info', `📊 浏览器资源统计: ${pages.length} 个页面`);
}
return results;
} catch (error) {
log('error', `❌ 综合测试失败: ${error.message}`);
// 确保清理共享浏览器资源
if (sharedBrowser) {
await sharedBrowser.close().catch(e => log('warn', `共享浏览器关闭失败: ${e.message}`));
}
return { ...results, error };
}
}
async function initBrowser() {
if (!browser) {
log('info', '🚀 启动浏览器...');
const launchOptions = {
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--disable-blink-features=AutomationControlled',
'--disable-web-security',
'--disable-features=VizDisplayCompositor'
],
protocolTimeout: 60000,
defaultViewport: {
width: 1920,
height: 1080
}
};
try {
browser = await puppeteer.launch(launchOptions);
log('info', '✅ 浏览器启动成功');
// 如果启用测试模式,运行快速测试
if (CONFIG.TESTING_MODE) {
log('debug', '🧪 执行浏览器启动测试...');
const testPage = await browser.newPage();
await testPage.goto('https://example.com', { waitUntil: 'networkidle2', timeout: 10000 });
const testResult = await testPage.evaluate(() => ({
title: document.title,
readyState: document.readyState
}));
log('debug', '📋 浏览器测试结果:', testResult);
await testPage.close();
}
} catch (error) {
log('error', `❌ 浏览器启动失败: ${error.message}`);
throw error;
}
}
return browser;
}
// 混合优化版本:集成Cloudflare Worker的TDF认证 + Docker版本的UI回退
async function performAjaxRequest(page, prompt, model, sessionId = null, retryCount = 0) {
const startTime = Date.now();
log('info', '📡 尝试直接AJAX请求(混合优化模式)...');
log('info', '🔐 第一步:获取TDF值和SessionID...');
log('debug', '📋 请求参数:');
log('debug', ` - prompt: "${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}"`);
log('debug', ` - model: ${model}`);
log('debug', ` - 重试次数: ${retryCount}`);
try {
// 步骤1:获取TDF值(Cloudflare Worker方法)
const tdf = await page.evaluate(async () => {
return new Promise((resolve, reject) => {
const url = `https://data.toolbaz.com/info.php?v=1&_v=j101&a=1786349895&t=pageview&_s=1`;
fetch(url, { method: 'POST' })
.then(response => response.json())
.then(data => {
if (!data.t) {
reject(new Error('TDF响应无效'));
return;
}
const serverTime = data.t;
const clientTime = Math.floor(Date.now() / 1000);
const realTdf = serverTime - clientTime;
// 模拟真实用户的时钟偏移
const fakeClockDrift = Math.floor(Math.random() * 58) + 2;
const humanizedTdf = realTdf + fakeClockDrift;
resolve(humanizedTdf);
})
.catch(error => reject(error));
});
});
log('info', `✅ TDF获取成功: ${tdf}`);
// 步骤2:生成SessionID和基础Token(保持Docker版本的成功结构)
const authData = await page.evaluate((tdfValue) => {
// 模拟 updateC() 函数生成SessionID
function gRS(length) {
const c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const cLength = c.length;
for (let i = 0; i < length; i++) {
result += c.charAt(Math.floor(Math.random() * cLength));
}
return result;
}
function setC(name, value, days) {
const d = new Date();
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
const expires = "expires=" + d.toUTCString();
document.cookie = name + "=" + (value || "") + ";" + expires + ";path=/";
}
function getCookie(name) {
const cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.startsWith(name + "=")) {
return cookie.substring(name.length + 1);
}
}
return null;
}
function cTZ() {
return Math.floor(new Date().getTime() / 1000);
}
// 生成新的SessionID(保持原有36位格式,但添加变化)
const session_id = gRS(36);
setC("SessionID", session_id, 1);
// 模拟 xA1pY() 函数生成Token(保持原有优势,添加小幅优化)
function xA1pY() {
const bR6wF = {
nV5kP: navigator.userAgent,
lQ9jX: navigator.language,
sD2zR: `${window.screen.width}x${window.screen.height}`,
tY4hL: Intl.DateTimeFormat().resolvedOptions().timeZone,
pL8mC: navigator.platform,
cQ3vD: window.screen.colorDepth,
hK7jN: navigator.hardwareConcurrency || "unknown",
};
const uT4bX = {
mM9wZ: [], // 保持关键:行为数据置空
kP8jY: [], // 保持关键:键盘行为置空
};
// 简化版:不收集实际的行为数据
function hA9kQ(tQ8zY) {
return gRS(6) + tQ8zY;
}
function gH7wN(dA3vR) {
const jS9mY = JSON.stringify(dA3vR);
return btoa(unescape(encodeURIComponent(jS9mY)));
}
function gF2jX() {
const cD8yL = {
bR6wF,
uT4bX,
tuTcS: cTZ(),
tDfxy: tdfValue || getCookie("tdf"), // 使用传入的tdf值
RtyJt: gRS(36),
};
const tN5wX = hA9kQ(gH7wN(cD8yL));
return tN5wX;
}
return gF2jX();
}
const token = xA1pY();
return {
session_id,
token
};
}, tdf);
log('info', `✅ 获取认证信息成功 - SessionID: ${authData.session_id.substring(0, 8)}...`);
// 第二步:请求CAPTCHA token(添加智能延迟)
log('info', '🔑 第二步:请求CAPTCHA token...');
// 模拟真实用户的思考延迟(优化:减少延迟)
await new Promise(resolve => setTimeout(resolve, 50 + Math.random() * 100));
const captchaResponse = await page.evaluate((authData) => {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('session_id', authData.session_id);
formData.append('token', authData.token);
// 模拟网络延迟
setTimeout(() => {
fetch('https://data.toolbaz.com/token.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(response => {
resolve(response);
})
.catch(error => {
reject(error);
});
}, 50 + Math.random() * 50); // 50-100ms随机延迟
});
}, authData);
if (!captchaResponse || !captchaResponse.token) {
throw new Error('CAPTCHA token获取失败: ' + JSON.stringify(captchaResponse));
}
log('info', `✅ CAPTCHA token获取成功: ${captchaResponse.token.substring(0, 20)}...`);
// 第三步:处理文本并提交(添加用户行为模拟)
log('info', '📝 第三步:处理文本并提交...');
// 模拟用户输入思考和准备时间(优化:减少延迟)
const thinkingDelay = 200 + Math.random() * 300; // 200-500ms
log('debug', `🤔 模拟用户思考时间: ${thinkingDelay}ms`);
await new Promise(resolve => setTimeout(resolve, thinkingDelay));
const finalResponse = await page.evaluate((promptText, modelText, captchaToken, authData, debugMode) => {
const debug = debugMode || false;
const startTime = Date.now();
// 模拟 encodeHtmlEntities 函数
function encodeHtmlEntities(text) {
const entityMap = {
'"': "&quot;",
"&": "&amp;",
"'": "&#x27;",
"<": "&lt;",
">": "&gt;",
"`": "&#x60;",
};
return text.replace(/[&<>"'`]/g, function (match) {
return entityMap[match];
}) + "ㅤ"; // 添加特殊字符
}
// 处理输入文本
const inp_val = encodeHtmlEntities(promptText.trim());
const text = inp_val; // 因为没有advanced_fields_text,直接使用处理后的文本
if (debug) {
console.log('📝 最终处理的文本:', text);
console.log('🔑 使用CAPTCHA token:', captchaToken);
console.log('🆔 使用Session ID:', authData.session_id);
}
// 构造最终请求数据
const requestData = {
text: text,
capcha: captchaToken, // 注意是 capcha 不是 captcha
model: modelText || 'gemini-2.5-flash',
session_id: authData.session_id
};
const urlParams = new URLSearchParams();
Object.entries(requestData).forEach(([key, value]) => {
urlParams.append(key, value);
});
const headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'https://toolbaz.com',
'Referer': 'https://toolbaz.com/writer/ai-writer',
'User-Agent': navigator.userAgent,
'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'X-Requested-With': 'XMLHttpRequest'
};
if (debug) {
console.log('🌐 发送最终请求到 writing.php');
console.log('📋 请求参数:', urlParams.toString());
}
return fetch('https://data.toolbaz.com/writing.php', {
method: 'POST',
headers: headers,
body: urlParams.toString(),
credentials: 'include'
})
.then(response => {
const responseTime = Date.now() - startTime;
if (debug) {
console.log('📡 响应状态:', response.status);
console.log('📡 响应时间:', responseTime + 'ms');
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.text();
})
.then(text => {
const responseTime = Date.now() - startTime;
if (debug) {
console.log('📡 响应长度:', text.length);
console.log('📡 响应前200字符:', text.substring(0, 200));
}
return {
success: true,
data: text,
responseTime
};
})
.catch(error => {
const responseTime = Date.now() - startTime;
if (debug) {
console.error('❌ 请求失败:', error);
}
return {
success: false,
error: error.message,
responseTime
};
});
}, prompt, model, captchaResponse.token, authData, CONFIG.DEBUG_LEVEL === 'verbose');
const totalTime = Date.now() - startTime;
if (finalResponse.success) {
log('info', '✅ 双重认证AJAX请求成功!');
log('info', `📋 响应长度: ${finalResponse.data.length}`);
log('info', `⏱️ 总响应时间: ${totalTime}ms`);
updatePerformanceStats('ajax', true, totalTime);
// 返回一致的数据结构:包含success属性的对象
return {
success: true,
data: finalResponse.data,
responseTime: totalTime
};
} else {
// 返回包含错误信息的对象,而不是抛出异常
return {
success: false,
error: finalResponse.error,
responseTime: totalTime
};
}
} catch (error) {
const totalTime = Date.now() - startTime;
log('error', `❌ 双重认证AJAX请求失败: ${error.message}`);
log('debug', `⏱️ 失败响应时间: ${totalTime}ms`);
updatePerformanceStats('ajax', false, totalTime);
if (retryCount < CONFIG.MAX_RETRIES) {
log('info', `🔄 重试第 ${retryCount + 1} 次...`);
await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY_DELAY));
return await performAjaxRequest(page, prompt, model, sessionId, retryCount + 1);
}
throw error;
}
try {
// 设置网络监听
const networkRequests = [];
const originalFetch = page.evaluateHandle(() => {
const originalFetch = window.fetch;
const requests = [];
window.fetch = function(...args) {
const [url, options] = args;
const startTime = Date.now();
requests.push({
url,
method: options?.method || 'GET',
headers: options?.headers,
body: options?.body,
startTime
});
return originalFetch.apply(this, args).then(response => {
const req = requests[requests.length - 1];
req.responseTime = Date.now() - startTime;
req.status = response.status;
req.statusText = response.statusText;
req.responseURL = response.url;
return response;
});
};
return { requests, originalFetch };
});
const response = await page.evaluate(async (promptText, modelText, sessId, debugMode) => {
const debug = debugMode || false;
const startTime = Date.now(); // 将 startTime 移到函数开始处
if (debug) {
console.log('🔍 浏览器端调试开始');
console.log(' promptText:', promptText);
console.log(' modelText:', modelText);
console.log(' sessId:', sessId);
}
try {
// 增强的请求验证
const requestData = {
text: promptText,
model: modelText || 'gemini-2.5-flash'
};
if (sessId) {
requestData.session_id = sessId;
if (debug) console.log('✅ 添加了session_id:', sessId);
} else {
if (debug) console.log('⚠️ 没有session_id,将发送无session请求');
}
// 转换为URLSearchParams
const urlParams = new URLSearchParams();
Object.entries(requestData).forEach(([key, value]) => {
urlParams.append(key, value);
});
if (debug) {
console.log('📤 最终发送的URL参数:');
for (const [key, value] of urlParams.entries()) {
console.log(` - ${key}:`, value);
}
}
// 构造增强的headers
const headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'https://toolbaz.com',
'Referer': 'https://toolbaz.com/writer/ai-writer',
'User-Agent': navigator.userAgent,
'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'X-Requested-With': 'XMLHttpRequest'
};
if (debug) {
console.log('🌐 发送fetch请求到 https://data.toolbaz.com/writing.php');
console.log('📋 请求Headers:', headers);
console.log('📋 请求Body:', urlParams.toString());
}
// 发送请求
const response = await fetch('https://data.toolbaz.com/writing.php', {
method: 'POST',
headers: headers,
body: urlParams.toString(),
credentials: 'include'
});
const responseTime = Date.now() - startTime;
if (debug) {
console.log('📡 AJAX响应状态:', response.status);
console.log('📡 AJAX响应类型:', response.type);
console.log('📡 AJAX响应URL:', response.url);
console.log('📡 响应时间:', responseTime + 'ms');
}
// 详细响应分析
const responseHeaders = {};
for (const [key, value] of response.headers.entries()) {
responseHeaders[key] = value;
}
if (debug) {
console.log('📡 AJAX响应头:', responseHeaders);
}
if (!response.ok) {
const errorText = await response.text();
if (debug) {
console.error('❌ HTTP错误响应内容:', errorText);
}
// 分析错误类型
let errorAnalysis = 'Unknown Error';
if (errorText.includes('CAPTCHA')) {
errorAnalysis = 'CAPTCHA Token Error';
} else if (errorText.includes('session')) {
errorAnalysis = 'Session Error';
} else if (response.status === 429) {
errorAnalysis = 'Rate Limited';
} else if (response.status >= 500) {
errorAnalysis = 'Server Error';
}
throw new Error(`HTTP ${response.status}: ${response.statusText} - ${errorAnalysis} - ${errorText}`);
}
const text = await response.text();
if (debug) {
console.log('📡 AJAX响应长度:', text.length);
console.log('📡 AJAX响应前200字符:', text.substring(0, 200));
}
// 响应内容分析
const responseAnalysis = {
length: text.length,
isEmpty: text.length === 0,
containsHtml: /<[^>]+>/.test(text),
containsError: /error|fail|invalid|unauthorized/i.test(text),
containsSuccess: /success|ok|complete/i.test(text),
preview: text.substring(0, 500)
};
if (debug) {
console.log('📊 响应分析:', responseAnalysis);
}
return {
success: true,
data: text,
analysis: responseAnalysis,
responseTime,
headers: responseHeaders
};
} catch (error) {
if (debug) {
console.error('❌ AJAX请求失败:', error);
console.error('❌ 错误堆栈:', error.stack);
}
return {
success: false,
error: error.message,
stack: error.stack,
responseTime: Date.now() - startTime
};
}
}, prompt, model, sessionId, CONFIG.DEBUG_LEVEL === 'verbose');
const totalTime = Date.now() - startTime;
log('verbose', '🏁 浏览器端调试结束');
log('verbose', '📋 Node.js收到的响应:', response);
log('debug', `⏱️ 总响应时间: ${totalTime}ms`);
// 更新性能统计
updatePerformanceStats('ajax', response.success, totalTime);
return response;
} catch (error) {
log('error', `❌ AJAX请求执行失败: ${error.message}`);
updatePerformanceStats('ajax', false, Date.now() - startTime);
// 如果是重试范围内,尝试重试
if (retryCount < CONFIG.MAX_RETRIES) {
log('info', `🔄 准备重试 (${retryCount + 1}/${CONFIG.MAX_RETRIES})...`);
await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY_DELAY));
return performAjaxRequest(page, prompt, model, sessionId, retryCount + 1);
}
return { success: false, error: error.message, stack: error.stack };
}
}
async function captureAuthCredentials(page) {
log('info', '🔐 捕获UI成功后的认证凭据...');
const credentials = await page.evaluate(() => {
const results = {
timestamp: Date.now(),
cookies: document.cookie,
localStorage: {},
sessionStorage: {},
hiddenInputs: {},
metaTags: {},
captchaToken: null,
sessionId: null,
otherTokens: {}
};
// 捕获localStorage
try {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && (key.includes('token') || key.includes('session') || key.includes('auth') || key.includes('sid') || key.includes('csrf'))) {
results.localStorage[key] = localStorage.getItem(key);
}
}
} catch (e) {}
// 捕获sessionStorage
try {
for (let i = 0; i < sessionStorage.length; i++) {
const key = sessionStorage.key(i);
if (key && (key.includes('token') || key.includes('session') || key.includes('auth') || key.includes('sid') || key.includes('csrf'))) {
results.sessionStorage[key] = sessionStorage.getItem(key);
}
}
} catch (e) {}
// 捕获所有hidden input
const hiddenInputs = document.querySelectorAll('input[type="hidden"]');
hiddenInputs.forEach(input => {
if (input.name && (input.name.includes('token') || input.name.includes('session') || input.name.includes('auth') || input.name.includes('sid') || input.name.includes('csrf') || input.name.includes('captcha'))) {
results.hiddenInputs[input.name] = input.value;
}
// 特殊查找常见的token字段
if (input.name === 'g-recaptcha-response' || input.id === 'g-recaptcha-response') {
results.captchaToken = input.value;
}
if (input.name === 'session_id' || input.id === 'session_id' || input.name === 'sid' || input.id === 'sid') {
results.sessionId = input.value;
}
});
// 捕获meta tags中的token
const metaTags = document.querySelectorAll('meta[name*="token"], meta[name*="csrf"], meta[property*="token"]');
metaTags.forEach(meta => {
results.metaTags[meta.name || meta.property] = meta.content;
});
// 查找window对象中的全局变量
try {
if (window.recaptchaToken) results.captchaToken = window.recaptchaToken;
if (window.session_id) results.sessionId = window.session_id;
if (window.csrfToken) results.otherTokens.csrfToken = window.csrfToken;
} catch (e) {}
return results;
});
log('info', `🔐 捕获到凭据: cookies=${!!credentials.cookies}, captcha=${!!credentials.captchaToken}, sessionId=${!!credentials.sessionId}`);
log('debug', '🔐 详细凭据:', credentials);
return credentials;
}
async function performAjaxRequestWithCredentials(page, prompt, model, credentials) {
log('info', '🚀 使用捕获的凭据执行AJAX请求...');
const response = await page.evaluate(async (promptText, modelText, authData, debugMode) => {
const debug = debugMode || false;
const startTime = Date.now();
if (debug) {
console.log('🔐 使用凭据的AJAX请求开始');
console.log(' authData:', authData);
}
try {
const requestData = {
text: promptText,
model: modelText || 'gemini-2.5-flash'
};
// 使用捕获的凭据
if (authData.sessionId) {
requestData.session_id = authData.sessionId;
if (debug) console.log('✅ 使用捕获的session_id:', authData.sessionId);
}
// 如果有captcha token,添加到请求中
if (authData.captchaToken) {
requestData['g-recaptcha-response'] = authData.captchaToken;
if (debug) console.log('✅ 使用捕获的captcha token');
}
// 添加其他可能的token
Object.entries(authData.otherTokens).forEach(([key, value]) => {
requestData[key] = value;
});
const urlParams = new URLSearchParams();
Object.entries(requestData).forEach(([key, value]) => {
urlParams.append(key, value);
});
const headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'https://toolbaz.com',
'Referer': 'https://toolbaz.com/writer/ai-writer',
'User-Agent': navigator.userAgent,
'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'X-Requested-With': 'XMLHttpRequest'
};
if (debug) {
console.log('🌐 发送带凭据的fetch请求');
console.log('📋 请求参数:', urlParams.toString());
}
const response = await fetch('https://data.toolbaz.com/writing.php', {
method: 'POST',
headers: headers,
body: urlParams.toString(),
credentials: 'include'
});
const responseTime = Date.now() - startTime;
if (debug) {
console.log('📡 响应状态:', response.status);
console.log('📡 响应时间:', responseTime + 'ms');
}
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);
}
const text = await response.text();
return {
success: true,
data: text,
responseTime
};
} catch (error) {
return {
success: false,
error: error.message,
responseTime: Date.now() - startTime
};
}
}, prompt, model, credentials, CONFIG.DEBUG_LEVEL === 'verbose');
return response;
}
async function cleanupHtmlResponse(html) {
return html
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<\/p>/gi, '\n\n')
.replace(/<[^>]+>/g, '')
.replace(/&nbsp;/g, ' ')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#039;/g, "'")
.trim();
}
function cleanPromptPrefix(prompt) {
const prefixPatterns = [
/^Write a code snippet that does the following in the specified language\s*:\s*/i,
/^Generate an original and engaging piece of writing on the following topic\s*:\s*/i,
/^Translate the following text to\s*\w+\s*:\s*/i,
/^Summarize the following text\s*:\s*/i,
/^Explain the following concept\s*:\s*/i,
/^Write a function that\s*:\s*/i,
/^Create a\s*\w+\s*that\s*:\s*/i
];
let cleanedPrompt = prompt;
for (const pattern of prefixPatterns) {
if (pattern.test(cleanedPrompt)) {
cleanedPrompt = cleanedPrompt.replace(pattern, '');
break;
}
}
return cleanedPrompt.trim();
}
async function chatWithBrowser(prompt, model) {
const startTime = Date.now();
let ajaxSuccess = false;
let uiFallback = false;
try {
log('info', `📝 开始聊天请求: ${prompt.substring(0, 50)}...`);
const browser = await initBrowser();
const page = await browser.newPage();
// 设置页面配置
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36');
// 过滤前缀文本
const cleanedPrompt = cleanPromptPrefix(prompt);
if (cleanedPrompt !== prompt) {
log('info', `🧹 已过滤前缀: ${prompt.substring(0, 50)}... -> ${cleanedPrompt.substring(0, 50)}...`);
}
try {
// 访问页面获取必要的会话信息
log('info', '🌐 访问页面获取会话信息...');
await page.goto('https://toolbaz.com/writer/ai-writer', {
waitUntil: 'networkidle2',
timeout: 30000
});
// 等待页面完全加载
await page.waitForSelector('#input', { timeout: 10000 });
log('debug', '✅ 页面加载完成');
log('info', '🔍 开始增强的session ID检测...');
// 简化的session检测(优化:减少复杂检测)
const sessionInfo = await page.evaluate((debugMode) => {
const debug = debugMode || false;
if (debug) {
console.log('🔍 简化版session ID检测开始');
}
const results = {
timestamp: Date.now(),
url: window.location.href,
finalSessionId: null,
detectionMethods: []
};
// 仅保留最有效的方法,注释掉复杂的检测
let sessionId = null;
let detectionMethod = null;
// 仅保留最可能成功的方法
const simpleMethods = [
() => {
const input = document.querySelector('input[name="session_id"]');
return input?.value || null;
},
() => {
const elem = document.querySelector('#session_id');
return elem?.value || null;
},
() => sessionStorage.getItem('session_id'),
() => window.session_id
];
for (let i = 0; i < simpleMethods.length; i++) {
const result = simpleMethods[i]();
if (result && result.length > 0) {
sessionId = result;
detectionMethod = `简化方法${i + 1}`;
if (debug) console.log(`✅ ${detectionMethod}成功:`, result);
break;
}
}
// 注释掉:复杂的检测逻辑(当前占用大量资源但效果不佳)
/*
// 原有的复杂检测逻辑已注释
const allElements = document.querySelectorAll('*');
const sessionElements = [];
...省略...
*/
results.finalSessionId = sessionId;
if (detectionMethod) {
results.detectionMethods.push(detectionMethod);
}
if (debug) {
console.log('🔍 简化版session ID结果:', sessionId);
}
return results;
}, CONFIG.DEBUG_LEVEL === 'verbose');
log('info', '📊 简化版Session检测结果:');
if (sessionInfo.finalSessionId) {
log('info', `✅ 找到session ID: "${sessionInfo.finalSessionId.substring(0, 20)}..."`);
log('info', `✅ session ID长度: ${sessionInfo.finalSessionId.length}`);
log('info', `✅ 检测方法: ${sessionInfo.detectionMethods.join(', ')}`);
} else {
log('warn', '❌ 未找到有效的session ID(简化检测)');
log('debug', '📋 简化检测结果:', sessionInfo);
}
const sessionId = sessionInfo.finalSessionId;
// 尝试直接AJAX请求(混合优化模式)
log('info', '📡 尝试直接AJAX请求(混合优化模式)...');
const ajaxResponse = await performAjaxRequest(page, cleanedPrompt, model, sessionId);
// 修复后的响应处理逻辑
if (ajaxResponse && ajaxResponse.success) {
// 清理HTML响应
const cleanedResponse = await cleanupHtmlResponse(ajaxResponse.data);
// 优化的响应验证:更宽松但准确的验证逻辑
const isRealError = (text) => {
const errorPatterns = [
/session.*invalid/i,
/session.*expired/i,
/error.*session/i,
/unauthorized/i,
/^<[^>]*>$/ // 纯HTML标签无内容
];
// 只有真正包含错误信息的响应才被认为是错误
return errorPatterns.some(pattern => pattern.test(text)) || text.trim().length === 0;
};
if (!isRealError(cleanedResponse)) {
ajaxSuccess = true;
const totalTime = Date.now() - startTime;
log('info', `✅ AJAX响应成功: ${cleanedResponse.substring(0, 100)}...`);
log('info', `✅ 响应长度: ${cleanedResponse.length}`);
log('info', `⏱️ 总处理时间: ${totalTime}ms`);
updatePerformanceStats('ajax', true, totalTime);
return cleanedResponse;
} else {
log('warn', '⚠️ AJAX响应包含真实错误,准备回退到UI方法...');
log('warn', ' 响应预览:', cleanedResponse.substring(0, 200));
}
} else {
log('warn', '❌ AJAX请求失败:', ajaxResponse?.error || '未知错误');
if (ajaxResponse?.responseTime) {
log('debug', `⏱️ 失败请求耗时: ${ajaxResponse.responseTime}ms`);
}
log('info', '🔄 准备回退到UI方法...');
}
// 混合模式:优先使用缓存的凭据,再获取新凭据
uiFallback = true;
log('info', '🔄 混合模式:智能凭据管理...');
let credentialsToUse = null;
// 1. 首先检查缓存的凭据
if (credentialCache.isValid()) {
credentialsToUse = credentialCache.data;
log('info', '🔐 使用缓存的凭据尝试AJAX请求...');
const cachedAjaxResponse = await performAjaxRequestWithCredentials(page, cleanedPrompt, model, credentialsToUse);
if (cachedAjaxResponse && cachedAjaxResponse.success) {
const totalTime = Date.now() - startTime;
log('info', `✅ 缓存凭据AJAX请求成功: ${cachedAjaxResponse.data.substring(0, 100)}...`);
log('info', `⏱️ 缓存模式处理时间: ${totalTime}ms`);
updatePerformanceStats('ajax', true, totalTime);
return cachedAjaxResponse.data;
} else {
log('warn', '❌ 缓存凭据失效,清空缓存并重新获取...');
credentialCache.clear();
}
}
// 2. 捕获当前页面的认证凭据
const currentCredentials = await captureAuthCredentials(page);
// 3. 尝试使用当前凭据进行AJAX请求
if (currentCredentials && (currentCredentials.captchaToken || currentCredentials.sessionId)) {
log('info', '🔐 发现新凭据,尝试凭据AJAX请求...');
const credAjaxResponse = await performAjaxRequestWithCredentials(page, cleanedPrompt, model, currentCredentials);
if (credAjaxResponse && credAjaxResponse.success) {
const totalTime = Date.now() - startTime;
log('info', `✅ 新凭据AJAX请求成功: ${credAjaxResponse.data.substring(0, 100)}...`);
log('info', `⏱️ 混合模式处理时间: ${totalTime}ms`);
// 缓存有效的凭据
credentialCache.set(currentCredentials, 30);
updatePerformanceStats('ajax', true, totalTime);
return credAjaxResponse.data;
} else {
log('warn', '❌ 新凭据AJAX请求失败,继续执行UI交互...');
}
} else {
log('info', '🔐 未发现有效凭据,直接执行UI交互...');
}
// 执行UI交互获取响应
log('info', '🖱️ 执行UI交互...');
// 增强的UI交互
await page.evaluate(() => {
const input = document.querySelector('#input');
if (input) {
input.value = '';
input.focus();
}
});
await page.type('#input', cleanedPrompt, { delay: 50 });
await page.click('#main_btn');
log('debug', '⏳ 等待UI响应...');
// 等待响应出现,增加超时检测
const uiResponse = await Promise.race([
page.waitForFunction(() => {
const output = document.querySelector('#output');
return output && output.innerText && output.innerText.trim().length > 0;
}, { timeout: 60000 }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('UI响应超时')), 60000)
)
]).then(async () => {
const text = await page.evaluate(() => {
const output = document.querySelector('#output');
return output ? output.innerText.trim() : '';
});
return text;
});
if (uiResponse && uiResponse.length > 0) {
const totalTime = Date.now() - startTime;
log('info', `✅ UI响应成功: ${uiResponse.substring(0, 100)}...`);
log('info', `✅ 响应长度: ${uiResponse.length}`);
log('info', `⏱️ 总处理时间: ${totalTime}ms`);
// UI成功后,再次尝试用这个页面的凭据进行AJAX请求,以备后续使用
log('info', '🔐 UI成功后,捕获并缓存凭据...');
const postUICredentials = await captureAuthCredentials(page);
// 如果捕获到有效凭据,进行缓存
if (postUICredentials && (postUICredentials.captchaToken || postUICredentials.sessionId)) {
credentialCache.set(postUICredentials, 60); // UI成功的凭据缓存60分钟
log('info', '✅ UI成功后的凭据已缓存');
}
updatePerformanceStats('ui', true, totalTime);
return uiResponse;
} else {
throw new Error('UI方法也未能获取有效响应');
}
} catch (error) {
log('error', `❌ 聊天请求失败: ${error.message}`);
log('error', `❌ 错误详情:`, error.stack);
// 记录失败统计
updatePerformanceStats(uiFallback ? 'ui' : 'ajax', false, Date.now() - startTime);
// 尝试最后一次紧急恢复
log('warn', '🚨 尝试紧急恢复...');
try {
await page.goto('https://toolbaz.com/writer/ai-writer', {
waitUntil: 'networkidle2',
timeout: 15000
});
await page.waitForSelector('#input', { timeout: 5000 });
await page.type('#input', cleanedPrompt, { delay: 100 });
await page.click('#main_btn');
const emergencyResponse = await page.waitForFunction(() => {
const output = document.querySelector('#output');
return output && output.innerText && output.innerText.trim().length > 0;
}, { timeout: 30000 }).then(async () => {
return await page.evaluate(() => document.querySelector('#output')?.innerText?.trim() || '');
});
if (emergencyResponse && emergencyResponse.length > 0) {
log('warn', '✅ 紧急恢复成功:', emergencyResponse.substring(0, 100) + '...');
return emergencyResponse;
}
} catch (recoveryError) {
log('error', `❌ 紧急恢复也失败了: ${recoveryError.message}`);
}
throw error;
} finally {
await page.close();
}
} catch (error) {
const totalTime = Date.now() - startTime;
log('error', `💥 完全聊天失败: ${error.message}`);
log('error', `💥 总耗时: ${totalTime}ms`);
updatePerformanceStats('unknown', false, totalTime);
throw error;
}
}
async function generateImageWithBrowser(prompt, model, size) {
const browser = await initBrowser();
const page = await browser.newPage();
try {
console.log(`🎨 图片生成: ${prompt.substring(0, 50)}...`);
await page.goto('https://toolbaz.com/image/ai-image-generator', { waitUntil: 'networkidle2', timeout: 30000 });
// 等待输入框
await page.waitForSelector('#prompt', { timeout: 10000 });
// 输入提示词
await page.click('#prompt', { clickCount: 3 });
await page.type('#prompt', prompt);
// 选择模型(如果需要)
if (model) {
try {
await page.select('#model', model);
} catch (e) {
console.log('⚠️ 模型选择失败,使用默认');
}
}
// 选择尺寸(如果需要)
if (size) {
try {
await page.select('#size', size);
} catch (e) {
console.log('⚠️ 尺寸选择失败,使用默认');
}
}
// 点击生成按钮
await page.click('#generat');
// 等待图片生成(等待结果区域出现图片)
await page.waitForSelector('img[src*="generated"], img[src*="image"]', { timeout: 120000 });
// 提取图片 URL
const imageUrl = await page.evaluate(() => {
const imgs = document.querySelectorAll('img[src*="generated"], img[src*="image"], img[src*="toolbaz"]');
for (let img of imgs) {
if (img.src && img.src.startsWith('http') && !img.src.includes('logo')) {
return img.src;
}
}
return null;
});
if (!imageUrl) {
throw new Error('未找到生成的图片');
}
console.log(`✅ 图片: ${imageUrl}`);
return imageUrl;
} catch (error) {
console.error(`❌ 图片生成失败: ${error.message}`);
throw error;
} finally {
await page.close();
}
}
async function handleChatCompletions(req, res) {
if (!verifyAuth(req)) return sendError(res, 401, 'Unauthorized');
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', async () => {
try {
const data = JSON.parse(body);
const messages = data.messages || [];
const lastMessage = messages[messages.length - 1]?.content || "Hello";
const model = data.model || CONFIG.DEFAULT_CHAT_MODEL;
const responseText = await chatWithBrowser(lastMessage, model);
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*'
});
const chunkSize = 10;
for (let i = 0; i < responseText.length; i += chunkSize) {
const content = responseText.slice(i, i + chunkSize);
res.write(`data: ${JSON.stringify({
id: `chatcmpl-${Date.now()}`,
object: 'chat.completion.chunk',
created: Math.floor(Date.now() / 1000),
model,
choices: [{ index: 0, delta: { content }, finish_reason: null }]
})}\n\n`);
await new Promise(r => setTimeout(r, 20));
}
res.write('data: [DONE]\n\n');
res.end();
} catch (error) {
console.error('聊天请求失败:', error);
sendError(res, 500, error.message);
}
});
}
async function handleImageGenerations(req, res) {
if (!verifyAuth(req)) return sendError(res, 401, 'Unauthorized');
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', async () => {
try {
const data = JSON.parse(body);
const prompt = data.prompt;
const model = data.model || "FLUX-1-schnell";
const imageUrl = await generateImageWithBrowser(prompt, model);
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify({ created: Math.floor(Date.now() / 1000), data: [{ url: imageUrl }] }));
} catch (error) {
console.error('图片请求失败:', error);
sendError(res, 500, error.message);
}
});
}
// 模型缓存(避免频繁重新提取)
let modelCache = {
data: null,
timestamp: null,
expiresAt: null,
isValid: function() {
return this.data && this.expiresAt && Date.now() < this.expiresAt;
},
set: function(models, ttlMinutes = 60) {
this.data = models;
this.timestamp = Date.now();
this.expiresAt = Date.now() + (ttlMinutes * 60 * 1000);
log('info', `🔐 模型列表已缓存,有效期 ${ttlMinutes} 分钟`);
},
clear: function() {
this.data = null;
this.timestamp = null;
this.expiresAt = null;
log('info', '🔐 模型缓存已清空');
}
};
async function handleModels(req, res) {
try {
log('info', '🔍 开始提取网站模型列表...');
// 检查缓存
if (modelCache.isValid()) {
log('info', `✅ 使用缓存模型列表 (${modelCache.data.length} 个模型)`);
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify({
object: 'list',
data: modelCache.data
}));
return;
}
// 尝试多种方法获取模型列表
let models = null;
let extractionMethod = '';
// 方法1: 尝试访问网站获取最新模型
try {
log('info', '🌐 方法1: 从网站提取模型列表...');
models = await extractModelsFromWebsite();
extractionMethod = 'website';
} catch (websiteError) {
log('warn', `⚠️ 网站提取失败: ${websiteError.message}`);
// 方法2: 尝试AJAX方式获取模型
try {
log('info', '📡 方法2: 尝试AJAX方式获取模型...');
models = await extractModelsViaAjax();
extractionMethod = 'ajax';
} catch (ajaxError) {
log('warn', `⚠️ AJAX提取失败: ${ajaxError.message}`);
// 方法3: 使用增强的静态配置
try {
log('info', '🔧 方法3: 使用增强静态配置...');
models = await getEnhancedStaticModels();
extractionMethod = 'enhanced-static';
} catch (staticError) {
log('error', `❌ 所有方法都失败了,使用基础静态配置: ${staticError.message}`);
models = getBasicStaticModels();
extractionMethod = 'basic-static';
}
}
}
// 更新缓存
if (models && models.length > 0) {
modelCache.set(models, 60); // 缓存60分钟
log('info', `✅ 成功提取 ${models.length} 个模型 (方法: ${extractionMethod})`);
// 记录提取到的模型(调试用)
if (CONFIG.DEBUG_LEVEL === 'verbose') {
models.forEach((model, index) => {
log('verbose', ` ${index + 1}. ${model.name} (${model.id})`);
});
}
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify({
object: 'list',
data: models,
metadata: {
extraction_method: extractionMethod,
extracted_at: new Date().toISOString(),
total_models: models.length
}
}));
} else {
throw new Error('没有获取到任何模型数据');
}
} catch (error) {
log('error', `❌ 模型提取完全失败: ${error.message}`);
// 最后的兜底策略
const fallbackModels = getBasicStaticModels();
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify({
object: 'list',
data: fallbackModels,
metadata: {
extraction_method: 'fallback',
error: error.message,
extracted_at: new Date().toISOString()
}
}));
}
}
// 新增:从网站提取模型的专用函数
async function extractModelsFromWebsite() {
const browser = await initBrowser();
const page = await browser.newPage();
try {
// 设置更长的超时时间
page.setDefaultTimeout(30000);
log('info', '🌐 访问写作页面...');
await page.goto('https://toolbaz.com/writer/ai-writer', {
waitUntil: 'networkidle2',
timeout: 30000
});
// 等待页面加载
await page.waitForTimeout(2000); // 给页面一些加载时间
log('info', '🔍 查找模型选择器...');
// 尝试多种选择器
const modelSelectors = [
'#selected-model',
'.model-selector',
'[data-model]',
'.model-dropdown-trigger'
];
let selectorFound = null;
for (const selector of modelSelectors) {
try {
await page.waitForSelector(selector, { timeout: 5000 });
selectorFound = selector;
log('info', `✅ 找到模型选择器: ${selector}`);
break;
} catch (e) {
continue;
}
}
if (!selectorFound) {
throw new Error('未找到模型选择器');
}
// 尝试点击选择器
try {
await page.click(selectorFound);
log('info', '✅ 成功点击模型选择器');
} catch (e) {
log('warn', `⚠️ 点击选择器失败,尝试直接查找模型: ${e.message}`);
}
// 等待下拉列表或模型列表出现
await page.waitForTimeout(1000);
// 查找模型选项
const models = await page.evaluate(() => {
const modelList = [];
// 尝试多种模型选项选择器
const modelSelectors = [
'.model-dropdown .model-option',
'.model-list .model-item',
'[data-model-option]',
'.model-selector option',
'select[name="model"] option',
'.ai-models .model',
'.available-models .model-item'
];
for (const selector of modelSelectors) {
try {
const elements = document.querySelectorAll(selector);
if (elements.length > 0) {
log(`✅ 使用选择器 ${selector} 找到 ${elements.length} 个模型选项`);
elements.forEach((element, index) => {
let name = '';
let id = '';
// 尝试获取模型名称
const nameElement = element.querySelector('.model-name') ||
element.querySelector('.name') ||
element.querySelector('span') ||
element;
name = nameElement.innerText || nameElement.textContent ||
nameElement.value || nameElement.getAttribute('data-model') || '';
name = name.trim();
if (name && name.length > 0) {
id = name.toLowerCase()
.replace(/[^a-z0-9\s]/g, '')
.replace(/\s+/g, '-')
.substring(0, 50);
modelList.push({
id: id || `model-${index}`,
name: name,
icon: element.querySelector('.model-icon, .icon')?.src || '',
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz'
});
}
});
// 如果找到了模型,就不再尝试其他选择器
if (modelList.length > 0) {
break;
}
}
} catch (e) {
continue;
}
}
return modelList;
});
if (models.length === 0) {
throw new Error('页面中没有找到模型选项');
}
return models;
} finally {
await page.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
}
}
// 新增:通过AJAX方式获取模型
async function extractModelsViaAjax() {
const browser = await initBrowser();
const page = await browser.newPage();
try {
await page.goto('https://toolbaz.com/writer/ai-writer', {
waitUntil: 'networkidle2',
timeout: 20000
});
const models = await page.evaluate(async () => {
return new Promise((resolve) => {
// 尝试通过AJAX获取模型列表
const modelEndpoints = [
'https://data.toolbaz.com/models.php',
'https://data.toolbaz.com/api/models',
'/api/models',
'/models'
];
let requestCount = 0;
const tryFetch = (url) => {
return fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'Referer': window.location.href
}
})
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error(`HTTP ${response.status}`);
})
.then(data => {
if (Array.isArray(data) && data.length > 0) {
return data.map((model, index) => ({
id: model.id || model.name?.toLowerCase().replace(/[^a-z0-9]/g, '-') || `model-${index}`,
name: model.name || model.displayName || `Model ${index}`,
icon: model.icon || '',
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz'
}));
}
throw new Error('无效的模型数据');
});
};
// 尝试所有端点
const promises = modelEndpoints.map(url =>
tryFetch(url).catch(err => {
requestCount++;
if (requestCount === modelEndpoints.length) {
throw new Error('所有AJAX端点都失败了');
}
return null;
})
);
Promise.all(promises)
.then(results => {
const validResults = results.filter(r => r !== null);
if (validResults.length > 0) {
resolve(validResults[0]); // 使用第一个成功的结果
} else {
throw new Error('没有成功的AJAX请求');
}
})
.catch(() => resolve([])); // 返回空数组表示失败
});
});
if (models.length === 0) {
throw new Error('AJAX方式未获取到模型');
}
return models;
} finally {
await page.close().catch(e => log('warn', `页面关闭失败: ${e.message}`));
}
}
// 新增:增强的静态模型配置
async function getEnhancedStaticModels() {
// 结合配置中的模型和常见的AI模型
const enhancedModels = [
// 聊天模型
...CONFIG.CHAT_MODELS.map(name => ({
id: name.toLowerCase().replace(/[^a-z0-9]/g, '-'),
name: name,
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz-2api'
})),
// 图片模型
...CONFIG.IMAGE_MODELS.map(name => ({
id: name.toLowerCase().replace(/[^a-z0-9]/g, '-'),
name: name,
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz-2api'
})),
// 额外的常见模型
{
id: 'gpt-4',
name: 'GPT-4',
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz-2api'
},
{
id: 'gpt-3.5-turbo',
name: 'GPT-3.5 Turbo',
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz-2api'
},
{
id: 'claude-3-sonnet',
name: 'Claude 3 Sonnet',
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz-2api'
}
];
// 去重
const uniqueModels = [];
const seenIds = new Set();
for (const model of enhancedModels) {
if (!seenIds.has(model.id)) {
seenIds.add(model.id);
uniqueModels.push(model);
}
}
return uniqueModels;
}
// 新增:基础静态模型配置
function getBasicStaticModels() {
return [...CONFIG.CHAT_MODELS, ...CONFIG.IMAGE_MODELS].map(id => ({
id: id.toLowerCase().replace(/[^a-z0-9]/g, '-'),
name: id,
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'toolbaz-2api'
}));
}
// 新增:测试API端点
async function handleTests(req, res) {
if (!verifyAuth(req)) return sendError(res, 401, 'Unauthorized');
const url = new URL(req.url, `http://${req.headers.host}`);
const testType = url.searchParams.get('type') || 'comprehensive';
log('info', `🧪 收到测试请求: ${testType}`);
try {
let result;
switch (testType) {
case 'config':
result = testBasicConfiguration();
break;
case 'network':
result = await testNetworkConnectivity();
break;
case 'browser':
result = await testBrowserEnvironment();
break;
case 'session':
result = await testSessionDetection();
break;
case 'ajax':
result = await testAjaxRequest();
break;
case 'prefix':
result = await testPromptPrefixFiltering();
break;
case 'comprehensive':
default:
result = await runComprehensiveTests();
break;
}
// 包含性能统计
result.performance = PERFORMANCE_STATS;
result.timestamp = new Date().toISOString();
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify(result, null, 2));
} catch (error) {
log('error', `❌ 测试执行失败: ${error.message}`);
sendError(res, 500, error.message);
}
}
// 新增:状态API端点
async function handleStatus(req, res) {
try {
const status = {
service: CONFIG.PROJECT_NAME,
version: CONFIG.PROJECT_VERSION,
uptime: Math.floor(process.uptime()),
timestamp: new Date().toISOString(),
performance: PERFORMANCE_STATS,
config: {
testing_mode: CONFIG.TESTING_MODE,
debug_level: CONFIG.DEBUG_LEVEL,
max_retries: CONFIG.MAX_RETRIES,
performance_monitoring: CONFIG.PERFORMANCE_MONITORING
},
browser: browser ? 'running' : 'stopped',
cache: {
models: modelCache.isValid() ? 'valid' : 'expired',
credentials: credentialCache.isValid() ? 'valid' : 'expired'
}
};
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify(status, null, 2));
} catch (error) {
log('error', `❌ 状态查询失败: ${error.message}`);
sendError(res, 500, error.message);
}
}
// 新增:模型刷新处理函数
async function handleModelsRefresh(req, res) {
if (!verifyAuth(req)) return sendError(res, 401, 'Unauthorized');
try {
log('info', '🔄 收到模型刷新请求...');
// 清除模型缓存
modelCache.clear();
// 强制重新提取模型
const response = await handleModels(req, res);
log('info', '✅ 模型刷新完成');
} catch (error) {
log('error', `❌ 模型刷新失败: ${error.message}`);
sendError(res, 500, error.message);
}
}
function handleUI(req, res) {
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>`;
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(html);
}
function verifyAuth(req) {
return req.headers['authorization'] === `Bearer ${CONFIG.API_MASTER_KEY}`;
}
function sendError(res, status, message) {
res.writeHead(status, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
res.end(JSON.stringify({ error: { message, type: 'api_error' } }));
}
function handleCORS(req, res) {
res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization' });
res.end();
}
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`);
log('info', `📨 ${req.method} ${url.pathname}`);
if (req.method === 'OPTIONS') return handleCORS(req, res);
if (url.pathname === '/') return handleUI(req, res);
if (url.pathname === '/v1/chat/completions') return handleChatCompletions(req, res);
if (url.pathname === '/v1/images/generations') return handleImageGenerations(req, res);
if (url.pathname === '/v1/models') return await handleModels(req, res);
// 新增的测试和状态端点
if (url.pathname === '/v1/tests') return await handleTests(req, res);
if (url.pathname === '/v1/status') return await handleStatus(req, res);
if (url.pathname === '/v1/models/refresh') return await handleModelsRefresh(req, res);
sendError(res, 404, 'Not Found');
});
process.on('uncaughtException', (error) => {
console.error('💥 未捕获异常:', error.message);
console.error(error.stack);
});
process.on('unhandledRejection', (reason) => {
console.error('💥 未处理Promise:', reason);
});
server.listen(CONFIG.PORT, async () => {
log('info', `🚀 ${CONFIG.PROJECT_NAME} v${CONFIG.PROJECT_VERSION} 运行在 http://localhost:${CONFIG.PORT}`);
log('info', `📝 使用 Puppeteer 控制真实浏览器`);
log('info', `✅ 聊天和绘图功能已就绪`);
// 如果启用测试模式,运行启动测试
if (CONFIG.TESTING_MODE) {
log('info', '🧪 测试模式已启用,开始启动时测试...');
try {
// 运行基本配置测试
const configTest = testBasicConfiguration();
if (configTest.failed === 0) {
log('info', '✅ 配置测试通过');
} else {
log('warn', `⚠️ 配置测试发现 ${configTest.failed} 个问题`);
}
// 可选:运行其他测试(异步)
if (CONFIG.DEBUG_LEVEL === 'verbose') {
setTimeout(async () => {
try {
log('info', '🌐 运行网络连接性测试...');
const networkTest = await testNetworkConnectivity();
const successCount = networkTest.filter(r => r.status === 'success').length;
log('info', `📊 网络测试完成: ${successCount}/${networkTest.length} 连接成功`);
} catch (error) {
log('error', `❌ 网络测试失败: ${error.message}`);
}
}, 2000);
}
} catch (error) {
log('error', `❌ 启动测试失败: ${error.message}`);
}
}
// 显示可用的API端点
log('info', '📚 可用的API端点:');
log('info', ' - GET / : Web界面');
log('info', ' - POST /v1/chat/completions : 聊天API');
log('info', ' - POST /v1/images/generations : 图片生成API');
log('info', ' - GET /v1/models : 模型列表API(带60分钟缓存)');
log('info', ' - GET /v1/models/refresh : 强制刷新模型列表API');
log('info', ' - GET /v1/status : 服务状态API(包含缓存状态)');
log('info', ' - GET /v1/tests : 测试API (?type=config|network|browser|session|ajax|comprehensive)');
// 启动性能统计定时显示
if (CONFIG.PERFORMANCE_MONITORING) {
setInterval(() => {
showPerformanceStats();
}, 300000); // 每5分钟显示一次统计
}
});
process.on('SIGTERM', async () => {
console.log('📴 关闭中...');
if (browser) await browser.close();
server.close(() => process.exit(0));
});