inital
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json .
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY server.js .
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "pulsar-signalling-server",
|
||||
"version": "1.0.0",
|
||||
"description": "WebRTC signalling server for Pulsar remote desktop",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": "^8.16.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
const { WebSocketServer } = require("ws");
|
||||
|
||||
const PORT = 3000;
|
||||
const wss = new WebSocketServer({ port: PORT });
|
||||
|
||||
// rooms: { roomId: { host: WebSocket, viewer: WebSocket } }
|
||||
const rooms = {};
|
||||
|
||||
console.log(`Pulsar signalling server listening on ws://localhost:${PORT}`);
|
||||
|
||||
wss.on("connection", (ws) => {
|
||||
ws.roomId = null;
|
||||
ws.role = null; // "host" or "viewer"
|
||||
|
||||
ws.on("message", (raw) => {
|
||||
let msg;
|
||||
try {
|
||||
msg = JSON.parse(raw);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const { type, roomId, data } = msg;
|
||||
|
||||
switch (type) {
|
||||
case "create": {
|
||||
if (!roomId) return;
|
||||
if (rooms[roomId]) {
|
||||
ws.send(JSON.stringify({ type: "error", message: "Room already exists" }));
|
||||
return;
|
||||
}
|
||||
rooms[roomId] = { host: ws, viewer: null };
|
||||
ws.roomId = roomId;
|
||||
ws.role = "host";
|
||||
ws.send(JSON.stringify({ type: "created", roomId }));
|
||||
console.log(`Room created: ${roomId}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case "join": {
|
||||
if (!roomId || !rooms[roomId]) {
|
||||
ws.send(JSON.stringify({ type: "error", message: "Room not found" }));
|
||||
return;
|
||||
}
|
||||
|
||||
const room = rooms[roomId];
|
||||
|
||||
if (room.viewer) {
|
||||
ws.send(JSON.stringify({ type: "error", message: "Room full" }));
|
||||
return;
|
||||
}
|
||||
|
||||
room.viewer = ws;
|
||||
ws.roomId = roomId;
|
||||
ws.role = "viewer";
|
||||
|
||||
ws.send(JSON.stringify({ type: "joined", roomId }));
|
||||
room.host.send(JSON.stringify({ type: "peer_joined" }));
|
||||
console.log(`Viewer joined room: ${roomId}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case "signal": {
|
||||
if (!ws.roomId || !rooms[ws.roomId]) return;
|
||||
|
||||
const room = rooms[ws.roomId];
|
||||
const target = ws.role === "host" ? room.viewer : room.host;
|
||||
|
||||
if (target && target.readyState === 1) {
|
||||
target.send(JSON.stringify({ type: "signal", data }));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
ws.on("close", () => {
|
||||
if (!ws.roomId || !rooms[ws.roomId]) return;
|
||||
|
||||
const room = rooms[ws.roomId];
|
||||
|
||||
if (ws.role === "host") {
|
||||
// notify viewer if connected
|
||||
if (room.viewer && room.viewer.readyState === 1) {
|
||||
room.viewer.send(JSON.stringify({ type: "host_disconnected" }));
|
||||
}
|
||||
delete rooms[ws.roomId];
|
||||
console.log(`Room closed: ${ws.roomId}`);
|
||||
} else if (ws.role === "viewer") {
|
||||
room.viewer = null;
|
||||
if (room.host && room.host.readyState === 1) {
|
||||
room.host.send(JSON.stringify({ type: "peer_disconnected" }));
|
||||
}
|
||||
console.log(`Viewer left room: ${ws.roomId}`);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on("error", (err) => {
|
||||
console.error("WebSocket error:", err.message);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user