@jcbuisson/express-x 1.8.3 → 2.0.0

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 CHANGED
@@ -33,23 +33,17 @@ backed in a [Prisma](https://www.prisma.io/) database
33
33
 
34
34
  ```js
35
35
  // app.js
36
- import bodyParser from 'body-parser'
37
36
  import { expressXServer } from '@jcbuisson/express-x'
38
37
 
39
38
  // `app` is a regular express application, enhanced with service and real-time features
40
39
  const app = expressX()
41
40
 
42
- // create two CRUD database services. They provide Prisma methods: `create`, 'createMany', 'find', 'findMany', 'upsert', etc.
43
- app.createDatabaseService('User')
44
- app.createDatabaseService('Post')
45
-
46
- // add body parsers for http requests
47
- app.use(bodyParser.json())
48
- app.use(bodyParser.urlencoded({ extended: false }))
41
+ // configure prisma client from schema
42
+ const prisma = new PrismaClient()
49
43
 
50
- // add http/rest endpoints
51
- app.addHttpRest('/api/user', app.service('User'))
52
- app.addHttpRest('/api/post', app.service('Post'))
44
+ // create two CRUD database services. They provide Prisma methods: `create`, 'createMany', 'find', 'findMany', 'upsert', etc.
45
+ app.createService('User', prisma.User)
46
+ app.createService('Post', prisma.Post)
53
47
 
54
48
  app.server.listen(8000, () => console.log(`App listening at http://localhost:8000`))
55
49
  ```
@@ -88,40 +82,18 @@ datasource db {
88
82
 
89
83
  Then create the database:
90
84
  ```bash
91
- npx prisma migrate dev --name init
85
+ npx prisma db push
92
86
  ```
93
87
 
94
88
  The sqlite database file is created at `prisma/dev.db`
95
89
 
96
90
 
97
- ## Run the application
91
+ ## Run the application on the server side
98
92
 
99
93
  ```bash
100
94
  node app.js
101
95
  ```
102
96
 
103
- It prints the following lines in the console:
104
- ```bash
105
- created service 'user' over entity 'User'
106
- created service 'post' over entity 'Post'
107
- added HTTP endpoints for service 'user' at path '/api/user'
108
- added HTTP endpoints for service 'post' at path '/api/post'
109
- App listening at http://localhost:8000
110
- ```
111
-
112
-
113
- ## Enjoy your HTTP REST API
114
-
115
- Now you can try the HTTP endpoints `/api/user` and `/api/post`; open a new console and run HTTP requests:
116
- ```bash
117
- curl -X POST -H 'Content-Type: application/json' -d '{"name":"JC"}' http://localhost:8000/api/user
118
- # --> {"id":1,"name":"JC"}
119
- curl http://localhost:8000/api/user
120
- # --> [{"id":1,"name":"JC"}]
121
- ```
122
-
123
- With a few lines of code, we got a complete REST API over the database tables. But there is more!
124
-
125
97
 
126
98
  ## Use it with a websocket client
127
99
 
@@ -162,9 +134,9 @@ async function main() {
162
134
  main()
163
135
  ```
164
136
 
165
- For simplicity we use a node client, but you of course you would write something similar with your favorite front-end framework.
137
+ For simplicity we use a node client, but you would write something similar with your favorite front-end framework.
166
138
 
167
- You can use on the client side the exact same statements on services as you would on the server side, such as: `app.service('User').create(...)`.
139
+ You can use the exact same statements on services on the client side as you would on the server side, such as: `app.service('User').create(...)`.
168
140
  Of course the `app` object here on the client is quite different that the `app` object on the server; you can find explanations [here]().
169
141
 
170
142
  Now run the client script:
@@ -218,11 +190,11 @@ import { PrismaClient } from '@prisma/client'
218
190
  const app = expressX()
219
191
 
220
192
  // configure prisma client from schema
221
- app.set('prisma', new PrismaClient())
193
+ const prisma = new PrismaClient()
222
194
 
223
195
  // create two CRUD database services. They provide Prisma methods: `create`, 'createMany', 'find', 'findMany', 'upsert', etc.
224
- app.createDatabaseService('User')
225
- app.createDatabaseService('Post')
196
+ app.createService('User', prisma.User)
197
+ app.createService('Post', prisma.Post)
226
198
 
227
199
  // publish
228
200
  app.service('User').publish(async (post, context) => {
@@ -233,9 +205,9 @@ app.service('Post').publish(async (post, context) => {
233
205
  })
234
206
 
235
207
  // subscribe
236
- app.on('connection', (connection) => {
237
- console.log('connection', connection.id)
238
- app.joinChannel('anonymous', connection)
208
+ app.on('connection', (socket) => {
209
+ console.log('connection', socket.id)
210
+ app.joinChannel('anonymous', socket)
239
211
  })
240
212
 
241
213
  app.server.listen(8000, () => console.log(`App listening at http://localhost:8000`))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jcbuisson/express-x",
3
- "version": "1.8.3",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "src/index.mjs",
@@ -17,7 +17,6 @@
17
17
  },
18
18
  "keywords": [],
19
19
  "dependencies": {
20
- "@prisma/client": "^4.10.1",
21
20
  "bcryptjs": "^2.4.3",
22
21
  "config": "^3.3.9",
23
22
  "express": "^4.18.2",
@@ -1,8 +1,11 @@
1
1
 
2
2
  import bcrypt from 'bcryptjs'
3
3
 
4
- import { getConnectionDataItem } from './context.mjs'
5
4
 
5
+ export const addTimestamp = (field) => async (context) => {
6
+ context.result[field] = (new Date()).toISOString()
7
+ return context
8
+ }
6
9
 
7
10
  /*
8
11
  * Hash password of user record
@@ -33,20 +36,22 @@ export function protect(field) {
33
36
 
34
37
  /*
35
38
  * Does nothing for calls which are not client-side with websocket transport
36
- * Check if the 'expireAt' key in connection data is met
37
- * If it is met, throw an error (which will be sent back to the calling server or client) and reset connection data
39
+ * Check if the 'expireAt' key in socket.data is met
40
+ * If it is met, throw an error (which will be sent back to the calling server or client) and reset socket.data
38
41
  * If not, do nothing. If needed, an application-level hook may automatically extend the expiration data at each service call
39
42
  */
40
43
  export const isNotExpired = async (context) => {
44
+ // return context
41
45
  if (context.caller !== 'client' || context.transport !== 'ws') return
42
46
 
43
- const expireAt = await getConnectionDataItem(context, 'expireAt')
47
+ const expireAt = context.socket.data.expireAt
44
48
  if (expireAt) {
45
49
  const expireAtDate = new Date(expireAt)
46
50
  const now = new Date()
47
51
  if (now > expireAtDate) {
48
- // expiration date is met: clear connection data & throw exception
49
- await resetConnection(context)
52
+ // expiration date is met: clear socket.data & throw exception
53
+ const { clientIP } = socket.data
54
+ socket.data = { clientIP }
50
55
  throw new Error('session-expired')
51
56
  }
52
57
  } else {
package/src/index.mjs CHANGED
@@ -1,20 +1,11 @@
1
1
 
2
2
  import { expressX } from './server.mjs'
3
3
  import { hashPassword, protect, isNotExpired } from './common-hooks.mjs'
4
- import { getContextConnection, resetConnection, getConnectionDataItem, setConnectionDataItem, removeConnectionDataItem, sendServiceEventToClient } from './context.mjs'
5
4
 
6
5
  export {
7
6
  expressX,
8
7
 
9
- getContextConnection,
10
- resetConnection,
11
-
12
- getConnectionDataItem,
13
- setConnectionDataItem,
14
- removeConnectionDataItem,
15
-
16
- sendServiceEventToClient,
17
-
8
+ addTimestamp,
18
9
  hashPassword,
19
10
  protect,
20
11
  isNotExpired,
package/src/server.mjs CHANGED
@@ -1,94 +1,146 @@
1
+ import express from "express"
2
+ import { createServer } from "http"
3
+ import { Server } from "socket.io"
1
4
 
2
- import http from 'http'
3
- import { Server } from 'socket.io'
4
- import express from 'express'
5
5
 
6
- /*
7
- * Enhance `app` express application with services and real-time features
8
- */
9
- export function expressX(prisma, config) {
6
+ export function expressX(config) {
7
+
8
+ const services = {}
9
+ let appHooks = []
10
10
 
11
11
  const app = express()
12
+ const httpServer = createServer(app)
12
13
 
13
14
  // so that config can be accessed anywhere with app.get('config')
14
15
  app.set('config', config)
15
16
 
16
- // websocket transport by default
17
- if (config.WS_TRANSPORT == undefined) config.WS_TRANSPORT = true
18
-
19
- const services = {}
20
- let appHooks = []
21
-
22
- const cnx2Socket = {}
17
+ const io = new Server(httpServer, {
18
+ path: config?.WS_PATH || '/socket.io/',
19
+ connectionStateRecovery: {
20
+ // the backup duration of the sessions and the packets
21
+ maxDisconnectionDuration: 2 * 60 * 1000,
22
+ // whether to skip middlewares upon successful recovery
23
+ skipMiddlewares: true,
24
+ }
25
+ })
23
26
 
24
- async function createConnection(clientIP) {
25
- const connection = await prisma.Connection.create({
26
- data: {
27
- clientIP,
28
- }
29
- })
30
- return connection
27
+ // logging function - a winston logger must be configured first
28
+ app.log = (severity, message) => {
29
+ const logger = app.get('logger')
30
+ if (logger) logger.log(severity, message); else console.log(`[${severity}]`, message)
31
31
  }
32
32
 
33
- function getConnection(id) {
34
- return prisma.Connection.findUnique({ where: { id }})
35
- }
33
+ io.on('connection', async function(socket) {
34
+ if (socket.recovered) {
35
+ // recovery was successful: socket.id, socket.rooms and socket.data were restored
36
+ console.log('reconnection!!!', socket.id)
36
37
 
37
- async function cloneConnection(id, connection) {
38
- return await prisma.Connection.update({
39
- where: { id },
40
- data: {
41
- clientIP: connection.clientIP,
42
- channelNames: connection.channelNames,
43
- data: connection.data,
44
- }
45
- })
46
- }
38
+ } else {
39
+ // new or unrecoverable session
40
+ console.log("connection", socket.id)
41
+ }
47
42
 
48
- async function deleteConnection(id) {
49
- try {
50
- await prisma.Connection.delete({ where: { id }})
51
- } catch(err) {
52
- // in case it would no longer exist
43
+ const clientIP = socket.handshake.address
44
+ socket.data = {
45
+ clientIP,
53
46
  }
54
- }
47
+ // app.log('verbose', `Client connected ${connectionId} from IP ${clientIP}`)
48
+ app.log('verbose', `Client connected ${socket.id} from IP ${clientIP}`)
55
49
 
56
- function getSocket(connectionId) {
57
- return cnx2Socket[connectionId]
58
- }
50
+ // emit 'connection' event for app (expressjs extends EventEmitter)
51
+ app.emit('connection', socket)
59
52
 
60
- function setSocket(connectionId, socket) {
61
- cnx2Socket[connectionId] = socket
62
- }
53
+ // send 'connected' event to client
54
+ // socket.emit('connected', connectionId)
55
+ socket.emit('connected', socket.id)
63
56
 
57
+ socket.on('disconnect', () => {
58
+ // app.log('verbose', `Client disconnected ${connectionId}`)
59
+ app.log('verbose', `Client disconnected ${socket.id}`)
60
+ })
64
61
 
65
- // logging function - a winston logger must be configured first
66
- app.log = (severity, message) => {
67
- const logger = app.get('logger')
68
- if (logger) logger.log(severity, message)
69
- }
62
+ /*
63
+ * Handle websocket client request
64
+ * Emit in return a 'client-response' message
65
+ */
66
+ socket.on('client-request', async ({ uid, name, action, args }) => {
67
+ const trimmedArgs = args ? JSON.stringify(args).slice(0, 300) : ''
68
+ app.log('verbose', `client-request ${uid} ${name} ${action} ${trimmedArgs}`)
69
+ if (name in services) {
70
+ const service = services[name]
71
+ try {
72
+ const serviceMethod = service['__' + action]
73
+ if (serviceMethod) {
74
+ const context = {
75
+ app,
76
+ caller: 'client',
77
+ transport: 'ws',
78
+ socket,
79
+ // connectionId,
80
+ serviceName: name,
81
+ methodName: action,
82
+ args,
83
+ }
70
84
 
71
- /*
72
- * create a service `name` based on Prisma table `entity`
73
- */
74
- function createDatabaseService(name, prismaOptions = { entity: name }) {
75
- // take all prisma methods on `entity` table
76
- const methods = prisma[prismaOptions.entity]
77
-
78
- const service = createService(name, methods)
79
-
80
- service.prisma = prisma
81
- service.entity = prismaOptions.entity
82
-
83
- app.log('info', `created service '${name}' over table '${prismaOptions.entity}'`)
84
- return service
85
- }
85
+ try {
86
+ // call method with context
87
+ const result = await serviceMethod(context, ...args)
88
+
89
+ const trimmedResult = result ? JSON.stringify(result).slice(0, 300) : ''
90
+ app.log('verbose', `client-response ${uid} ${trimmedResult}`)
91
+ socket.emit('client-response', {
92
+ uid,
93
+ result,
94
+ })
95
+ } catch(err) {
96
+ console.log('!!!!!!error', err.code, err)
97
+ app.log('verbose', err.stack)
98
+ socket.emit('client-response', {
99
+ uid,
100
+ error: {
101
+ code: err.code || 'unknown-error',
102
+ message: err.stack,
103
+ }
104
+ })
105
+ }
106
+ } else {
107
+ socket.emit('client-response', {
108
+ uid,
109
+ error: {
110
+ code: 'missing-method',
111
+ message: `there is no method named '${action}' for service '${name}'`,
112
+ }
113
+ })
114
+ }
115
+ } catch(err) {
116
+ console.log('err', err)
117
+ app.log('verbose', err.stack)
118
+ socket.emit('client-response', {
119
+ uid,
120
+ error: {
121
+ code: err.code || 'unknown-error',
122
+ message: err.stack,
123
+ }
124
+ })
125
+ }
126
+ } else {
127
+ socket.emit('client-response', {
128
+ uid,
129
+ error: {
130
+ code: 'missing-service',
131
+ message: `there is no service named '${name}'`,
132
+ }
133
+ })
134
+ }
135
+ })
136
+
137
+ })
86
138
 
87
139
  /*
88
140
  * create a service `name` with given `methods`
89
141
  */
90
142
  function createService(name, methods) {
91
- const service = { _name: name }
143
+ const service = { name }
92
144
 
93
145
  for (const methodName in methods) {
94
146
  const method = methods[methodName]
@@ -112,7 +164,7 @@ export function expressX(prisma, config) {
112
164
  }
113
165
 
114
166
  // call method
115
- const result = await method(...context.args)
167
+ const result = await method(...args)
116
168
  // put result into context
117
169
  context.result = result
118
170
 
@@ -124,29 +176,22 @@ export function expressX(prisma, config) {
124
176
  await hook(context)
125
177
  }
126
178
 
127
- // publish event (websocket transport)
128
- if (config.WS_TRANSPORT && service.publishFunction) {
179
+ // publish 'service-event' event associated to service/method
180
+ if (service.publishFunction) {
181
+ // collect channel names to socket is member of
129
182
  const channelNames = await service.publishFunction(context)
130
- app.log('verbose', `publish channels ${service._name} ${methodName} ${channelNames}`)
131
- const connections = await app.prisma.Connection.findMany({})
132
- for (const channelName of channelNames) {
133
- app.log('verbose', `service-event ${service._name} ${methodName} ${channelName}`)
134
- const connectionList = connections.filter(connection => {
135
- const channelNames = JSON.parse(connection.channelNames)
136
- return channelNames.includes(channelName)
137
- })
138
-
139
- for (const connection of connectionList) {
140
- const trimmedResult = result ? JSON.stringify(result).slice(0, 300) : ''
141
- app.log('verbose', `emit to ${connection.id} ${service._name} ${methodName} ${trimmedResult}`)
142
- const socket = getSocket(connection.id)
143
- // emit service event
144
- socket && socket.emit('service-event', {
145
- name: service._name,
146
- action: methodName,
147
- result,
148
- })
183
+ app.log('verbose', `publish channels ${service.name} ${methodName} ${channelNames}`)
184
+ // send event on all these channels
185
+ if (channelNames.length > 0) {
186
+ let sender = io.to(channelNames[0])
187
+ for (let i = 1; i < channelNames.length; i++) {
188
+ sender = sender.to(channelNames[i])
149
189
  }
190
+ sender.emit('service-event', {
191
+ name: service.name,
192
+ action: methodName,
193
+ result,
194
+ })
150
195
  }
151
196
  }
152
197
 
@@ -182,13 +227,6 @@ export function expressX(prisma, config) {
182
227
  return service
183
228
  }
184
229
 
185
- // `app.service(name)` starts here!
186
- function service(name) {
187
- // get service from `services` cache
188
- if (name in services) return services[name]
189
- app.log('error', `there is no service named '${name}'`, 'missing-service')
190
- }
191
-
192
230
  function configure(callback) {
193
231
  callback(app)
194
232
  }
@@ -198,296 +236,33 @@ export function expressX(prisma, config) {
198
236
  appHooks = hooks
199
237
  }
200
238
 
201
- /*
202
- * add an HTTP REST endpoint at `path`, based on `service`
203
- */
204
- async function addHttpRest(path, service) {
205
- const context = {
206
- app,
207
- caller: 'client',
208
- transport: 'http',
209
- serviceName: service._name,
210
-
211
- // params: { name: service._name }
212
- }
213
-
214
- // introspect schema and return a map: field name => prisma type
215
- function getTypesMap() {
216
- const dmmf = service.prisma._runtimeDataModel
217
- const fieldDescriptions = dmmf.models[service._name].fields
218
- return fieldDescriptions.reduce((accu, descr) => {
219
- accu[descr.name] = descr.type
220
- return accu
221
- }, {})
222
- }
223
-
224
-
225
- app.post(path, async (req, res) => {
226
- app.log('verbose', `http request POST ${req.url}`)
227
- context.req = req
228
- try {
229
- const value = await service.__create(context, { data: req.body })
230
- res.json(value)
231
- } catch(err) {
232
- app.log('error', err)
233
- res.status(500).send(err.toString())
234
- }
235
- })
236
-
237
- app.get(path, async (req, res) => {
238
- app.log('verbose', `http request GET ${req.url}`)
239
- context.req = req
240
- const query = { ...req.query }
241
- try {
242
- // the values in `req.query` are all strings, but Prisma need proper types
243
- // we need to introspect column types and do the proper transtyping
244
- for (const fieldName in query) {
245
- const typesDict = getTypesMap()
246
- const fieldType = typesDict[fieldName]
247
-
248
- if (fieldType === 'Int') {
249
- query[fieldName] = parseInt(query[fieldName])
250
- } else if (fieldType === 'Float') {
251
- query[fieldName] = parseFloat(query[fieldName])
252
- } else if (fieldType === 'Boolean') {
253
- query[fieldName] = (query[fieldName] === 't') ? true : false
254
- } else if (fieldType === 'String') {
255
- query[fieldName] = query[fieldName]
256
- } else {
257
- // ?
258
- query[fieldName] = query[fieldName]
259
- }
260
- }
261
- // call __findMany
262
- const values = await service.__findMany(context, {
263
- where: query,
264
- })
265
- res.json(values)
266
- } catch(err) {
267
- app.log('error', err)
268
- res.status(500).send(err.toString())
269
- }
270
- })
271
-
272
- app.get(`${path}/:id`, async (req, res) => {
273
- app.log('verbose', `http request GET ${req.url}`)
274
- context.req = req
275
- try {
276
- const value = await service.__findUnique(context, {
277
- where: {
278
- id: parseInt(req.params.id)
279
- }
280
- })
281
- res.json(value)
282
- } catch(err) {
283
- app.log('error', err)
284
- res.status(500).send(err.toString())
285
- }
286
- })
287
-
288
- app.patch(`${path}/:id`, async (req, res) => {
289
- app.log('verbose', `http request PATCH ${req.url}`)
290
- context.req = req
291
- try {
292
- const value = await service.__update(context, {
293
- where: {
294
- id: parseInt(req.params.id),
295
- },
296
- data: req.body,
297
- })
298
- res.json(value)
299
- } catch(err) {
300
- app.log('error', err)
301
- res.status(500).send(err.toString())
302
- }
303
- })
304
-
305
- app.delete(`${path}/:id`, async (req, res) => {
306
- app.log('verbose', `http request DELETE ${req.url}`)
307
- context.req = req
308
- try {
309
- const value = await service.__delete(context, {
310
- where: {
311
- id: parseInt(req.params.id)
312
- }
313
- })
314
- res.json(value)
315
- } catch(err) {
316
- app.log('error', err)
317
- res.status(500).send(err.toString())
318
- }
319
- })
320
-
321
- app.log('info', `added HTTP endpoints for service '${service._name}' at path '${path}'`)
239
+ // `app.service(name)` starts here!
240
+ function service(name) {
241
+ // get service from `services` cache
242
+ if (name in services) return services[name]
243
+ app.log('error', `there is no service named '${name}'`, 'missing-service')
322
244
  }
323
245
 
324
- /*
325
- * Create HTTP server
326
- */
327
- const httpServer = new http.Server(app)
328
-
329
- if (config.WS_TRANSPORT) {
330
- /*
331
- * Add websocket transport
332
- */
333
- const io = new Server(httpServer, {
334
- path: config.WS_PATH || '/socket.io/',
335
- })
336
-
337
-
338
- io.on('connection', async function(socket) {
339
- const clientIP = socket.request?.connection?.remoteAddress || 'unknown'
340
- const connection = await createConnection(clientIP)
341
- app.log('verbose', `Client connected ${connection.id} from IP ${clientIP}`)
342
-
343
- setSocket(connection.id, socket)
344
-
345
- // emit 'connection' event for app (expressjs extends EventEmitter)
346
- app.emit('connection', connection)
347
-
348
- // send 'connected' event to client
349
- socket.emit('connected', connection.id)
350
-
351
- socket.on('disconnect', () => {
352
- app.log('verbose', `Client disconnected ${connection.id}`)
353
-
354
- // remove connection record after expiration delay, if it still exists
355
- setTimeout(async () => {
356
- const connectionId = connection.id
357
- // check if connection still exists
358
- const cnx = await getConnection(connectionId)
359
- if (cnx) {
360
- app.log('verbose', `Delete connection ${connectionId}`)
361
- await deleteConnection(connectionId)
362
- }
363
- }, config.SESSION_EXPIRE_DELAY || 24*60*60000)
364
- })
365
-
366
-
367
- // handle connection data transfer caused by a disconnection/reconnection (page reload, network issue, etc.)
368
- socket.on('cnx-transfer', async ({ from, to }) => {
369
- app.log('verbose', `cnx-transfer from ${from} to ${to}`)
370
- // copy connection data from 'from' to 'to'
371
- const fromConnection = await getConnection(from)
372
- if (!fromConnection) return
373
- const toConnection = await cloneConnection(to, fromConnection)
374
- // associate socket to 'to'
375
- setSocket(to, socket)
376
- // delete 'from'
377
- await deleteConnection(from)
378
- // send acknowledge to client
379
- socket.emit('cnx-transfer-ack', toConnection)
380
- })
381
-
382
- /*
383
- * Handle websocket client request
384
- * Emit in return a 'client-response' message
385
- */
386
- socket.on('client-request', async ({ uid, name, action, args }) => {
387
- const trimmedArgs = args ? JSON.stringify(args).slice(0, 300) : ''
388
- app.log('verbose', `client-request ${connection.id} ${uid} ${name} ${action} ${trimmedArgs}`)
389
- if (name in services) {
390
- const service = services[name]
391
- try {
392
- const serviceMethod = service['__' + action]
393
- if (serviceMethod) {
394
- const context = {
395
- app,
396
- caller: 'client',
397
- transport: 'ws',
398
- connectionId: connection.id,
399
- serviceName: name,
400
- methodName: action,
401
- args,
402
-
403
- // params: { connectionId: connection.id, name, action, args },
404
- }
405
-
406
- try {
407
- const result = await serviceMethod(context, ...args)
408
-
409
- const trimmedResult = result ? JSON.stringify(result).slice(0, 300) : ''
410
- app.log('verbose', `client-response ${connection.id} ${uid} ${trimmedResult}`)
411
- socket.emit('client-response', {
412
- uid,
413
- result,
414
- })
415
- } catch(err) {
416
- console.log('!!!!!!error', err.code, err)
417
- app.log('verbose', err.stack)
418
- socket.emit('client-response', {
419
- uid,
420
- error: {
421
- code: err.code || 'unknown-error',
422
- message: err.stack,
423
- }
424
- })
425
- }
426
- } else {
427
- socket.emit('client-response', {
428
- uid,
429
- error: {
430
- code: 'missing-method',
431
- message: `there is no method named '${action}' for service '${name}'`,
432
- }
433
- })
434
- }
435
- } catch(error) {
436
- console.log('err', err)
437
- app.log('verbose', error.stack)
438
- socket.emit('client-response', {
439
- uid,
440
- error: {
441
- code: err.code || 'unknown-error',
442
- message: error.stack,
443
- }
444
- })
445
- }
446
- } else {
447
- socket.emit('client-response', {
448
- uid,
449
- error: {
450
- code: 'missing-service',
451
- message: `there is no service named '${name}'`,
452
- }
453
- })
454
- }
455
- })
456
- })
457
- }
458
-
459
- async function joinChannel(channelName, connection) {
460
- app.log('verbose', `Joining channel ${channelName} ${connection.id}`)
461
- const channelNames = JSON.parse(connection.channelNames)
462
- if (!channelNames.includes(channelName)) channelNames.push(channelName)
463
- await app.prisma.Connection.update({
464
- where: { id: connection.id },
465
- data: { channelNames: JSON.stringify(channelNames) },
466
- })
246
+ async function joinChannel(channelName, socket) {
247
+ app.log('verbose', `joining ${channelName}, ${JSON.stringify(socket.data)}`)
248
+ socket.join(channelName)
467
249
  }
468
250
 
469
- async function leaveChannel(channelName, connection) {
470
- app.log('verbose', `Leaving channel ${channelName} ${connection.id}`)
471
- const channelNames = JSON.parse(connection.channelNames).filter(name => name !== channelName)
472
- await app.prisma.Connection.update({
473
- where: { id },
474
- data: { channelNames: JSON.stringify(channelNames) },
475
- })
251
+ async function leaveChannel(channelName, socket) {
252
+ app.log('verbose', `leaving ${channelName}, ${JSON.stringify(socket.data)}`)
253
+ socket.leave(channelName)
476
254
  }
477
255
 
478
-
479
256
  // enhance `app` with objects and methods
480
257
  return Object.assign(app, {
481
- prisma,
482
- getSocket, setSocket,
483
- createDatabaseService,
258
+ io,
259
+ httpServer,
484
260
  createService,
485
261
  service,
486
262
  configure,
487
263
  hooks,
488
- addHttpRest,
489
- httpServer,
490
264
  joinChannel,
491
265
  leaveChannel,
492
266
  })
267
+
493
268
  }
package/src/context.mjs DELETED
@@ -1,62 +0,0 @@
1
-
2
- export async function getContextConnection(context) {
3
- const id = context.connectionId
4
- const connection = await context.app.prisma.Connection.findUnique({ where: { id }})
5
- return connection
6
- }
7
-
8
- export async function resetConnection(context) {
9
- const id = context.connectionId
10
- // by using updateMany, we cover the case where the connection `id` no longer exists
11
- await context.app.prisma.Connection.updateMany({
12
- where: { id },
13
- data: {
14
- data: '{}',
15
- channelNames: '[]',
16
- }
17
- })
18
- }
19
-
20
- export async function getConnectionDataItem(context, key) {
21
- const id = context.connectionId
22
- const connection = await context.app.prisma.Connection.findUnique({ where: { id }})
23
- const data = JSON.parse(connection.data)
24
- return data[key]
25
- }
26
-
27
- export async function setConnectionDataItem(context, key, value) {
28
- const id = context.connectionId
29
- const connection = await context.app.prisma.Connection.findUnique({ where: { id }})
30
- const data = JSON.parse(connection.data)
31
- data[key] = value
32
- await context.app.prisma.Connection.update({
33
- where: { id },
34
- data: {
35
- data: JSON.stringify(data)
36
- }
37
- })
38
- }
39
-
40
- export async function removeConnectionDataItem(context, key) {
41
- const id = context.connectionId
42
- const connection = await context.app.prisma.Connection.findUnique({ where: { id }})
43
- const data = JSON.parse(connection.data)
44
- delete data[key]
45
- await context.app.prisma.Connection.update({
46
- where: { id },
47
- data: {
48
- data: JSON.stringify(data)
49
- }
50
- })
51
- }
52
-
53
- export async function sendServiceEventToClient(context, name, action, result) {
54
- const id = context.connectionId
55
- const socket = context.app.cnx2Socket[id]
56
- socket.emit('service-event', {
57
- name,
58
- action,
59
- result,
60
- })
61
-
62
- }