@owlmeans/server-socket 0.1.2 → 0.1.4
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 +34 -528
- package/build/helper.d.ts +3 -3
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js.map +1 -1
- package/build/service.js +4 -4
- package/build/service.js.map +1 -1
- package/build/utils/connection.d.ts +1 -1
- package/build/utils/connection.d.ts.map +1 -1
- package/build/utils/connection.js +27 -0
- package/build/utils/connection.js.map +1 -1
- package/build/utils/server.d.ts +3 -3
- package/build/utils/server.d.ts.map +1 -1
- package/build/utils/server.js.map +1 -1
- package/package.json +12 -11
- package/src/helper.ts +3 -3
- package/src/service.ts +6 -6
- package/src/utils/connection.ts +31 -1
- package/src/utils/server.ts +3 -3
- package/tsconfig.json +6 -11
package/README.md
CHANGED
|
@@ -1,560 +1,66 @@
|
|
|
1
1
|
# @owlmeans/server-socket
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Server-side WebSocket connection handler and service for OwlMeans backends.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- `handleConnection()` wraps WebSocket business logic — analogous to `handleBody` but for WS connections
|
|
8
|
+
- `appendSocketService()` + `createSocketMiddleware()` wire the WebSocket service into a server context
|
|
9
|
+
- Used in viable for real-time thinking journal updates and file watching
|
|
10
|
+
- The `Connection` interface (from `@owlmeans/socket`) is the runtime object passed to your handler
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
- **Supports real-time communication** between server and clients
|
|
11
|
-
- **Integrates with module system** for WebSocket route handling
|
|
12
|
-
- **Implements authentication and authorization** for WebSocket connections
|
|
13
|
-
- **Offers connection management** with lifecycle hooks and proper cleanup
|
|
14
|
-
- **Enables bidirectional messaging** with typed message handling
|
|
12
|
+
## Installation
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
### Socket Service
|
|
19
|
-
A service that manages WebSocket server functionality and integrates with the Fastify web server to provide WebSocket endpoints.
|
|
20
|
-
|
|
21
|
-
### Connection Handling
|
|
22
|
-
Provides abstraction for WebSocket connections with proper lifecycle management, authentication, and error handling.
|
|
23
|
-
|
|
24
|
-
### Module Integration
|
|
25
|
-
WebSocket endpoints are defined using the OwlMeans module system, allowing for consistent routing, validation, and middleware application.
|
|
26
|
-
|
|
27
|
-
### Authentication & Authorization
|
|
28
|
-
WebSocket connections can be protected using the same guard and gate system used for HTTP endpoints.
|
|
29
|
-
|
|
30
|
-
## API Reference
|
|
31
|
-
|
|
32
|
-
### Services
|
|
33
|
-
|
|
34
|
-
#### `createSocketService(alias?): SocketService`
|
|
35
|
-
|
|
36
|
-
Creates a WebSocket service that can be registered with a server context.
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
import { createSocketService } from '@owlmeans/server-socket'
|
|
40
|
-
|
|
41
|
-
const socketService = createSocketService('websocket')
|
|
14
|
+
```bash
|
|
15
|
+
bun add @owlmeans/server-socket
|
|
42
16
|
```
|
|
43
17
|
|
|
44
|
-
|
|
45
|
-
- `alias`: string (optional) - Service alias, defaults to `DEFAULT_ALIAS` ('socket-server')
|
|
18
|
+
## Usage
|
|
46
19
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
#### `SocketService`
|
|
50
|
-
|
|
51
|
-
Interface for the WebSocket service.
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
interface SocketService extends InitializedService {
|
|
55
|
-
update: (api: ApiServer) => Promise<void>
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
**Methods:**
|
|
60
|
-
- `update(api)`: Configures the WebSocket server with the provided API server instance
|
|
61
|
-
|
|
62
|
-
### Helper Functions
|
|
63
|
-
|
|
64
|
-
#### `handleConnection<T>(handler): RefedModuleHandler`
|
|
65
|
-
|
|
66
|
-
Creates a WebSocket connection handler that integrates with the module system.
|
|
20
|
+
Handle a WebSocket connection on a module route:
|
|
67
21
|
|
|
68
22
|
```typescript
|
|
69
23
|
import { handleConnection } from '@owlmeans/server-socket'
|
|
70
24
|
import type { Connection } from '@owlmeans/socket'
|
|
71
25
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
console.log('New WebSocket connection')
|
|
76
|
-
|
|
77
|
-
conn.onMessage(async (message) => {
|
|
78
|
-
console.log('Received:', message)
|
|
79
|
-
await conn.send({ type: 'echo', data: message })
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
conn.onClose(() => {
|
|
83
|
-
console.log('Connection closed')
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
)
|
|
87
|
-
```
|
|
26
|
+
export const watch = handleConnection<Connection>(async (conn, context, req) => {
|
|
27
|
+
const ctx = context as Context
|
|
28
|
+
const projectId = req.params.id as string
|
|
88
29
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- `req`: Abstract request
|
|
94
|
-
- `res`: Abstract response
|
|
30
|
+
// Observe events sent by the client
|
|
31
|
+
const unsubscribe = conn.observe<FileEvent>('file-change', async (event) => {
|
|
32
|
+
await ctx.fileStore().save({ projectId, ...event.payload })
|
|
33
|
+
})
|
|
95
34
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
#### `Config`
|
|
101
|
-
|
|
102
|
-
Configuration interface extending ServerConfig.
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
interface Config extends ServerConfig {}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
#### `Context<C>`
|
|
109
|
-
|
|
110
|
-
Context interface that extends ServerContext with API server capabilities.
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
interface Context<C extends Config = Config> extends ServerContext<C>, ApiServerAppend {}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Constants
|
|
117
|
-
|
|
118
|
-
#### `DEFAULT_ALIAS`
|
|
119
|
-
|
|
120
|
-
Default alias for the socket service.
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
const DEFAULT_ALIAS = 'socket-server'
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Usage Examples
|
|
127
|
-
|
|
128
|
-
### Basic WebSocket Server Setup
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
import { makeServerContext, makeServerConfig } from '@owlmeans/server-context'
|
|
132
|
-
import { createSocketService } from '@owlmeans/server-socket'
|
|
133
|
-
import { createApiService } from '@owlmeans/server-api'
|
|
134
|
-
import { AppType } from '@owlmeans/context'
|
|
135
|
-
|
|
136
|
-
// Create server configuration
|
|
137
|
-
const config = makeServerConfig(AppType.Backend, 'websocket-server', {
|
|
138
|
-
host: 'localhost',
|
|
139
|
-
port: 3000
|
|
35
|
+
// Push updates to the client
|
|
36
|
+
const listener = ctx.fileStore().listen(record => {
|
|
37
|
+
conn.notify('file-update', record)
|
|
38
|
+
})
|
|
140
39
|
})
|
|
141
|
-
|
|
142
|
-
// Create context and services
|
|
143
|
-
const context = makeServerContext(config)
|
|
144
|
-
const apiService = createApiService()
|
|
145
|
-
const socketService = createSocketService()
|
|
146
|
-
|
|
147
|
-
// Register services
|
|
148
|
-
context.registerService(apiService)
|
|
149
|
-
context.registerService(socketService)
|
|
150
|
-
|
|
151
|
-
// Configure and initialize
|
|
152
|
-
context.configure()
|
|
153
|
-
await context.init()
|
|
154
|
-
|
|
155
|
-
console.log('WebSocket server running on ws://localhost:3000')
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### WebSocket Module with Authentication
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
import { module, guard } from '@owlmeans/module'
|
|
162
|
-
import { route } from '@owlmeans/route'
|
|
163
|
-
import { handleConnection } from '@owlmeans/server-socket'
|
|
164
|
-
import type { Connection } from '@owlmeans/socket'
|
|
165
|
-
|
|
166
|
-
// Create WebSocket module with authentication
|
|
167
|
-
const chatModule = module(
|
|
168
|
-
route('chat-socket', '/ws/chat'),
|
|
169
|
-
guard('authenticated')
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
// Set WebSocket handler
|
|
173
|
-
chatModule.handle = handleConnection<Connection>(
|
|
174
|
-
async (conn, ctx, req, res) => {
|
|
175
|
-
const userId = req.auth?.user?.id
|
|
176
|
-
|
|
177
|
-
if (!userId) {
|
|
178
|
-
await conn.close(1008, 'Authentication required')
|
|
179
|
-
return
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
console.log(`User ${userId} connected to chat`)
|
|
183
|
-
|
|
184
|
-
// Join user to chat room
|
|
185
|
-
await joinChatRoom(conn, userId)
|
|
186
|
-
|
|
187
|
-
// Handle incoming messages
|
|
188
|
-
conn.onMessage(async (message) => {
|
|
189
|
-
if (message.type === 'chat-message') {
|
|
190
|
-
await broadcastMessage(userId, message.content)
|
|
191
|
-
}
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
// Handle disconnection
|
|
195
|
-
conn.onClose(() => {
|
|
196
|
-
console.log(`User ${userId} disconnected from chat`)
|
|
197
|
-
leaveChatRoom(userId)
|
|
198
|
-
})
|
|
199
|
-
}
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
// Register module with context
|
|
203
|
-
context.registerModule(chatModule)
|
|
204
40
|
```
|
|
205
41
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
import { handleConnection } from '@owlmeans/server-socket'
|
|
210
|
-
import type { Connection } from '@owlmeans/socket'
|
|
211
|
-
|
|
212
|
-
const dataStreamModule = module(
|
|
213
|
-
route('data-stream', '/ws/data'),
|
|
214
|
-
guard('authenticated')
|
|
215
|
-
)
|
|
42
|
+
## API
|
|
216
43
|
|
|
217
|
-
|
|
218
|
-
async (conn, ctx, req, res) => {
|
|
219
|
-
const userId = req.auth?.user?.id
|
|
220
|
-
|
|
221
|
-
// Start streaming data to client
|
|
222
|
-
const streamInterval = setInterval(async () => {
|
|
223
|
-
const data = await fetchLatestData(userId)
|
|
224
|
-
await conn.send({
|
|
225
|
-
type: 'data-update',
|
|
226
|
-
timestamp: Date.now(),
|
|
227
|
-
data: data
|
|
228
|
-
})
|
|
229
|
-
}, 1000)
|
|
230
|
-
|
|
231
|
-
// Handle client requests
|
|
232
|
-
conn.onMessage(async (message) => {
|
|
233
|
-
switch (message.type) {
|
|
234
|
-
case 'subscribe':
|
|
235
|
-
await subscribeToChannel(userId, message.channel)
|
|
236
|
-
break
|
|
237
|
-
case 'unsubscribe':
|
|
238
|
-
await unsubscribeFromChannel(userId, message.channel)
|
|
239
|
-
break
|
|
240
|
-
case 'get-history':
|
|
241
|
-
const history = await getHistoricalData(message.from, message.to)
|
|
242
|
-
await conn.send({
|
|
243
|
-
type: 'history-data',
|
|
244
|
-
requestId: message.requestId,
|
|
245
|
-
data: history
|
|
246
|
-
})
|
|
247
|
-
break
|
|
248
|
-
}
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
// Cleanup on disconnect
|
|
252
|
-
conn.onClose(() => {
|
|
253
|
-
clearInterval(streamInterval)
|
|
254
|
-
unsubscribeFromAllChannels(userId)
|
|
255
|
-
})
|
|
256
|
-
}
|
|
257
|
-
)
|
|
258
|
-
```
|
|
44
|
+
### `handleConnection<T>(handler): RefedModuleHandler`
|
|
259
45
|
|
|
260
|
-
|
|
46
|
+
Wraps a WebSocket handler. The handler receives `(conn: T, ctx, req, res)`.
|
|
261
47
|
|
|
262
48
|
```typescript
|
|
263
|
-
|
|
264
|
-
import { route } from '@owlmeans/route'
|
|
265
|
-
import { handleConnection } from '@owlmeans/server-socket'
|
|
266
|
-
|
|
267
|
-
// Define message validation schema
|
|
268
|
-
const messageSchema = {
|
|
269
|
-
type: 'object',
|
|
270
|
-
properties: {
|
|
271
|
-
type: { type: 'string', enum: ['join', 'leave', 'message'] },
|
|
272
|
-
content: { type: 'string', maxLength: 1000 },
|
|
273
|
-
roomId: { type: 'string' }
|
|
274
|
-
},
|
|
275
|
-
required: ['type']
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const validatedSocketModule = module(
|
|
279
|
-
route('validated-socket', '/ws/validated'),
|
|
280
|
-
filter(body(messageSchema))
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
validatedSocketModule.handle = handleConnection<Connection>(
|
|
284
|
-
async (conn, ctx, req, res) => {
|
|
285
|
-
conn.onMessage(async (message) => {
|
|
286
|
-
// Message is automatically validated against schema
|
|
287
|
-
try {
|
|
288
|
-
switch (message.type) {
|
|
289
|
-
case 'join':
|
|
290
|
-
await joinRoom(conn, message.roomId)
|
|
291
|
-
await conn.send({ type: 'joined', roomId: message.roomId })
|
|
292
|
-
break
|
|
293
|
-
case 'leave':
|
|
294
|
-
await leaveRoom(conn, message.roomId)
|
|
295
|
-
await conn.send({ type: 'left', roomId: message.roomId })
|
|
296
|
-
break
|
|
297
|
-
case 'message':
|
|
298
|
-
await broadcastToRoom(message.roomId, {
|
|
299
|
-
type: 'room-message',
|
|
300
|
-
content: message.content,
|
|
301
|
-
sender: req.auth?.user?.id
|
|
302
|
-
})
|
|
303
|
-
break
|
|
304
|
-
}
|
|
305
|
-
} catch (error) {
|
|
306
|
-
await conn.send({
|
|
307
|
-
type: 'error',
|
|
308
|
-
message: error.message
|
|
309
|
-
})
|
|
310
|
-
}
|
|
311
|
-
})
|
|
312
|
-
}
|
|
313
|
-
)
|
|
49
|
+
handler: (conn: T, ctx: Context, req: AbstractRequest, res: AbstractResponse) => Promise<void>
|
|
314
50
|
```
|
|
315
51
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
import { handleConnection } from '@owlmeans/server-socket'
|
|
320
|
-
import type { Connection } from '@owlmeans/socket'
|
|
321
|
-
|
|
322
|
-
// Global connection registry
|
|
323
|
-
const connections = new Map<string, Connection>()
|
|
324
|
-
|
|
325
|
-
const managedConnectionModule = module(
|
|
326
|
-
route('managed-connection', '/ws/managed')
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
managedConnectionModule.handle = handleConnection<Connection>(
|
|
330
|
-
async (conn, ctx, req, res) => {
|
|
331
|
-
const connectionId = generateConnectionId()
|
|
332
|
-
|
|
333
|
-
// Register connection
|
|
334
|
-
connections.set(connectionId, conn)
|
|
335
|
-
|
|
336
|
-
// Send connection info
|
|
337
|
-
await conn.send({
|
|
338
|
-
type: 'connection-established',
|
|
339
|
-
connectionId: connectionId,
|
|
340
|
-
serverTime: Date.now()
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
// Handle ping/pong for connection health
|
|
344
|
-
conn.onMessage(async (message) => {
|
|
345
|
-
if (message.type === 'ping') {
|
|
346
|
-
await conn.send({ type: 'pong', timestamp: Date.now() })
|
|
347
|
-
}
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
// Cleanup on disconnect
|
|
351
|
-
conn.onClose(() => {
|
|
352
|
-
connections.delete(connectionId)
|
|
353
|
-
console.log(`Connection ${connectionId} closed`)
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
// Send periodic server stats
|
|
357
|
-
const statsInterval = setInterval(async () => {
|
|
358
|
-
await conn.send({
|
|
359
|
-
type: 'server-stats',
|
|
360
|
-
activeConnections: connections.size,
|
|
361
|
-
serverUptime: process.uptime()
|
|
362
|
-
})
|
|
363
|
-
}, 30000)
|
|
364
|
-
|
|
365
|
-
conn.onClose(() => {
|
|
366
|
-
clearInterval(statsInterval)
|
|
367
|
-
})
|
|
368
|
-
}
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
// Function to broadcast to all connections
|
|
372
|
-
const broadcastToAll = async (message: any) => {
|
|
373
|
-
const promises = Array.from(connections.values()).map(conn =>
|
|
374
|
-
conn.send(message).catch(console.error)
|
|
375
|
-
)
|
|
376
|
-
await Promise.allSettled(promises)
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### Error Handling
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
import { handleConnection } from '@owlmeans/server-socket'
|
|
384
|
-
import { ResilientError } from '@owlmeans/error'
|
|
385
|
-
|
|
386
|
-
const errorHandlingModule = module(
|
|
387
|
-
route('error-handling', '/ws/errors')
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
errorHandlingModule.handle = handleConnection<Connection>(
|
|
391
|
-
async (conn, ctx, req, res) => {
|
|
392
|
-
conn.onMessage(async (message) => {
|
|
393
|
-
try {
|
|
394
|
-
await processMessage(message)
|
|
395
|
-
} catch (error) {
|
|
396
|
-
if (error instanceof ResilientError) {
|
|
397
|
-
// Handle known errors gracefully
|
|
398
|
-
await conn.send({
|
|
399
|
-
type: 'error',
|
|
400
|
-
code: error.code,
|
|
401
|
-
message: error.message,
|
|
402
|
-
recoverable: true
|
|
403
|
-
})
|
|
404
|
-
} else {
|
|
405
|
-
// Handle unknown errors
|
|
406
|
-
console.error('Unexpected error:', error)
|
|
407
|
-
await conn.send({
|
|
408
|
-
type: 'error',
|
|
409
|
-
message: 'Internal server error',
|
|
410
|
-
recoverable: false
|
|
411
|
-
})
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
})
|
|
415
|
-
|
|
416
|
-
conn.onError((error) => {
|
|
417
|
-
console.error('WebSocket connection error:', error)
|
|
418
|
-
})
|
|
419
|
-
}
|
|
420
|
-
)
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
## Integration Patterns
|
|
424
|
-
|
|
425
|
-
### With Chat Systems
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// Real-time chat implementation
|
|
429
|
-
const chatRooms = new Map<string, Set<Connection>>()
|
|
430
|
-
|
|
431
|
-
const joinRoom = async (conn: Connection, roomId: string) => {
|
|
432
|
-
if (!chatRooms.has(roomId)) {
|
|
433
|
-
chatRooms.set(roomId, new Set())
|
|
434
|
-
}
|
|
435
|
-
chatRooms.get(roomId)!.add(conn)
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
const broadcastToRoom = async (roomId: string, message: any) => {
|
|
439
|
-
const room = chatRooms.get(roomId)
|
|
440
|
-
if (room) {
|
|
441
|
-
const promises = Array.from(room).map(conn => conn.send(message))
|
|
442
|
-
await Promise.allSettled(promises)
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### With Gaming Applications
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
// Game server with real-time updates
|
|
451
|
-
const gameSessionModule = module(
|
|
452
|
-
route('game-session', '/ws/game/:sessionId'),
|
|
453
|
-
guard('authenticated')
|
|
454
|
-
)
|
|
455
|
-
|
|
456
|
-
gameSessionModule.handle = handleConnection<Connection>(
|
|
457
|
-
async (conn, ctx, req, res) => {
|
|
458
|
-
const sessionId = req.params.sessionId
|
|
459
|
-
const playerId = req.auth?.user?.id
|
|
460
|
-
|
|
461
|
-
await joinGameSession(sessionId, playerId, conn)
|
|
462
|
-
|
|
463
|
-
conn.onMessage(async (message) => {
|
|
464
|
-
switch (message.type) {
|
|
465
|
-
case 'player-move':
|
|
466
|
-
await processPlayerMove(sessionId, playerId, message.move)
|
|
467
|
-
await broadcastGameState(sessionId)
|
|
468
|
-
break
|
|
469
|
-
case 'player-action':
|
|
470
|
-
await processPlayerAction(sessionId, playerId, message.action)
|
|
471
|
-
break
|
|
472
|
-
}
|
|
473
|
-
})
|
|
474
|
-
}
|
|
475
|
-
)
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
### With Monitoring Systems
|
|
479
|
-
|
|
480
|
-
```typescript
|
|
481
|
-
// Real-time monitoring dashboard
|
|
482
|
-
const monitoringModule = module(
|
|
483
|
-
route('monitoring', '/ws/monitor'),
|
|
484
|
-
guard('admin')
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
monitoringModule.handle = handleConnection<Connection>(
|
|
488
|
-
async (conn, ctx, req, res) => {
|
|
489
|
-
// Send initial system state
|
|
490
|
-
await conn.send({
|
|
491
|
-
type: 'system-state',
|
|
492
|
-
data: await getSystemMetrics()
|
|
493
|
-
})
|
|
494
|
-
|
|
495
|
-
// Subscribe to system events
|
|
496
|
-
const unsubscribe = await subscribeToSystemEvents(async (event) => {
|
|
497
|
-
await conn.send({
|
|
498
|
-
type: 'system-event',
|
|
499
|
-
event: event
|
|
500
|
-
})
|
|
501
|
-
})
|
|
502
|
-
|
|
503
|
-
conn.onClose(unsubscribe)
|
|
504
|
-
}
|
|
505
|
-
)
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
## Security Considerations
|
|
509
|
-
|
|
510
|
-
### Authentication and Authorization
|
|
511
|
-
- **Guard Integration** - Use authentication guards to protect WebSocket endpoints
|
|
512
|
-
- **Token Validation** - Validate JWT tokens or session tokens before accepting connections
|
|
513
|
-
- **Connection Limits** - Implement connection limits per user or IP address
|
|
514
|
-
|
|
515
|
-
### Data Validation
|
|
516
|
-
- **Message Validation** - Always validate incoming WebSocket messages
|
|
517
|
-
- **Rate Limiting** - Implement rate limiting for message frequency
|
|
518
|
-
- **Sanitization** - Sanitize user input to prevent injection attacks
|
|
519
|
-
|
|
520
|
-
### Connection Security
|
|
521
|
-
- **TLS/SSL** - Use secure WebSocket connections (WSS) in production
|
|
522
|
-
- **Origin Validation** - Validate connection origins to prevent CSRF attacks
|
|
523
|
-
- **Connection Timeouts** - Implement proper timeout mechanisms
|
|
524
|
-
|
|
525
|
-
## Best Practices
|
|
526
|
-
|
|
527
|
-
1. **Implement proper error handling** - Always handle connection errors and message processing errors
|
|
528
|
-
2. **Use connection pooling** - Manage connections efficiently to prevent memory leaks
|
|
529
|
-
3. **Implement heartbeat mechanism** - Use ping/pong to detect dead connections
|
|
530
|
-
4. **Validate all messages** - Never trust incoming WebSocket messages without validation
|
|
531
|
-
5. **Handle cleanup properly** - Always clean up resources when connections close
|
|
532
|
-
6. **Monitor connection health** - Track connection metrics and performance
|
|
533
|
-
|
|
534
|
-
## Error Handling
|
|
52
|
+
The `conn` object is a server-side `Connection` (from `@owlmeans/socket`).
|
|
535
53
|
|
|
536
|
-
|
|
54
|
+
### `appendSocketService<C, T>(context): void`
|
|
537
55
|
|
|
538
|
-
|
|
539
|
-
- **Connection errors** - Proper handling of WebSocket connection failures
|
|
540
|
-
- **Authentication errors** - Clear error messages for authentication failures
|
|
541
|
-
- **Validation errors** - Detailed validation error reporting
|
|
56
|
+
Registers the WebSocket service into the context. Called automatically by `makeContext()`.
|
|
542
57
|
|
|
543
|
-
|
|
58
|
+
### `createSocketMiddleware(): Middleware`
|
|
544
59
|
|
|
545
|
-
|
|
546
|
-
- `@owlmeans/context` - For service registration and context management
|
|
547
|
-
- `@owlmeans/server-api` - For API server integration
|
|
548
|
-
- `@owlmeans/server-context` - For server-side context management
|
|
549
|
-
- `@owlmeans/server-module` - For module system integration
|
|
550
|
-
- `@owlmeans/module` - For core module functionality
|
|
551
|
-
- `@owlmeans/socket` - For connection abstractions
|
|
552
|
-
- `@owlmeans/error` - For error handling
|
|
553
|
-
- `@fastify/websocket` - For WebSocket server implementation
|
|
60
|
+
Creates the middleware that initializes the socket service during context init.
|
|
554
61
|
|
|
555
62
|
## Related Packages
|
|
556
63
|
|
|
557
|
-
- [`@owlmeans/socket`](../socket)
|
|
558
|
-
- [`@owlmeans/
|
|
559
|
-
- [`@owlmeans/
|
|
560
|
-
- [`@owlmeans/server-module`](../server-module) - Server-side module system
|
|
64
|
+
- [`@owlmeans/socket`](../socket) — `Connection`, `MessageType`, `EventMessage` types
|
|
65
|
+
- [`@owlmeans/server-app`](../server-app) — calls `appendSocketService` in `makeContext`
|
|
66
|
+
- [`@owlmeans/client-socket`](../client-socket) — client-side counterpart
|
package/build/helper.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { AbstractRequest, AbstractResponse } from '@owlmeans/
|
|
1
|
+
import type { AbstractRequest, AbstractResponse } from '@owlmeans/entrypoint';
|
|
2
2
|
import type { Context } from '@owlmeans/server-api';
|
|
3
|
-
import type {
|
|
3
|
+
import type { RefedEntrypointHandler } from '@owlmeans/server-entrypoint';
|
|
4
4
|
import type { Connection } from '@owlmeans/socket';
|
|
5
5
|
import type { WebSocket } from '@fastify/websocket';
|
|
6
|
-
export declare const handleConnection: <T extends Connection = Connection>(handler: (conn: T, ctx: Context, req: AbstractRequest<WebSocket>, res: AbstractResponse<any>) => Promise<void>) =>
|
|
6
|
+
export declare const handleConnection: <T extends Connection = Connection>(handler: (conn: T, ctx: Context, req: AbstractRequest<WebSocket>, res: AbstractResponse<any>) => Promise<void>) => RefedEntrypointHandler<AbstractResponse<any>>;
|
|
7
7
|
//# sourceMappingURL=helper.d.ts.map
|
package/build/helper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGnD,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAC/D,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,gBAAgB,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,KAC3G,sBAAsB,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAUhD,CAAA"}
|
package/build/helper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAMjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,MAAM,CAAC,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAMjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,MAAM,CAAC,MAAM,gBAAgB,GAEwB,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACxF,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAY,CAAA;IAClD,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;IAC/D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,GAAG,CAAC,MAAM,CAAC,CAAU,CAAC,CAAA;IACxB,CAAC;IAED,yDAAyD;IACzD,OAAO,GAAG,CAAC,KAAK,CAAA;AAClB,CAAC,CAAA"}
|
package/build/service.js
CHANGED
|
@@ -3,7 +3,7 @@ import { DEFAULT_ALIAS } from './consts.js';
|
|
|
3
3
|
import { canServerModule } from './utils/server.js';
|
|
4
4
|
import { fastifyWebsocket } from '@fastify/websocket';
|
|
5
5
|
import { authorize, executeResponse, extractContext, handleError, populateContext, provideRequest } from '@owlmeans/server-api/utils';
|
|
6
|
-
import {
|
|
6
|
+
import { EntrypointOutcome, provideResponse } from '@owlmeans/entrypoint';
|
|
7
7
|
import { ResilientError } from '@owlmeans/error';
|
|
8
8
|
export const createSocketService = (alias = DEFAULT_ALIAS) => {
|
|
9
9
|
const service = createService(alias, {
|
|
@@ -16,7 +16,7 @@ export const createSocketService = (alias = DEFAULT_ALIAS) => {
|
|
|
16
16
|
await api.server.register(async (server) => {
|
|
17
17
|
server.addHook('preHandler', async (req, reply) => {
|
|
18
18
|
const context = extractContext(req, service.ctx, alias);
|
|
19
|
-
await context?.
|
|
19
|
+
await context?.entrypoints()
|
|
20
20
|
.filter(module => canServerModule(context, module) && !module.route.isIntermediate())
|
|
21
21
|
.reduce(async (ctx, module) => {
|
|
22
22
|
let context = await ctx;
|
|
@@ -55,7 +55,7 @@ export const createSocketService = (alias = DEFAULT_ALIAS) => {
|
|
|
55
55
|
return context;
|
|
56
56
|
}, Promise.resolve(context));
|
|
57
57
|
});
|
|
58
|
-
await Promise.all(ctx.
|
|
58
|
+
await Promise.all(ctx.entrypoints().filter(module => canServerModule(ctx, module) && !module.route.isIntermediate()).map(async (module) => {
|
|
59
59
|
await module.resolve();
|
|
60
60
|
if (module.handle == null) {
|
|
61
61
|
return;
|
|
@@ -74,7 +74,7 @@ export const createSocketService = (alias = DEFAULT_ALIAS) => {
|
|
|
74
74
|
void module.handle(request, {
|
|
75
75
|
resolve: (value, outcome) => {
|
|
76
76
|
conn.send(typeof value === 'string' ? value : JSON.stringify(value));
|
|
77
|
-
if (outcome ===
|
|
77
|
+
if (outcome === EntrypointOutcome.Ok) {
|
|
78
78
|
conn.close();
|
|
79
79
|
}
|
|
80
80
|
},
|
package/build/service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAGhE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AACrI,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WebSocket } from '@fastify/websocket';
|
|
2
2
|
import type { Connection } from '@owlmeans/socket';
|
|
3
3
|
import type { Config, Context } from '@owlmeans/server-api';
|
|
4
|
-
import { AbstractRequest } from '@owlmeans/
|
|
4
|
+
import { AbstractRequest } from '@owlmeans/entrypoint';
|
|
5
5
|
export declare const makeConnection: <C extends Config, T extends Context<C> = Context<C>>(request: AbstractRequest<WebSocket>, context: T) => Connection;
|
|
6
6
|
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAI3D,OAAO,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAI3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAOtD,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAChF,SAAS,eAAe,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,KAC9C,UA0IF,CAAA"}
|
|
@@ -82,8 +82,35 @@ export const makeConnection = (request, context) => {
|
|
|
82
82
|
const messageHandler = async (_message) => {
|
|
83
83
|
_message = Array.isArray(_message) ? _message : [_message];
|
|
84
84
|
const message = _message.map(msg => msg.toString('utf8')).join('');
|
|
85
|
+
if (message.startsWith('{') || message.startsWith('[')) {
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(message);
|
|
88
|
+
if (parsed.type === 'ping') {
|
|
89
|
+
console.log("Received soft ping, sending pong.");
|
|
90
|
+
conn.pong();
|
|
91
|
+
conn.send(JSON.stringify({ type: 'pong' }));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
console.error(e);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
85
98
|
await model.receive(message);
|
|
86
99
|
};
|
|
100
|
+
conn.on("ping", () => {
|
|
101
|
+
console.log("Received hard ping, sending pong.");
|
|
102
|
+
conn.pong();
|
|
103
|
+
});
|
|
104
|
+
conn.on('open', (ws) => {
|
|
105
|
+
const heartbeat = setInterval(() => {
|
|
106
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
107
|
+
ws.ping();
|
|
108
|
+
}
|
|
109
|
+
}, 30_000);
|
|
110
|
+
conn.on('close', () => {
|
|
111
|
+
clearInterval(heartbeat);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
87
114
|
const closeHandler = async (code) => {
|
|
88
115
|
const msg = {
|
|
89
116
|
type: MessageType.System,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/utils/connection.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,iBAAiB,EAAE,qBAAqB,EAAE,WAAW,EAAE,yBAAyB,EAAE,kBAAkB,EACrG,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAG1E,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,OAAmC,EAAE,OAAU,EACnC,EAAE;IACd,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,IAAI,yBAAyB,CAAC,SAAS,CAAC,CAAA;IAChD,CAAC;IACD,IAAI,IAAI,GAA8C,SAAS,CAAA;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IAEzB,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAA;IAErC,KAAK,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE;QACvB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;QAClB,6CAA6C;IAC/C,CAAC,CAAA;IAED,KAAK,CAAC,IAAI,GAAG,KAAK,EAAC,OAAO,EAAC,EAAE;QAC3B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,KAAK,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IAClF,CAAC,CAAA;IAED,uEAAuE;IACvE,KAAK,CAAC,YAAY,GAAG,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5C,IAAI,mBAAmB,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;YAC/C,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,OAAgC,CAAA;gBAC5C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;gBACpD,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;oBAClB,MAAM,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAA;gBACrC,CAAC;gBACD,MAAM,KAAK,GAAG,iBAAiB,CAAO,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;gBACtE,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAA;gBAEtB,OAAO,CAAC,mBAAmB,CAAC,aAAa,EAAE,KAAY,CAAC,CAAA;YAC1D,CAAC;YACD,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpB,IAAI,GAAG,OAAO,CAAA;gBAEd,OAAO,CAAC,KAAK,EAAE,IAAW,CAAC,CAAA;YAC7B,CAAC;QACH,CAAC;aAAM,IAAI,mBAAmB,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;YACvD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBACjB,MAAM,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAA;YACrC,CAAC;YAED,OAAO,CAAC,KAAK,EAAE,IAAW,CAAC,CAAA;QAC7B,CAAC;QACD,MAAM,IAAI,iBAAiB,CAAC,cAAc,CAAC,CAAA;IAC7C,CAAC,CAAA;IAED,KAAK,CAAC,OAAO,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE;QACrC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,IAAI,CAAA;YACX,IAAI,CAAC;gBACH,IAAI,aAAa,GAAI,OAAO,CAAC,KAAa,EAAE,CAAC,UAAU,CAAC,CAAA;gBACxD,IAAI,aAAa,IAAI,IAAI,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG,iBAAiB,CAAyB,aAAa,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;oBAC7F,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAA;oBAChC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC9C,IAAI,GAAG,KAAK,CAAA;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAA;YACrC,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;gBAC9C,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,CAAA;YACnD,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;gBAC3B,oCAAoC;gBACpC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAA;YACtC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;gBAC3C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,CAAA;YAChD,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;IAED,MAAM,cAAc,GAAG,KAAK,EAAE,QAA2B,EAAE,EAAE;QAC3D,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;QAC1D,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAElE,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;gBAClC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC3B,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;oBAChD,IAAI,CAAC,IAAI,EAAE,CAAA;oBACX,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;gBAC7C,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;QAED,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC,CAAA;IAED,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACnB,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;QAChD,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAa,EAAE,EAAE;QAChC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBACrC,EAAE,CAAC,IAAI,EAAE,CAAA;YACX,CAAC;QACH,CAAC,EAAE,MAAM,CAAC,CAAA;QACV,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACpB,aAAa,CAAC,SAAS,CAAC,CAAA;QAC1B,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,MAAM,YAAY,GAAG,KAAK,EAAE,IAAY,EAAE,EAAE;QAC1C,MAAM,GAAG,GAAmC;YAC1C,IAAI,EAAE,WAAW,CAAC,MAAM;YACxB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,EAAE,IAAI,EAAE;SAClB,CAAA;QACD,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YAC1B,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACpB,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,KAAK,EAAC,QAAQ,EAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QAC5E,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,CAAA;QACnC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;IACjC,CAAC,CAAA;IAED,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,CAAC,CAAA;IAClC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;IAE9B,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
|
package/build/utils/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { CommonEntrypoint } from '@owlmeans/entrypoint';
|
|
2
|
+
import type { ServerEntrypoint } from '@owlmeans/server-entrypoint';
|
|
3
3
|
import type { Context } from '@owlmeans/server-api';
|
|
4
|
-
export declare const canServerModule: (context: Context, module:
|
|
4
|
+
export declare const canServerModule: (context: Context, module: CommonEntrypoint) => module is ServerEntrypoint<unknown>;
|
|
5
5
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAGnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEnD,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,EAAE,QAAQ,gBAAgB,KAAG,MAAM,IAAI,gBAAgB,CAAC,OAAO,CAc9G,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAG3C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAG3C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAgB,EAAE,MAAwB,EAAuC,EAAE;IACjH,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;QAChD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC7F,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,KAAK,cAAc,CAAC,MAAM,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,gBAAgB,IAAI,MAAM,CAAC,KAAK,CAAA;AACzC,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/server-socket",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -21,24 +21,25 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
24
25
|
"@types/connect": "^3.4.38",
|
|
25
26
|
"nodemon": "^3.1.11",
|
|
26
|
-
"typescript": "^
|
|
27
|
+
"typescript": "^6.0.2"
|
|
27
28
|
},
|
|
28
29
|
"peerDependencies": {
|
|
29
30
|
"fastify": "*"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"@fastify/websocket": "^11.0.1",
|
|
33
|
-
"@owlmeans/auth": "^0.1.
|
|
34
|
-
"@owlmeans/basic-envelope": "^0.1.
|
|
35
|
-
"@owlmeans/context": "^0.1.
|
|
36
|
-
"@owlmeans/
|
|
37
|
-
"@owlmeans/server-api": "^0.1.
|
|
38
|
-
"@owlmeans/server-auth": "^0.1.
|
|
39
|
-
"@owlmeans/server-context": "^0.1.
|
|
40
|
-
"@owlmeans/server-
|
|
41
|
-
"@owlmeans/socket": "^0.1.
|
|
34
|
+
"@owlmeans/auth": "^0.1.4",
|
|
35
|
+
"@owlmeans/basic-envelope": "^0.1.4",
|
|
36
|
+
"@owlmeans/context": "^0.1.4",
|
|
37
|
+
"@owlmeans/entrypoint": "^0.1.4",
|
|
38
|
+
"@owlmeans/server-api": "^0.1.4",
|
|
39
|
+
"@owlmeans/server-auth": "^0.1.4",
|
|
40
|
+
"@owlmeans/server-context": "^0.1.4",
|
|
41
|
+
"@owlmeans/server-entrypoint": "^0.1.4",
|
|
42
|
+
"@owlmeans/socket": "^0.1.4"
|
|
42
43
|
},
|
|
43
44
|
"publishConfig": {
|
|
44
45
|
"access": "public"
|
package/src/helper.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { assertContext } from '@owlmeans/context'
|
|
2
|
-
import type { AbstractRequest, AbstractResponse } from '@owlmeans/
|
|
2
|
+
import type { AbstractRequest, AbstractResponse } from '@owlmeans/entrypoint'
|
|
3
3
|
import type { Context } from '@owlmeans/server-api'
|
|
4
|
-
import type {
|
|
4
|
+
import type { RefedEntrypointHandler } from '@owlmeans/server-entrypoint'
|
|
5
5
|
import type { Connection } from '@owlmeans/socket'
|
|
6
6
|
import type { WebSocket } from '@fastify/websocket'
|
|
7
7
|
import { makeConnection } from './utils/connection.js'
|
|
8
8
|
|
|
9
9
|
export const handleConnection: <T extends Connection = Connection>(
|
|
10
10
|
handler: (conn: T, ctx: Context, req: AbstractRequest<WebSocket>, res: AbstractResponse<any>) => Promise<void>
|
|
11
|
-
) =>
|
|
11
|
+
) => RefedEntrypointHandler<AbstractResponse<any>> = handler => ref => async (req, res) => {
|
|
12
12
|
const ctx = assertContext(ref.ref?.ctx) as Context
|
|
13
13
|
try {
|
|
14
14
|
await handler(makeConnection(req, ctx) as any, ctx, req, res)
|
package/src/service.ts
CHANGED
|
@@ -2,13 +2,13 @@ import { assertContext, createService } from '@owlmeans/context'
|
|
|
2
2
|
import type { SocketService } from './types.js'
|
|
3
3
|
import type { Config, Context, Request } from '@owlmeans/server-api'
|
|
4
4
|
import { DEFAULT_ALIAS } from './consts.js'
|
|
5
|
-
import type { FixerService,
|
|
5
|
+
import type { FixerService, ServerEntrypoint } from '@owlmeans/server-entrypoint'
|
|
6
6
|
import { canServerModule } from './utils/server.js'
|
|
7
7
|
import { fastifyWebsocket } from '@fastify/websocket'
|
|
8
8
|
import type { WebSocket } from '@fastify/websocket'
|
|
9
9
|
import { authorize, executeResponse, extractContext, handleError, populateContext, provideRequest } from '@owlmeans/server-api/utils'
|
|
10
|
-
import {
|
|
11
|
-
import type { AbstractRequest, GateService } from '@owlmeans/
|
|
10
|
+
import { EntrypointOutcome, provideResponse } from '@owlmeans/entrypoint'
|
|
11
|
+
import type { AbstractRequest, GateService } from '@owlmeans/entrypoint'
|
|
12
12
|
import { ResilientError } from '@owlmeans/error'
|
|
13
13
|
|
|
14
14
|
export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketService => {
|
|
@@ -23,7 +23,7 @@ export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketServic
|
|
|
23
23
|
await api.server.register(async server => {
|
|
24
24
|
server.addHook('preHandler', async (req, reply) => {
|
|
25
25
|
const context = extractContext(req, service.ctx as Context, alias)
|
|
26
|
-
await context?.
|
|
26
|
+
await context?.entrypoints<ServerEntrypoint<Request>>()
|
|
27
27
|
.filter(module => canServerModule(context, module) && !module.route.isIntermediate())
|
|
28
28
|
.reduce<Promise<Context>>(async (ctx, module) => {
|
|
29
29
|
let context = await ctx
|
|
@@ -68,7 +68,7 @@ export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketServic
|
|
|
68
68
|
}, Promise.resolve(context))
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
await Promise.all(ctx.
|
|
71
|
+
await Promise.all(ctx.entrypoints<ServerEntrypoint<Request>>().filter(
|
|
72
72
|
module => canServerModule(ctx, module) && !module.route.isIntermediate()
|
|
73
73
|
).map(async module => {
|
|
74
74
|
await module.resolve()
|
|
@@ -92,7 +92,7 @@ export const createSocketService = (alias: string = DEFAULT_ALIAS): SocketServic
|
|
|
92
92
|
void module.handle<AbstractRequest<WebSocket>>(request, {
|
|
93
93
|
resolve: (value, outcome) => {
|
|
94
94
|
conn.send(typeof value === 'string' ? value : JSON.stringify(value))
|
|
95
|
-
if (outcome ===
|
|
95
|
+
if (outcome === EntrypointOutcome.Ok) {
|
|
96
96
|
conn.close()
|
|
97
97
|
}
|
|
98
98
|
},
|
package/src/utils/connection.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { Config, Context } from '@owlmeans/server-api'
|
|
|
4
4
|
import {
|
|
5
5
|
SocketUnsupported, createBasicConnection, MessageType, SocketInitializationError, SocketUnauthorized
|
|
6
6
|
} from '@owlmeans/socket'
|
|
7
|
-
import { AbstractRequest } from '@owlmeans/
|
|
7
|
+
import { AbstractRequest } from '@owlmeans/entrypoint'
|
|
8
8
|
import { AuthenticationStage, AUTH_QUERY } from '@owlmeans/auth'
|
|
9
9
|
import type { Auth, AuthCredentials } from '@owlmeans/auth'
|
|
10
10
|
import { isAuth, isAuthCredentials, isAuthToken } from '@owlmeans/auth'
|
|
@@ -100,9 +100,39 @@ export const makeConnection = <C extends Config, T extends Context<C> = Context<
|
|
|
100
100
|
const messageHandler = async (_message: Buffer | Buffer[]) => {
|
|
101
101
|
_message = Array.isArray(_message) ? _message : [_message]
|
|
102
102
|
const message = _message.map(msg => msg.toString('utf8')).join('')
|
|
103
|
+
|
|
104
|
+
if (message.startsWith('{') || message.startsWith('[')) {
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(message)
|
|
107
|
+
if (parsed.type === 'ping') {
|
|
108
|
+
console.log("Received soft ping, sending pong.")
|
|
109
|
+
conn.pong()
|
|
110
|
+
conn.send(JSON.stringify({ type: 'pong' }))
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.error(e)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
103
117
|
await model.receive(message)
|
|
104
118
|
}
|
|
105
119
|
|
|
120
|
+
conn.on("ping", () => {
|
|
121
|
+
console.log("Received hard ping, sending pong.")
|
|
122
|
+
conn.pong()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
conn.on('open', (ws: WebSocket) => {
|
|
126
|
+
const heartbeat = setInterval(() => {
|
|
127
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
128
|
+
ws.ping()
|
|
129
|
+
}
|
|
130
|
+
}, 30_000)
|
|
131
|
+
conn.on('close', () => {
|
|
132
|
+
clearInterval(heartbeat)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
106
136
|
const closeHandler = async (code: number) => {
|
|
107
137
|
const msg: EventMessage<{ code: number }> = {
|
|
108
138
|
type: MessageType.System,
|
package/src/utils/server.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { CommonEntrypoint } from '@owlmeans/entrypoint'
|
|
2
|
+
import type { ServerEntrypoint } from '@owlmeans/server-entrypoint'
|
|
3
3
|
import { RouteProtocols } from '@owlmeans/route'
|
|
4
4
|
import { AppType } from '@owlmeans/context'
|
|
5
5
|
import type { Context } from '@owlmeans/server-api'
|
|
6
6
|
|
|
7
|
-
export const canServerModule = (context: Context, module:
|
|
7
|
+
export const canServerModule = (context: Context, module: CommonEntrypoint): module is ServerEntrypoint<unknown> => {
|
|
8
8
|
if (module.route.route.type !== AppType.Backend) {
|
|
9
9
|
return false
|
|
10
10
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json",
|
|
4
|
+
"@owlmeans/dep-config/tsconfig.react.json"
|
|
5
5
|
],
|
|
6
6
|
"compilerOptions": {
|
|
7
|
-
"rootDir": "./src/",
|
|
8
|
-
"outDir": "./build/"
|
|
9
|
-
"moduleResolution": "Bundler"
|
|
7
|
+
"rootDir": "./src/",
|
|
8
|
+
"outDir": "./build/"
|
|
10
9
|
},
|
|
11
|
-
"exclude": [
|
|
12
|
-
|
|
13
|
-
"./build/**/*",
|
|
14
|
-
"./*.ts"
|
|
15
|
-
]
|
|
16
|
-
}
|
|
10
|
+
"exclude": ["./dist/**/*", "./build/**/*", "./*.ts"]
|
|
11
|
+
}
|