File size: 9,465 Bytes
82cb77d
 
 
 
 
 
 
 
 
 
 
 
2275d80
df986b8
82cb77d
 
 
 
 
 
e258fb0
 
 
d9a3952
 
 
82cb77d
 
 
 
 
 
 
 
2496079
82cb77d
 
 
 
df986b8
d453417
d9a3952
 
 
d453417
0af80ea
72dadb8
d453417
 
 
 
 
 
82cb77d
d9a3952
 
 
 
 
 
 
 
 
82cb77d
2496079
 
82cb77d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df986b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82cb77d
 
df986b8
82cb77d
 
 
 
 
df986b8
82cb77d
 
df986b8
82cb77d
 
df986b8
82cb77d
df986b8
82cb77d
 
 
 
df986b8
 
82cb77d
 
df986b8
82cb77d
 
df986b8
 
 
 
82cb77d
 
df986b8
 
82cb77d
 
df986b8
 
82cb77d
 
df986b8
82cb77d
 
df986b8
82cb77d
df986b8
82cb77d
 
 
 
df986b8
82cb77d
df986b8
82cb77d
df986b8
82cb77d
 
 
 
 
df986b8
82cb77d
df986b8
 
 
82cb77d
 
 
 
 
 
 
df986b8
82cb77d
 
 
 
 
 
 
 
 
 
df986b8
82cb77d
2496079
df986b8
 
 
 
 
 
0f18588
 
 
df986b8
 
 
 
2d206c5
 
df986b8
 
 
 
 
24965e4
df986b8
 
c76a8db
df986b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82cb77d
2d206c5
df986b8
 
 
 
 
 
82cb77d
 
2275d80
df986b8
 
 
2d206c5
df986b8
 
82cb77d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df986b8
82cb77d
 
 
 
df986b8
82cb77d
 
 
 
 
 
 
 
 
 
df986b8
82cb77d
 
 
 
 
df986b8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
import { serveStatic } from '@hono/node-server/serve-static'
import { chromium, type Browser, type BrowserContext, type Route, type Page } from 'playwright'
import process from 'process'
import fs from 'fs'

const app = new Hono()

// 浏览器实例
let browser: Browser | null = null
let gensparkContext: BrowserContext | null = null

const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3';
// 初始化浏览器
async function initBrowser() {
  if (!browser) {
    browser = await chromium.launch({
      headless: true,
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-dev-shm-usage',
        '--disable-blink-features=AutomationControlled', // 禁用自动化特征
        '--disable-infobars',
        '--window-size=1920,1080'
      ],
      executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH, // 使用系统 Chromium
    })
  }
  return browser
}

// 初始化genspark页面
async function initGensparkContext() {
  const browser = await initBrowser()

  if (!gensparkContext) {
    gensparkContext = await browser.newContext({
      userAgent: userAgent,
      viewport: { width: 1920, height: 1080 },
      extraHTTPHeaders: {
        'Accept-Language': 'en-US,en;q=0.9'
      },
      deviceScaleFactor: 1,
      locale: 'en-US',
      timezoneId: 'America/New_York',
      geolocation: { longitude: -73.935242, latitude: 40.730610 }, // 纽约坐标,可根据需要调整
      permissions: ['geolocation'],
      javaScriptEnabled: true,
      bypassCSP: true, // 绕过内容安全策略
      colorScheme: 'light',
      acceptDownloads: true,
    })

    // 注入反检测脚本
    await gensparkContext.addInitScript(() => {
      Object.defineProperty(navigator, 'webdriver', { get: () => false })
      Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] })
      //@ts-ignore
      window.navigator.chrome = { runtime: {} }
      Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] })
    })
  }

  return gensparkContext
}

// 验证响应头值是否有效
function isValidHeaderValue(value: string): boolean {
  // 检查值是否为空或包含无效字符
  if (!value || typeof value !== 'string') return false;
  // 检查是否包含换行符或回车符
  if (/[\r\n]/.test(value)) return false;
  return true;
}

// 处理请求转发
async function handleRequest(url: string, method: string, headers: any, body?: any) {
  const browser = await initBrowser()
  const page = await browser.newPage()

  try {
    // 只移除确实需要移除的请求头
    delete headers['host']
    delete headers['connection']
    delete headers['content-length']
    delete headers['accept-encoding']
    // 移除cf相关的头
    delete headers['cdn-loop']
    delete headers['cf-connecting-ip']
    delete headers['cf-connecting-o2o']
    delete headers['cf-ew-via']
    delete headers['cf-ray']
    delete headers['cf-visitor']
    delete headers['cf-worker']

    //移除其他无效的请求头
    delete headers['x-direct-url']
    delete headers['x-forwarded-for']
    delete headers['x-forwarded-port']
    delete headers['x-forwarded-proto']

    headers['user-agent'] = userAgent

    console.log('处理请求:', method, url, headers, body)
    // 设置请求拦截器
    await page.route('**/*', async (route: Route) => {
      const request = route.request()
      if (request.url() === url) {
        await route.continue({
          method: method,
          headers: {
            ...request.headers(),
            ...headers
          },
          postData: body
        })
      } else {
        // 允许其他资源加载
        await route.continue()
      }
    })

    // 配置页面请求选项
    const response = await page.goto(url, {
      waitUntil: 'domcontentloaded', // 改为更快的加载策略
      timeout: 600000
    })

    if (!response) {
      throw new Error('未收到响应')
    }

    // 等待页面加载完成
    await page.waitForLoadState('networkidle', { timeout: 600000 }).catch(() => {
      console.log('等待页面加载超时,继续处理')
    })

    // 获取响应数据
    const status = response.status()
    const responseHeaders = response.headers()

    // 确保移除可能导致解码问题的响应头
    delete responseHeaders['content-encoding']
    delete responseHeaders['content-length']

    // 过滤无效的响应头
    const validHeaders: Record<string, string> = {}
    for (const [key, value] of Object.entries(responseHeaders)) {
      if (isValidHeaderValue(value as string)) {
        validHeaders[key] = value as string
      } else {
        console.warn(`跳过无效的响应头: ${key}: ${value}`)
      }
    }

    // 直接获取响应体的二进制数据
    const responseBody = await response.body()

    console.log('请求处理完成:', status, responseBody.toString())

    await page.close()

    return {
      status,
      headers: validHeaders,
      body: responseBody
    }
  } catch (error: any) {
    await page.close()
    console.error('请求处理错误:', error)
    throw new Error(`请求失败: ${error.message}`)
  }
}


// 添加静态文件服务
app.use('/public/*', serveStatic({ root: './' }))

// 修改点 1: 处理根路由直接返回 index.html 内容,而不是重定向
app.get('/', async (c) => {
  try {
    const htmlContent = fs.readFileSync('./index.html', 'utf-8')
    return c.html(htmlContent)
  } catch (error) {
    console.error('读取index.html失败:', error)
    return c.text('无法读取主页', 500)
  }
})

// 修改点 2: 添加 /genspark 路由来获取reCAPTCHA令牌
app.get('/genspark', async (c) => {

  const headers = Object.fromEntries(c.req.raw.headers)
  // Get the cookie string from headers
  const cookieString = headers.cookie || '';
  // Parse cookies into an array of objects with name and value properties
  const cookies = cookieString.split(';').map(cookie => {
    const [name, value] = cookie.trim().split('=');
    if (name.startsWith("_ga")) {
      return { name, value, domain: 'genspark.ai', path: '/' };
    }
    return { name, value, domain: 'www.genspark.ai', path: '/' };
  }).filter(cookie => cookie.name && cookie.value);

  gensparkContext = await initGensparkContext()

  console.log('Cookies:', cookies)
  if (cookies && cookies.length > 0) {
    await gensparkContext.clearCookies()
    await gensparkContext.addCookies(cookies);
  }
  const gensparkPage = await gensparkContext.newPage()
  try {
    //刷新页面以确保获取新令牌
    await gensparkPage.goto('https://www.genspark.ai/agents?type=moa_chat', {
      waitUntil: 'networkidle',
      timeout: 3600000
    })
    // 执行脚本获取令牌
    const token = await gensparkPage.evaluate(() => {
      return new Promise((resolve, reject) => {
        // @ts-ignore
        window.grecaptcha.ready(function () {
          // @ts-ignore
          grecaptcha.execute(
            "6Leq7KYqAAAAAGdd1NaUBJF9dHTPAKP7DcnaRc66",
            { action: 'copilot' },
          ).then(function (token: string) {
            resolve(token)
          }).catch(function (error: Error) {
            reject(error)
          });
        });

        // 设置超时
        setTimeout(() => reject(new Error("获取令牌超时")), 10000);
      });
    }).catch(error => {
      return c.json({ code: 500, message: '获取令牌失败' })
    });
    console.log('token:', token)
    return c.json({ code: 200, message: '获取令牌成功', token: token })
  }
  catch (error) {
    console.error('获取令牌失败:', error)
    if (gensparkContext) {
      await gensparkContext.close().catch(() => { });
      gensparkContext = null;
    }
  }
  finally {
    await gensparkPage.close().catch(() => { });
  }
  console.log('token:', "获取令牌失败")
  return c.json({ code: 500, message: '获取令牌失败' })
})

// 处理所有 HTTP 方法
app.all('*', async (c) => {
  const url = c.req.query('url')
  if (!url) {
    return c.text('Missing url parameter', 400)
  }

  try {
    const method = c.req.method
    const headers = Object.fromEntries(c.req.raw.headers)
    const body = method !== 'GET' ? await c.req.text() : undefined

    const result = await handleRequest(url, method, headers, body)

    // 创建标准响应
    const response = new Response(result.body, {
      status: result.status,
      headers: new Headers({
        ...result.headers,
        'content-encoding': 'identity'  // 显式设置不使用压缩
      })
    })

    return response
  } catch (error) {
    console.error('Error:', error)
    return new Response('Internal Server Error', {
      status: 500,
      headers: new Headers({
        'content-type': 'text/plain'
      })
    })
  }
})

// 清理函数
async function cleanup() {
  if (gensparkContext) {
    await gensparkContext.close().catch(() => { });
    gensparkContext = null;
  }

  if (browser) {
    await browser.close().catch(() => { });
    browser = null;
  }
  process.exit(0)
}

// 监听进程退出信号
process.on('SIGINT', cleanup)
process.on('SIGTERM', cleanup)

const port = Number(process.env.PORT || '7860');
console.log(`Server is running on port  http://localhost:${port}`)

// 启动服务器
serve({
  fetch: app.fetch,
  port: port
})