const express = require("express"); const http = require("http"); const crypto = require("crypto"); const { chromium } = require("playwright-core"); const WebSocket = require("ws"); const app = express(); const server = http.createServer(app); const wss = new WebSocket.Server({ server }); const sessions = new Map(); const PORT = process.env.PORT || 7860; const CHROME = process.env.CHROMIUM_PATH || "/usr/bin/chromium"; app.use(express.static("public")); function token() { return crypto.randomBytes(24).toString("hex"); } async function createSession() { const browser = await chromium.launch({ executablePath: CHROME, headless: true, args: [ "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-gpu", "--disable-extensions", "--disable-background-networking", "--disable-sync" ] }); const context = await browser.newContext({ viewport: { width: 1280, height: 720 }, deviceScaleFactor: 1, userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/124.0.0.0 Safari/537.36" }); const page = await context.newPage(); return { browser, context, page, clients: new Set() }; } async function destroySession(id) { const s = sessions.get(id); if (!s) return; sessions.delete(id); try { await s.browser.close(); } catch {} } wss.on("connection", async ws => { const id = token(); let session; try { session = await createSession(); sessions.set(id, session); session.clients.add(ws); ws.send(JSON.stringify({ type: "ready", token: id })); } catch { ws.close(); return; } const sendFrame = async () => { if (ws.readyState !== WebSocket.OPEN) return; try { const image = await session.page.screenshot({ type: "jpeg", quality: 65 }); ws.send(JSON.stringify({ type: "frame", data: image.toString("base64") })); } catch {} }; const timer = setInterval(sendFrame, 125); ws.on("message", async raw => { let msg; try { msg = JSON.parse(raw.toString()); } catch { return; } try { if (msg.type === "navigate") { let url = String(msg.url || "").trim(); if (!/^https?:\/\//i.test(url)) { url = "https://" + url; } await session.page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 }); return; } if (msg.type === "mouse") { await session.page.mouse.move( Number(msg.x), Number(msg.y) ); if (msg.action === "down") { await session.page.mouse.down(); } if (msg.action === "up") { await session.page.mouse.up(); } return; } if (msg.type === "wheel") { await session.page.mouse.wheel( Number(msg.dx || 0), Number(msg.dy || 0) ); return; } if (msg.type === "touch") { const client = await session.context.newCDPSession( session.page ); await client.send( "Input.dispatchTouchEvent", { type: msg.action, touchPoints: msg.action === "touchEnd" ? [] : [{ x: Number(msg.x), y: Number(msg.y) }] } ); await client.detach(); return; } if (msg.type === "key") { await session.page.keyboard.press( String(msg.key) ); return; } if (msg.type === "text") { await session.page.keyboard.insertText( String(msg.text || "") ); return; } if (msg.type === "reload") { await session.page.reload({ waitUntil: "domcontentloaded" }); } } catch {} }); ws.on("close", async () => { clearInterval(timer); await destroySession(id); }); }); server.listen(PORT, "0.0.0.0", () => { console.log(`Orbit Chromium Engine :${PORT}`); });