diff --git a/src/tools/account-register/sites/windsurf.js b/src/tools/account-register/sites/windsurf.js index a45c705..b3b4cc3 100644 --- a/src/tools/account-register/sites/windsurf.js +++ b/src/tools/account-register/sites/windsurf.js @@ -19,6 +19,7 @@ const { DEFAULT_CONFIG } = require('../config'); const CardGenerator = require('../../card-generator/generator'); const database = require('../../database'); const CapSolverAPI = require('../utils/capsolver-api'); +const BrowserManager = require('../utils/browser-manager'); class WindsurfRegister { constructor(options = {}) { @@ -28,14 +29,17 @@ class WindsurfRegister { this.human = new HumanBehavior(); this.emailService = new EmailVerificationService(); this.capsolver = new CapSolverAPI(); + + // 浏览器管理器(支持多profile并发) + this.browserManager = new BrowserManager({ + profileId: options.adspowerUserId || process.env.ADSPOWER_USER_ID, + siteName: this.siteName + }); this.browser = null; this.page = null; this.currentStep = 0; this.accountData = null; - // AdsPower配置(支持多profile并发) - this.adspowerUserId = options.adspowerUserId || process.env.ADSPOWER_USER_ID; - // 记录注册时间和额外信息 this.registrationTime = null; this.quotaInfo = null; @@ -190,114 +194,22 @@ class WindsurfRegister { } /** - * 初始化浏览器 - 使用 AdsPower 指纹浏览器 + * 初始化浏览器 */ - async initBrowser(options = {}) { - const puppeteer = require('puppeteer'); - const axios = require('axios'); + async initBrowser() { + const result = await this.browserManager.launch(); + this.browser = result.browser; + this.page = result.page; - logger.info(this.siteName, '启动 AdsPower 指纹浏览器...'); - - // 检查 AdsPower 配置 - const adspowerUserId = this.adspowerUserId; - if (!adspowerUserId) { - logger.error(this.siteName, ''); - logger.error(this.siteName, '❌ 未配置 ADSPOWER_USER_ID'); - logger.error(this.siteName, ''); - logger.error(this.siteName, '请在 .env 文件中配置或传入 options.adspowerUserId:'); - logger.error(this.siteName, 'ADSPOWER_USER_ID=your_profile_id'); - logger.error(this.siteName, ''); - throw new Error('未配置 AdsPower 用户ID'); - } - - const apiBase = process.env.ADSPOWER_API || 'http://local.adspower.net:50325'; - const apiKey = process.env.ADSPOWER_API_KEY; - - const startUrl = `${apiBase}/api/v1/browser/start?user_id=${encodeURIComponent(adspowerUserId)}`; - - // 配置请求头 - const headers = {}; - if (apiKey && apiKey.trim()) { - headers['Authorization'] = `Bearer ${apiKey}`; - logger.info(this.siteName, '✓ 使用 API Key 认证'); - } - - logger.info(this.siteName, ` → 启动 AdsPower 配置: ${adspowerUserId}`); - - try { - const response = await axios.get(startUrl, { headers }); - const data = response.data; - - if (data.code !== 0) { - logger.error(this.siteName, ''); - logger.error(this.siteName, `AdsPower API 返回错误: ${JSON.stringify(data)}`); - logger.error(this.siteName, ''); - logger.error(this.siteName, '解决方案:'); - logger.error(this.siteName, '1. 确保 AdsPower 应用已启动并登录'); - logger.error(this.siteName, '2. 检查配置文件 ID 是否正确: ' + adspowerUserId); - logger.error(this.siteName, '3. 如果需要 API Key,请在 AdsPower 设置中生成'); - logger.error(this.siteName, '4. 尝试在 AdsPower 中手动打开一次浏览器配置'); - logger.error(this.siteName, ''); - throw new Error(`AdsPower 启动失败: ${data.msg || JSON.stringify(data)}`); - } - - // 获取 WebSocket 端点 - const wsEndpoint = data.data.ws && ( - data.data.ws.puppeteer || - data.data.ws.selenium || - data.data.ws.ws || - data.data.ws - ); - - if (!wsEndpoint) { - throw new Error('AdsPower 未返回 WebSocket 端点'); - } - - logger.info(this.siteName, ` → WebSocket: ${wsEndpoint}`); - - // 连接到 AdsPower 浏览器 - this.browser = await puppeteer.connect({ - browserWSEndpoint: wsEndpoint, - defaultViewport: null - }); - - // 获取已存在的页面 - const pages = await this.browser.pages(); - this.page = pages[0] || await this.browser.newPage(); - - // 关闭多余的标签页(AdsPower 需要至少保留一个) - if (pages.length > 1) { - for (let i = 1; i < pages.length; i++) { - try { - await pages[i].close(); - } catch (e) { - // 忽略关闭失败 - } - } - } - - logger.success(this.siteName, '✓ AdsPower 浏览器连接成功'); - logger.info(this.siteName, '✓ 使用真实指纹,可同时绕过 Cloudflare 和 Stripe'); - - logger.info(this.siteName, '等待浏览器完全准备...'); - await this.human.randomDelay(2000, 3000); - - } catch (error) { - logger.error(this.siteName, ''); - logger.error(this.siteName, `❌ AdsPower 连接失败: ${error.message}`); - logger.error(this.siteName, ''); - throw error; - } + logger.info(this.siteName, '等待浏览器完全准备...'); + await this.human.randomDelay(2000, 3000); } /** * 关闭浏览器 */ async closeBrowser() { - if (this.browser) { - await this.browser.close(); - logger.info(this.siteName, '浏览器已关闭'); - } + await this.browserManager.close(); } /** @@ -1417,49 +1329,7 @@ class WindsurfRegister { logger.info(this.siteName, `[步骤 9/${this.getTotalSteps()}] 清理并关闭浏览器`); try { - // 清除所有浏览器数据(类似 Ctrl+Shift+Delete) - logger.info(this.siteName, ' → 清除所有浏览器数据(Cookies、Cache、Storage等)...'); - try { - // 使用 Chrome DevTools Protocol 进行深度清理 - const client = await this.page.target().createCDPSession(); - - // 1. 清除浏览器 Cookies - await client.send('Network.clearBrowserCookies'); - logger.success(this.siteName, ' → ✓ 已清除所有 Cookies'); - - // 2. 清除浏览器缓存 - await client.send('Network.clearBrowserCache'); - logger.success(this.siteName, ' → ✓ 已清除浏览器缓存'); - - // 3. 清除所有存储数据(localStorage, sessionStorage, IndexedDB, WebSQL, Cache Storage, Service Workers) - await client.send('Storage.clearDataForOrigin', { - origin: '*', - storageTypes: 'all' - }); - logger.success(this.siteName, ' → ✓ 已清除所有存储数据'); - - // 4. 额外清理:访问目标网站并清除其存储 - await this.page.goto('https://windsurf.com', { waitUntil: 'domcontentloaded' }); - await this.page.evaluate(() => { - try { - localStorage.clear(); - sessionStorage.clear(); - } catch (e) {} - }); - - // 5. 关闭 CDP 会话 - await client.detach(); - - logger.success(this.siteName, ' → ✓ 浏览器数据清除完成(全新状态)'); - - } catch (e) { - logger.warn(this.siteName, ` → 清除浏览器数据失败: ${e.message}`); - } - - // 关闭浏览器 - logger.info(this.siteName, ' → 关闭浏览器...'); - await this.closeBrowser(); - logger.success(this.siteName, ' → ✓ 浏览器已关闭'); + await this.browserManager.clearAndClose(); this.currentStep = 9; logger.success(this.siteName, `步骤 9 完成`); diff --git a/src/tools/account-register/utils/browser-manager.js b/src/tools/account-register/utils/browser-manager.js new file mode 100644 index 0000000..05db735 --- /dev/null +++ b/src/tools/account-register/utils/browser-manager.js @@ -0,0 +1,214 @@ +/** + * Browser Manager - AdsPower 指纹浏览器管理器 + * 统一管理浏览器的启动、连接、关闭等操作 + */ + +const puppeteer = require('puppeteer'); +const axios = require('axios'); +const logger = require('../../../shared/logger'); + +class BrowserManager { + constructor(options = {}) { + this.profileId = options.profileId || process.env.ADSPOWER_USER_ID; + this.apiBase = options.apiBase || process.env.ADSPOWER_API || 'http://local.adspower.net:50325'; + this.apiKey = options.apiKey || process.env.ADSPOWER_API_KEY; + this.siteName = options.siteName || 'Browser'; + + this.browser = null; + this.page = null; + } + + /** + * 启动 AdsPower 浏览器 + */ + async launch() { + logger.info(this.siteName, '启动 AdsPower 指纹浏览器...'); + + // 检查配置 + if (!this.profileId) { + logger.error(this.siteName, ''); + logger.error(this.siteName, '❌ 未配置 ADSPOWER_USER_ID'); + logger.error(this.siteName, ''); + logger.error(this.siteName, '请在 .env 文件中配置或传入 profileId:'); + logger.error(this.siteName, 'ADSPOWER_USER_ID=your_profile_id'); + logger.error(this.siteName, ''); + throw new Error('未配置 AdsPower 用户ID'); + } + + const startUrl = `${this.apiBase}/api/v1/browser/start?user_id=${encodeURIComponent(this.profileId)}`; + + // 配置请求头 + const headers = {}; + if (this.apiKey && this.apiKey.trim()) { + headers['Authorization'] = `Bearer ${this.apiKey}`; + logger.info(this.siteName, '✓ 使用 API Key 认证'); + } + + logger.info(this.siteName, ` → 启动 AdsPower 配置: ${this.profileId}`); + + try { + const response = await axios.get(startUrl, { headers }); + const data = response.data; + + if (data.code !== 0) { + logger.error(this.siteName, ''); + logger.error(this.siteName, `AdsPower API 返回错误: ${JSON.stringify(data)}`); + logger.error(this.siteName, ''); + logger.error(this.siteName, '解决方案:'); + logger.error(this.siteName, '1. 确保 AdsPower 应用已启动并登录'); + logger.error(this.siteName, `2. 检查配置文件 ID 是否正确: ${this.profileId}`); + logger.error(this.siteName, '3. 如果需要 API Key,请在 AdsPower 设置中生成'); + logger.error(this.siteName, '4. 尝试在 AdsPower 中手动打开一次浏览器配置'); + logger.error(this.siteName, ''); + throw new Error(`AdsPower 启动失败: ${data.msg || JSON.stringify(data)}`); + } + + // 获取 WebSocket 端点 + const wsEndpoint = data.data.ws && ( + data.data.ws.puppeteer || + data.data.ws.selenium || + data.data.ws.ws || + data.data.ws + ); + + if (!wsEndpoint) { + throw new Error('AdsPower 未返回 WebSocket 端点'); + } + + logger.info(this.siteName, ` → WebSocket: ${wsEndpoint}`); + + // 连接到 AdsPower 浏览器 + this.browser = await puppeteer.connect({ + browserWSEndpoint: wsEndpoint, + defaultViewport: null + }); + + // 获取已存在的页面 + const pages = await this.browser.pages(); + this.page = pages[0] || await this.browser.newPage(); + + // 关闭多余的标签页 + if (pages.length > 1) { + for (let i = 1; i < pages.length; i++) { + try { + await pages[i].close(); + } catch (e) { + // 忽略关闭失败 + } + } + } + + logger.success(this.siteName, '✓ AdsPower 浏览器连接成功'); + logger.info(this.siteName, '✓ 使用真实指纹,可同时绕过 Cloudflare 和 Stripe'); + + return { browser: this.browser, page: this.page }; + + } catch (error) { + logger.error(this.siteName, ''); + logger.error(this.siteName, `❌ AdsPower 连接失败: ${error.message}`); + logger.error(this.siteName, ''); + throw error; + } + } + + /** + * 获取当前页面 + */ + getPage() { + if (!this.page) { + throw new Error('浏览器页面未初始化,请先调用 launch()'); + } + return this.page; + } + + /** + * 获取浏览器实例 + */ + getBrowser() { + if (!this.browser) { + throw new Error('浏览器未初始化,请先调用 launch()'); + } + return this.browser; + } + + /** + * 清除浏览器数据 + */ + async clearData() { + if (!this.page) { + logger.warn(this.siteName, '页面未初始化,跳过清除数据'); + return; + } + + logger.info(this.siteName, ' → 清除所有浏览器数据(Cookies、Cache、Storage等)...'); + + try { + const client = await this.page.target().createCDPSession(); + + // 1. 清除浏览器 Cookies + await client.send('Network.clearBrowserCookies'); + logger.success(this.siteName, ' → ✓ 已清除所有 Cookies'); + + // 2. 清除浏览器缓存 + await client.send('Network.clearBrowserCache'); + logger.success(this.siteName, ' → ✓ 已清除浏览器缓存'); + + // 3. 清除所有存储数据 + await client.send('Storage.clearDataForOrigin', { + origin: '*', + storageTypes: 'all' + }); + logger.success(this.siteName, ' → ✓ 已清除所有存储数据'); + + // 4. 额外清理:访问目标网站并清除其存储 + const currentUrl = this.page.url(); + if (currentUrl && currentUrl.startsWith('http')) { + await this.page.evaluate(() => { + try { + localStorage.clear(); + sessionStorage.clear(); + } catch (e) {} + }); + } + + // 5. 关闭 CDP 会话 + await client.detach(); + + logger.success(this.siteName, ' → ✓ 浏览器数据清除完成(全新状态)'); + + } catch (e) { + logger.warn(this.siteName, ` → 清除浏览器数据失败: ${e.message}`); + } + } + + /** + * 关闭浏览器 + */ + async close() { + if (!this.browser) { + logger.warn(this.siteName, '浏览器未初始化,无需关闭'); + return; + } + + try { + logger.info(this.siteName, ' → 关闭浏览器...'); + await this.browser.disconnect(); + this.browser = null; + this.page = null; + logger.success(this.siteName, ' → ✓ 浏览器已关闭'); + } catch (error) { + logger.error(this.siteName, `关闭浏览器失败: ${error.message}`); + throw error; + } + } + + /** + * 清除数据并关闭浏览器 + */ + async clearAndClose() { + await this.clearData(); + await this.close(); + } +} + +module.exports = BrowserManager;