Spaces:
Sleeping
Sleeping
| 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; |