thibbatta / server.js
vikarshana's picture
Update server.js
6665a69 verified
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;