@device-portal/server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/main.js +113 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @device-portal/server
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@device-portal/server)
|
|
4
|
+
|
|
5
|
+
Signaling server for WebRTC used by `@device-portal/react`.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
You can run the server with the following command:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx @device-portal/server
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The server will run on `ws://localhost:8080` by default.
|
|
16
|
+
|
|
17
|
+
## Development
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npm ci
|
|
21
|
+
npm run dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Build the server with:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm run build
|
|
28
|
+
```
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_server_1 = require("@hono/node-server");
|
|
4
|
+
const serve_static_1 = require("@hono/node-server/serve-static");
|
|
5
|
+
const node_ws_1 = require("@hono/node-ws");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const hono_1 = require("hono");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const app = new hono_1.Hono();
|
|
10
|
+
const { injectWebSocket, upgradeWebSocket } = (0, node_ws_1.createNodeWebSocket)({ app });
|
|
11
|
+
const rooms = new Map();
|
|
12
|
+
const webSocketToPeerId = new Map();
|
|
13
|
+
const webSocketToRoom = new Map();
|
|
14
|
+
app.get('/v0/', upgradeWebSocket((context) => {
|
|
15
|
+
return {
|
|
16
|
+
onOpen(event, webSocket) {
|
|
17
|
+
const peerId = crypto.randomUUID();
|
|
18
|
+
webSocketToPeerId.set(webSocket.raw, peerId);
|
|
19
|
+
console.log(`WebSocket connection opened: ${peerId}`);
|
|
20
|
+
webSocket.send(JSON.stringify({ type: 'identity', data: { peerId } }));
|
|
21
|
+
},
|
|
22
|
+
onMessage(event, webSocket) {
|
|
23
|
+
const peerId = webSocketToPeerId.get(webSocket.raw);
|
|
24
|
+
const message = JSON.parse(event.data);
|
|
25
|
+
switch (message.type) {
|
|
26
|
+
case 'join-room': {
|
|
27
|
+
const room = message.room;
|
|
28
|
+
webSocketToRoom.set(webSocket.raw, room);
|
|
29
|
+
if (!rooms.has(room)) {
|
|
30
|
+
rooms.set(room, new Set());
|
|
31
|
+
}
|
|
32
|
+
const roomPeers = rooms.get(room);
|
|
33
|
+
roomPeers.add(webSocket.raw);
|
|
34
|
+
console.log(`Peer ${peerId} joined room: ${room}`);
|
|
35
|
+
// Notify other peers in the room that a new peer has joined
|
|
36
|
+
for (const client of roomPeers) {
|
|
37
|
+
if (client !== webSocket.raw &&
|
|
38
|
+
client.readyState === 1 /* WebSocket.OPEN */) {
|
|
39
|
+
client.send(JSON.stringify({ type: 'peer-joined', data: { peerId } }));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case 'offer':
|
|
45
|
+
case 'answer':
|
|
46
|
+
case 'ice-candidate': {
|
|
47
|
+
const room = webSocketToRoom.get(webSocket.raw);
|
|
48
|
+
console.log(`Forwarding ${message.type} from ${peerId} in room: ${room}`);
|
|
49
|
+
if (room && rooms.has(room)) {
|
|
50
|
+
for (const client of rooms.get(room)) {
|
|
51
|
+
if (client !== webSocket.raw &&
|
|
52
|
+
client.readyState === 1 /* WebSocket.OPEN */) {
|
|
53
|
+
// If message has a target 'to', only send to that client
|
|
54
|
+
const clientPeerId = webSocketToPeerId.get(client);
|
|
55
|
+
if (message.to && clientPeerId !== message.to) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
client.send(JSON.stringify({
|
|
59
|
+
type: message.type,
|
|
60
|
+
from: peerId,
|
|
61
|
+
data: message.data,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
onClose(event, webSocket) {
|
|
71
|
+
const peerId = webSocketToPeerId.get(webSocket.raw);
|
|
72
|
+
const room = webSocketToRoom.get(webSocket.raw);
|
|
73
|
+
if (room && rooms.has(room)) {
|
|
74
|
+
const roomPeers = rooms.get(room);
|
|
75
|
+
roomPeers.delete(webSocket.raw);
|
|
76
|
+
// Notify other peers in the room that a peer has left
|
|
77
|
+
for (const client of roomPeers) {
|
|
78
|
+
if (client.readyState === 1 /* WebSocket.OPEN */) {
|
|
79
|
+
client.send(JSON.stringify({ type: 'peer-left', data: { peerId } }));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (roomPeers.size === 0) {
|
|
83
|
+
rooms.delete(room);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
webSocketToPeerId.delete(webSocket.raw);
|
|
87
|
+
webSocketToRoom.delete(webSocket.raw);
|
|
88
|
+
console.log(`WebSocket connection closed: ${peerId}`);
|
|
89
|
+
},
|
|
90
|
+
onError(event, webSocket) {
|
|
91
|
+
const peerId = webSocketToPeerId.get(webSocket.raw);
|
|
92
|
+
console.error(`WebSocket error for ${peerId}:`, event);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}));
|
|
96
|
+
const storybookPath = (0, path_1.resolve)(__dirname, '../frontend/storybook-static');
|
|
97
|
+
if ((0, fs_1.existsSync)(storybookPath)) {
|
|
98
|
+
app.use('/*', (0, serve_static_1.serveStatic)({ root: '../frontend/storybook-static' }));
|
|
99
|
+
}
|
|
100
|
+
const portString = process.env.PORT;
|
|
101
|
+
let port = 8080;
|
|
102
|
+
if (portString) {
|
|
103
|
+
const parsedPort = parseInt(portString, 10);
|
|
104
|
+
if (!isNaN(parsedPort)) {
|
|
105
|
+
port = parsedPort;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
console.log(`Starting signaling server on 0.0.0.0:${port}…`);
|
|
109
|
+
const server = (0, node_server_1.serve)({
|
|
110
|
+
fetch: app.fetch,
|
|
111
|
+
port,
|
|
112
|
+
});
|
|
113
|
+
injectWebSocket(server);
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@device-portal/server",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"main": "dist/main.js",
|
|
5
|
+
"bin": {
|
|
6
|
+
"device-portal-server": "dist/main.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/main.js",
|
|
11
|
+
"dev": "node --watch -r ts-node/register src/main.ts",
|
|
12
|
+
"test": "tsc --build --dry",
|
|
13
|
+
"prepare": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"/dist/"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"hono": "^4.1.0",
|
|
20
|
+
"@hono/node-server": "^1.8.0",
|
|
21
|
+
"@hono/node-ws": "^1.3.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.0.0",
|
|
25
|
+
"ts-node": "^10.9.1",
|
|
26
|
+
"@types/node": "^20.0.0",
|
|
27
|
+
"@types/ws": "^8.5.3"
|
|
28
|
+
}
|
|
29
|
+
}
|