File size: 5,115 Bytes
33d9697
 
 
a016163
8843e8b
 
 
 
33d9697
 
 
 
0c4e80b
33d9697
ca5573b
6665a69
 
 
33d9697
 
 
 
 
 
 
 
 
 
 
 
8843e8b
3909b11
8843e8b
3909b11
 
 
8843e8b
 
 
 
 
 
 
 
58819e0
3909b11
 
 
 
 
 
 
8843e8b
3909b11
 
 
 
 
 
 
 
 
d143045
3909b11
 
cfb1f28
3909b11
58ca2bd
8843e8b
 
 
 
3909b11
8843e8b
58ca2bd
3909b11
25897e7
d143045
8843e8b
3909b11
 
 
 
8843e8b
3909b11
 
 
 
8843e8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3909b11
 
 
 
d143045
8843e8b
3909b11
74042c5
3909b11
74042c5
3909b11
 
ca5573b
8843e8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33d9697
0c4e80b
 
33d9697
 
 
 
 
 
 
 
 
 
 
 
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
const express = require('express');
const path = require('path');
const fs = require('fs');
const puppeteer = require('puppeteer');
const stream = require('puppeteer-stream');
const WebSocket = require('ws');
const http = require('http');

const app = express();
const PORT = 7860;

// 中间件
app.use(express.json({ limit: '40mb' }));
app.use(express.static('.'));

const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

// CORS支持
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
    if (req.method === 'OPTIONS') {
        res.sendStatus(200);
    } else {
        next();
    }
});

const sessions = {}; // store active sessions

// Launch Puppeteer page and create remote session
async function launchRemoteBrowser(url) {
  const browser = await puppeteer.launch({
    executablePath: '/opt/google/chrome/chrome',
    headless: 'new', // fully headless
    args: [
      '--no-sandbox',
      '--disable-setuid-sandbox',
      '--disable-web-security',
      '--disable-blink-features=AutomationControlled'
    ]
  });

  const page = (await browser.pages())[0] || await browser.newPage();

  await page.setViewport({ width: 1280, height: 900 });
  await page.setUserAgent(
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
  );

  // Anti-bot & anti-devtools bypass
  await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, 'webdriver', { get: () => false });
    Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3] });
    Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
    window.chrome = { runtime: {} };
    window.console.debug = () => {};
    window.console.log = () => {};
    ['oncontextmenu', 'onkeydown', 'onkeyup', 'onbeforeunload'].forEach(e => window[e] = null);
  });

  await page.setRequestInterception(true);
  page.on('request', req => req.continue()); // allow all requests

  await page.goto(url, { waitUntil: 'load', timeout: 60000 });

  // Start streaming
  const mediaStream = await stream.getStream(page, { audio: false, video: true });

  // Generate session ID
  const sessionId = Math.random().toString(36).substring(2, 12);
  sessions[sessionId] = { browser, page, mediaStream };

  return sessionId;
}

// Serve web page to connect to live session
app.get('/remote/:sessionId', (req, res) => {
  const { sessionId } = req.params;
  if (!sessions[sessionId]) return res.status(404).send('Session not found');

  // Simple client page that connects to WebSocket
  res.send(`
    <html>
      <body>
        <h2>Remote Live Browser</h2>
        <video id="video" autoplay playsinline style="width:1280px;height:720px;border:1px solid black;"></video>
        <script>
          const ws = new WebSocket('ws://' + location.host + '/ws/${sessionId}');
          const video = document.getElementById('video');

          ws.onmessage = async (msg) => {
            const data = msg.data;
            if (data instanceof Blob || typeof data === 'string') {
              // Normally you would use WebRTC to stream video
              console.log('Message:', data);
            }
          };

          // Capture clicks & send to server
          video.addEventListener('click', e => {
            const rect = video.getBoundingClientRect();
            const x = (e.clientX - rect.left) / rect.width;
            const y = (e.clientY - rect.top) / rect.height;
            ws.send(JSON.stringify({ type: 'click', x, y }));
          });
        </script>
      </body>
    </html>
  `);
});

// Start new session via API
app.get('/api/live', async (req, res) => {
  const { url } = req.query;
  if (!url) return res.status(400).send('URL is required');

  const sessionId = await launchRemoteBrowser(url);
  res.send(`✅ Live browser launched. Open <a href="/remote/${sessionId}">this link</a> to interact remotely.`);
});

// WebSocket for remote control
wss.on('connection', (ws, req) => {
  const sessionId = req.url.split('/').pop();
  const session = sessions[sessionId];
  if (!session) return ws.close();

  ws.on('message', async (msg) => {
    const data = JSON.parse(msg.toString());
    const { page } = session;

    if (data.type === 'click') {
      const { x, y } = data;
      const box = await page.viewport();
      await page.mouse.click(x * box.width, y * box.height);
    }
  });
});

// 健康检查端点
app.get('/', (req, res) => {
    res.json({ status: 'hii', timestamp: new Date().toISOString() });
});

// 启动服务器
app.listen(PORT, () => {
    console.log(`Puppeteer服务器运行在 http://localhost:${PORT}`);
    console.log('API端点:');
    console.log('  POST /api/generate-pdf - 生成PDF');
    console.log('  POST /api/generate-images - 生成图片');
    console.log('  GET  /api/health - 健康检查');
});

module.exports = app;