@laplace.live/event-bridge-server 0.2.0 → 0.2.2
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/CHANGELOG.md +13 -0
- package/README.md +19 -2
- package/index.ts +191 -0
- package/package.json +1 -1
- package/.env +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @laplace.live/event-bridge-server
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2ef27cf: release artifacts test
|
|
8
|
+
|
|
9
|
+
## 0.2.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 818636b: enhance configuration options for authentication and debug mode
|
|
14
|
+
- 6fbc787: add network interface configuration options
|
|
15
|
+
|
|
3
16
|
## 0.2.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -54,6 +54,23 @@ bun run start --auth "your-secure-token"
|
|
|
54
54
|
|
|
55
55
|
If the authentication token is set, all connections including the clients must provide it to connect.
|
|
56
56
|
|
|
57
|
+
### Network Interface
|
|
58
|
+
|
|
59
|
+
Control which network interface the server listens on:
|
|
60
|
+
|
|
61
|
+
1. Environment variable: `HOST="127.0.0.1"`
|
|
62
|
+
2. Command line: `--host 127.0.0.1`
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Listen only on localhost
|
|
66
|
+
export HOST="localhost"
|
|
67
|
+
|
|
68
|
+
# Listen on all interfaces
|
|
69
|
+
bun run start --host 0.0.0.0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
By default, the server listens on `localhost`.
|
|
73
|
+
|
|
57
74
|
### Debug Mode
|
|
58
75
|
|
|
59
76
|
Enable detailed debug logging using:
|
|
@@ -78,13 +95,13 @@ bun run start --debug
|
|
|
78
95
|
bun run start
|
|
79
96
|
|
|
80
97
|
# Start with CLI options
|
|
81
|
-
bun run start --debug --auth "your-secure-token"
|
|
98
|
+
bun run start --debug --auth "your-secure-token" --host 0.0.0.0
|
|
82
99
|
|
|
83
100
|
# Or with hot reloading during development
|
|
84
101
|
bun run dev
|
|
85
102
|
```
|
|
86
103
|
|
|
87
|
-
The WebSocket bridge server runs on `http://localhost:9696
|
|
104
|
+
The WebSocket bridge server runs on `http://localhost:9696` by default.
|
|
88
105
|
|
|
89
106
|
### Connection Types
|
|
90
107
|
|
package/index.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { parseArgs } from 'util'
|
|
2
|
+
import type { LaplaceEvent } from '@laplace.live/event-types'
|
|
3
|
+
|
|
4
|
+
// Parse command line arguments properly
|
|
5
|
+
const { values } = parseArgs({
|
|
6
|
+
args: Bun.argv,
|
|
7
|
+
options: {
|
|
8
|
+
debug: {
|
|
9
|
+
type: 'boolean',
|
|
10
|
+
},
|
|
11
|
+
auth: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
},
|
|
14
|
+
host: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
strict: false,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Get authentication token from environment variable or CLI
|
|
22
|
+
const AUTH_TOKEN = process.env['LEB_AUTH'] || process.env['LAPLACE_EVENT_BRIDGE_AUTH'] || values.auth || ''
|
|
23
|
+
|
|
24
|
+
// Debug mode configuration
|
|
25
|
+
const DEBUG_MODE = process.env['DEBUG'] === '1' || process.env['DEBUG']?.toLowerCase() === 'true' || !!values.debug
|
|
26
|
+
|
|
27
|
+
// Network interface configuration
|
|
28
|
+
const HOST = process.env['HOST'] || (values.host as string) || 'localhost'
|
|
29
|
+
|
|
30
|
+
interface Client {
|
|
31
|
+
id: string
|
|
32
|
+
isServer: boolean // Flag to identify if this client is the laplace-chat server
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const clients = new Map<any, Client>()
|
|
36
|
+
let nextClientId = 1
|
|
37
|
+
|
|
38
|
+
const server = Bun.serve<Client, {}>({
|
|
39
|
+
port: 9696,
|
|
40
|
+
hostname: HOST,
|
|
41
|
+
fetch(req, server) {
|
|
42
|
+
// Check if this is the laplace-chat server connecting
|
|
43
|
+
// Format: "laplace-event-bridge-role-server, password123"
|
|
44
|
+
const protocol = req.headers.get('sec-websocket-protocol')
|
|
45
|
+
const protocolParts = protocol ? protocol.split(',') : []
|
|
46
|
+
const role = protocolParts[0]?.trim()
|
|
47
|
+
const password = protocolParts[1]?.trim() // Password for verification
|
|
48
|
+
|
|
49
|
+
const isServer = role === 'laplace-event-bridge-role-server'
|
|
50
|
+
|
|
51
|
+
if (AUTH_TOKEN) {
|
|
52
|
+
if (password !== AUTH_TOKEN) {
|
|
53
|
+
console.log(`Authentication failed: Invalid token for ${isServer ? 'server' : 'client'} connection`)
|
|
54
|
+
return new Response('Unauthorized', { status: 401 })
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Upgrade the request to a WebSocket connection
|
|
59
|
+
const success = server.upgrade(req, {
|
|
60
|
+
data: {
|
|
61
|
+
id: `${isServer ? 'server' : 'client'}-${nextClientId++}`,
|
|
62
|
+
isServer,
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (!success) {
|
|
67
|
+
return new Response('WebSocket upgrade failed. This is a WebSocket server.', { status: 400 })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return undefined
|
|
71
|
+
},
|
|
72
|
+
websocket: {
|
|
73
|
+
open(ws) {
|
|
74
|
+
const clientId = ws.data.id
|
|
75
|
+
const isServer = ws.data.isServer
|
|
76
|
+
clients.set(ws, ws.data)
|
|
77
|
+
|
|
78
|
+
console.log(`Client connected: ${clientId}${isServer ? ' (laplace-chat server)' : ''}`)
|
|
79
|
+
|
|
80
|
+
// Welcome message
|
|
81
|
+
ws.send(
|
|
82
|
+
JSON.stringify({
|
|
83
|
+
type: 'established',
|
|
84
|
+
clientId,
|
|
85
|
+
isServer,
|
|
86
|
+
message: 'Connected to LAPLACE Event bridge',
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
},
|
|
90
|
+
message(ws, message) {
|
|
91
|
+
const clientId = ws.data.id
|
|
92
|
+
const isServer = ws.data.isServer
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const messageStr = message.toString()
|
|
96
|
+
let parsedMessage: LaplaceEvent
|
|
97
|
+
let broadcastMessage
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
// Try to parse as JSON
|
|
101
|
+
parsedMessage = JSON.parse(messageStr)
|
|
102
|
+
|
|
103
|
+
if (DEBUG_MODE) {
|
|
104
|
+
console.log(`Received ${parsedMessage.type} from ${clientId}:`, parsedMessage)
|
|
105
|
+
} else {
|
|
106
|
+
console.log(`Received ${parsedMessage.type} from ${clientId}`)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Prepare broadcast message in the same format
|
|
110
|
+
broadcastMessage = JSON.stringify({
|
|
111
|
+
...parsedMessage,
|
|
112
|
+
source: clientId,
|
|
113
|
+
})
|
|
114
|
+
} catch (e) {
|
|
115
|
+
// Handle as plain text if not JSON
|
|
116
|
+
console.log(`Received message from ${clientId}: ${messageStr}`)
|
|
117
|
+
|
|
118
|
+
// Prepare broadcast message for plain text
|
|
119
|
+
broadcastMessage = JSON.stringify({
|
|
120
|
+
type: 'unknown-message',
|
|
121
|
+
text: messageStr,
|
|
122
|
+
source: clientId,
|
|
123
|
+
timestamp: Date.now(),
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Broadcast to all clients EXCEPT the server
|
|
128
|
+
// If the message is from the server, broadcast to all clients
|
|
129
|
+
// If from a client, don't broadcast (or optionally can be enabled)
|
|
130
|
+
if (isServer) {
|
|
131
|
+
// console.log(`Broadcasting message from laplace-chat to all clients`)
|
|
132
|
+
|
|
133
|
+
// Broadcast to all clients except the server
|
|
134
|
+
for (const [client, data] of clients.entries()) {
|
|
135
|
+
if (client !== ws) {
|
|
136
|
+
client.send(broadcastMessage)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Send confirmation back to the server
|
|
141
|
+
ws.send(
|
|
142
|
+
JSON.stringify({
|
|
143
|
+
type: 'broadcast-success',
|
|
144
|
+
clientCount: clients.size - 1, // Excluding the server
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
})
|
|
147
|
+
)
|
|
148
|
+
} else {
|
|
149
|
+
// Optional: Echo back to the client that their message was received
|
|
150
|
+
ws.send(
|
|
151
|
+
JSON.stringify({
|
|
152
|
+
type: 'client-message-received',
|
|
153
|
+
message: 'Message received (client-to-server messages are not relayed)',
|
|
154
|
+
timestamp: Date.now(),
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(`Error processing message from ${clientId}:`, error)
|
|
160
|
+
ws.send(
|
|
161
|
+
JSON.stringify({
|
|
162
|
+
type: 'error',
|
|
163
|
+
message: 'Failed to process your message',
|
|
164
|
+
timestamp: Date.now(),
|
|
165
|
+
})
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
close(ws) {
|
|
170
|
+
const clientId = ws.data.id
|
|
171
|
+
const isServer = ws.data.isServer
|
|
172
|
+
clients.delete(ws)
|
|
173
|
+
console.log(`Client disconnected: ${clientId}${isServer ? ' (laplace-chat server)' : ''}`)
|
|
174
|
+
},
|
|
175
|
+
drain(ws) {
|
|
176
|
+
console.log(`WebSocket backpressure: ${ws.getBufferedAmount()}`)
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// Create a banner display at startup
|
|
182
|
+
function displayBanner() {
|
|
183
|
+
console.log('🌸 LAPLACE Event Bridge Server')
|
|
184
|
+
console.log(`🚀 Server running at http://${server.hostname}:${server.port}`)
|
|
185
|
+
console.log(`🔐 Authentication: ${AUTH_TOKEN ? '✅ Enabled' : '❌ Disabled'}`)
|
|
186
|
+
console.log(`🐛 Debug Mode: ${DEBUG_MODE ? '✅ Enabled' : '❌ Disabled'}`)
|
|
187
|
+
console.log(`⏱️ Started at: ${new Date().toLocaleString()}`)
|
|
188
|
+
console.log(`\nWaiting for connections...\n`)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
displayBanner()
|
package/package.json
CHANGED
package/.env
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# LAPLACE_EVENT_BRIDGE_AUTH=laplace
|