Coconut / server.js
Sachin5112's picture
Create server.js
8ea9686 verified
Raw
History Blame Contribute Delete
4.27 kB
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}`);
});