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(`

Remote Live Browser

`); }); // 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 this link 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;