@jcbuisson/express-x 1.8.4 → 2.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 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.4",
3
+ "version": "2.0.1",
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, resetConnection } 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
- import { hashPassword, protect, isNotExpired } from './common-hooks.mjs'
4
- import { getContextConnection, resetConnection, getConnectionDataItem, setConnectionDataItem, removeConnectionDataItem, sendServiceEventToClient } from './context.mjs'
3
+ import { addTimestamp, hashPassword, protect, isNotExpired } from './common-hooks.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,97 +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
- */
6
+ export function expressX(config) {
9
7
 
10
- // export function expressX(prisma, connectionAdapter, options) {
11
-
12
- export function expressX(prisma, config) {
8
+ const services = {}
9
+ let appHooks = []
13
10
 
14
11
  const app = express()
12
+ const httpServer = createServer(app)
15
13
 
16
14
  // so that config can be accessed anywhere with app.get('config')
17
15
  app.set('config', config)
18
16
 
19
- // websocket transport by default
20
- if (config.WS_TRANSPORT == undefined) config.WS_TRANSPORT = true
21
-
22
- const services = {}
23
- let appHooks = []
24
-
25
- 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
+ })
26
26
 
27
- async function createConnection(clientIP) {
28
- const connection = await prisma.Connection.create({
29
- data: {
30
- clientIP,
31
- }
32
- })
33
- 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)
34
31
  }
35
32
 
36
- function getConnection(id) {
37
- return prisma.Connection.findUnique({ where: { id }})
38
- }
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)
39
37
 
40
- async function cloneConnection(id, connection) {
41
- return await prisma.Connection.update({
42
- where: { id },
43
- data: {
44
- clientIP: connection.clientIP,
45
- channelNames: connection.channelNames,
46
- data: connection.data,
47
- }
48
- })
49
- }
38
+ } else {
39
+ // new or unrecoverable session
40
+ console.log("connection", socket.id)
41
+ }
50
42
 
51
- async function deleteConnection(id) {
52
- try {
53
- await prisma.Connection.delete({ where: { id }})
54
- } catch(err) {
55
- // in case it would no longer exist
43
+ const clientIP = socket.handshake.address
44
+ socket.data = {
45
+ clientIP,
56
46
  }
57
- }
47
+ // app.log('verbose', `Client connected ${connectionId} from IP ${clientIP}`)
48
+ app.log('verbose', `Client connected ${socket.id} from IP ${clientIP}`)
58
49
 
59
- function getSocket(connectionId) {
60
- return cnx2Socket[connectionId]
61
- }
50
+ // emit 'connection' event for app (expressjs extends EventEmitter)
51
+ app.emit('connection', socket)
62
52
 
63
- function setSocket(connectionId, socket) {
64
- cnx2Socket[connectionId] = socket
65
- }
53
+ // send 'connected' event to client
54
+ // socket.emit('connected', connectionId)
55
+ socket.emit('connected', socket.id)
66
56
 
57
+ socket.on('disconnect', () => {
58
+ // app.log('verbose', `Client disconnected ${connectionId}`)
59
+ app.log('verbose', `Client disconnected ${socket.id}`)
60
+ })
67
61
 
68
- // logging function - a winston logger must be configured first
69
- app.log = (severity, message) => {
70
- const logger = app.get('logger')
71
- if (logger) logger.log(severity, message)
72
- }
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
+ }
73
84
 
74
- /*
75
- * create a service `name` based on Prisma table `entity`
76
- */
77
- function createDatabaseService(name, prismaOptions = { entity: name }) {
78
- // take all prisma methods on `entity` table
79
- const methods = prisma[prismaOptions.entity]
80
-
81
- const service = createService(name, methods)
82
-
83
- service.prisma = prisma
84
- service.entity = prismaOptions.entity
85
-
86
- app.log('info', `created service '${name}' over table '${prismaOptions.entity}'`)
87
- return service
88
- }
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
+ })
89
138
 
90
139
  /*
91
140
  * create a service `name` with given `methods`
92
141
  */
93
142
  function createService(name, methods) {
94
- const service = { _name: name }
143
+ const service = { name }
95
144
 
96
145
  for (const methodName in methods) {
97
146
  const method = methods[methodName]
@@ -115,7 +164,7 @@ export function expressX(prisma, config) {
115
164
  }
116
165
 
117
166
  // call method
118
- const result = await method(...context.args)
167
+ const result = await method(...args)
119
168
  // put result into context
120
169
  context.result = result
121
170
 
@@ -127,29 +176,22 @@ export function expressX(prisma, config) {
127
176
  await hook(context)
128
177
  }
129
178
 
130
- // publish event (websocket transport)
131
- 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
132
182
  const channelNames = await service.publishFunction(context)
133
- app.log('verbose', `publish channels ${service._name} ${methodName} ${channelNames}`)
134
- const connections = await app.prisma.Connection.findMany({})
135
- for (const channelName of channelNames) {
136
- app.log('verbose', `service-event ${service._name} ${methodName} ${channelName}`)
137
- const connectionList = connections.filter(connection => {
138
- const channelNames = JSON.parse(connection.channelNames)
139
- return channelNames.includes(channelName)
140
- })
141
-
142
- for (const connection of connectionList) {
143
- const trimmedResult = result ? JSON.stringify(result).slice(0, 300) : ''
144
- app.log('verbose', `emit to ${connection.id} ${service._name} ${methodName} ${trimmedResult}`)
145
- const socket = getSocket(connection.id)
146
- // emit service event
147
- socket && socket.emit('service-event', {
148
- name: service._name,
149
- action: methodName,
150
- result,
151
- })
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])
152
189
  }
190
+ sender.emit('service-event', {
191
+ name: service.name,
192
+ action: methodName,
193
+ result,
194
+ })
153
195
  }
154
196
  }
155
197
 
@@ -185,13 +227,6 @@ export function expressX(prisma, config) {
185
227
  return service
186
228
  }
187
229
 
188
- // `app.service(name)` starts here!
189
- function service(name) {
190
- // get service from `services` cache
191
- if (name in services) return services[name]
192
- app.log('error', `there is no service named '${name}'`, 'missing-service')
193
- }
194
-
195
230
  function configure(callback) {
196
231
  callback(app)
197
232
  }
@@ -201,296 +236,33 @@ export function expressX(prisma, config) {
201
236
  appHooks = hooks
202
237
  }
203
238
 
204
- /*
205
- * add an HTTP REST endpoint at `path`, based on `service`
206
- */
207
- async function addHttpRest(path, service) {
208
- const context = {
209
- app,
210
- caller: 'client',
211
- transport: 'http',
212
- serviceName: service._name,
213
-
214
- // params: { name: service._name }
215
- }
216
-
217
- // introspect schema and return a map: field name => prisma type
218
- function getTypesMap() {
219
- const dmmf = service.prisma._runtimeDataModel
220
- const fieldDescriptions = dmmf.models[service._name].fields
221
- return fieldDescriptions.reduce((accu, descr) => {
222
- accu[descr.name] = descr.type
223
- return accu
224
- }, {})
225
- }
226
-
227
-
228
- app.post(path, async (req, res) => {
229
- app.log('verbose', `http request POST ${req.url}`)
230
- context.req = req
231
- try {
232
- const value = await service.__create(context, { data: req.body })
233
- res.json(value)
234
- } catch(err) {
235
- app.log('error', err)
236
- res.status(500).send(err.toString())
237
- }
238
- })
239
-
240
- app.get(path, async (req, res) => {
241
- app.log('verbose', `http request GET ${req.url}`)
242
- context.req = req
243
- const query = { ...req.query }
244
- try {
245
- // the values in `req.query` are all strings, but Prisma need proper types
246
- // we need to introspect column types and do the proper transtyping
247
- for (const fieldName in query) {
248
- const typesDict = getTypesMap()
249
- const fieldType = typesDict[fieldName]
250
-
251
- if (fieldType === 'Int') {
252
- query[fieldName] = parseInt(query[fieldName])
253
- } else if (fieldType === 'Float') {
254
- query[fieldName] = parseFloat(query[fieldName])
255
- } else if (fieldType === 'Boolean') {
256
- query[fieldName] = (query[fieldName] === 't') ? true : false
257
- } else if (fieldType === 'String') {
258
- query[fieldName] = query[fieldName]
259
- } else {
260
- // ?
261
- query[fieldName] = query[fieldName]
262
- }
263
- }
264
- // call __findMany
265
- const values = await service.__findMany(context, {
266
- where: query,
267
- })
268
- res.json(values)
269
- } catch(err) {
270
- app.log('error', err)
271
- res.status(500).send(err.toString())
272
- }
273
- })
274
-
275
- app.get(`${path}/:id`, async (req, res) => {
276
- app.log('verbose', `http request GET ${req.url}`)
277
- context.req = req
278
- try {
279
- const value = await service.__findUnique(context, {
280
- where: {
281
- id: parseInt(req.params.id)
282
- }
283
- })
284
- res.json(value)
285
- } catch(err) {
286
- app.log('error', err)
287
- res.status(500).send(err.toString())
288
- }
289
- })
290
-
291
- app.patch(`${path}/:id`, async (req, res) => {
292
- app.log('verbose', `http request PATCH ${req.url}`)
293
- context.req = req
294
- try {
295
- const value = await service.__update(context, {
296
- where: {
297
- id: parseInt(req.params.id),
298
- },
299
- data: req.body,
300
- })
301
- res.json(value)
302
- } catch(err) {
303
- app.log('error', err)
304
- res.status(500).send(err.toString())
305
- }
306
- })
307
-
308
- app.delete(`${path}/:id`, async (req, res) => {
309
- app.log('verbose', `http request DELETE ${req.url}`)
310
- context.req = req
311
- try {
312
- const value = await service.__delete(context, {
313
- where: {
314
- id: parseInt(req.params.id)
315
- }
316
- })
317
- res.json(value)
318
- } catch(err) {
319
- app.log('error', err)
320
- res.status(500).send(err.toString())
321
- }
322
- })
323
-
324
- 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')
325
244
  }
326
245
 
327
- /*
328
- * Create HTTP server
329
- */
330
- const httpServer = new http.Server(app)
331
-
332
- if (config.WS_TRANSPORT) {
333
- /*
334
- * Add websocket transport
335
- */
336
- const io = new Server(httpServer, {
337
- path: config.WS_PATH || '/socket.io/',
338
- })
339
-
340
-
341
- io.on('connection', async function(socket) {
342
- const clientIP = socket.request?.connection?.remoteAddress || 'unknown'
343
- const connection = await createConnection(clientIP)
344
- app.log('verbose', `Client connected ${connection.id} from IP ${clientIP}`)
345
-
346
- setSocket(connection.id, socket)
347
-
348
- // emit 'connection' event for app (expressjs extends EventEmitter)
349
- app.emit('connection', connection)
350
-
351
- // send 'connected' event to client
352
- socket.emit('connected', connection.id)
353
-
354
- socket.on('disconnect', () => {
355
- app.log('verbose', `Client disconnected ${connection.id}`)
356
-
357
- // remove connection record after expiration delay, if it still exists
358
- setTimeout(async () => {
359
- const connectionId = connection.id
360
- // check if connection still exists
361
- const cnx = await getConnection(connectionId)
362
- if (cnx) {
363
- app.log('verbose', `Delete connection ${connectionId}`)
364
- await deleteConnection(connectionId)
365
- }
366
- }, config.SESSION_EXPIRE_DELAY || 24*60*60000)
367
- })
368
-
369
-
370
- // handle connection data transfer caused by a disconnection/reconnection (page reload, network issue, etc.)
371
- socket.on('cnx-transfer', async ({ from, to }) => {
372
- app.log('verbose', `cnx-transfer from ${from} to ${to}`)
373
- // copy connection data from 'from' to 'to'
374
- const fromConnection = await getConnection(from)
375
- if (!fromConnection) return
376
- const toConnection = await cloneConnection(to, fromConnection)
377
- // associate socket to 'to'
378
- setSocket(to, socket)
379
- // delete 'from'
380
- await deleteConnection(from)
381
- // send acknowledge to client
382
- socket.emit('cnx-transfer-ack', toConnection)
383
- })
384
-
385
- /*
386
- * Handle websocket client request
387
- * Emit in return a 'client-response' message
388
- */
389
- socket.on('client-request', async ({ uid, name, action, args }) => {
390
- const trimmedArgs = args ? JSON.stringify(args).slice(0, 300) : ''
391
- app.log('verbose', `client-request ${connection.id} ${uid} ${name} ${action} ${trimmedArgs}`)
392
- if (name in services) {
393
- const service = services[name]
394
- try {
395
- const serviceMethod = service['__' + action]
396
- if (serviceMethod) {
397
- const context = {
398
- app,
399
- caller: 'client',
400
- transport: 'ws',
401
- connectionId: connection.id,
402
- serviceName: name,
403
- methodName: action,
404
- args,
405
-
406
- // params: { connectionId: connection.id, name, action, args },
407
- }
408
-
409
- try {
410
- const result = await serviceMethod(context, ...args)
411
-
412
- const trimmedResult = result ? JSON.stringify(result).slice(0, 300) : ''
413
- app.log('verbose', `client-response ${connection.id} ${uid} ${trimmedResult}`)
414
- socket.emit('client-response', {
415
- uid,
416
- result,
417
- })
418
- } catch(err) {
419
- console.log('!!!!!!error', err.code, err)
420
- app.log('verbose', err.stack)
421
- socket.emit('client-response', {
422
- uid,
423
- error: {
424
- code: err.code || 'unknown-error',
425
- message: err.stack,
426
- }
427
- })
428
- }
429
- } else {
430
- socket.emit('client-response', {
431
- uid,
432
- error: {
433
- code: 'missing-method',
434
- message: `there is no method named '${action}' for service '${name}'`,
435
- }
436
- })
437
- }
438
- } catch(error) {
439
- console.log('err', err)
440
- app.log('verbose', error.stack)
441
- socket.emit('client-response', {
442
- uid,
443
- error: {
444
- code: err.code || 'unknown-error',
445
- message: error.stack,
446
- }
447
- })
448
- }
449
- } else {
450
- socket.emit('client-response', {
451
- uid,
452
- error: {
453
- code: 'missing-service',
454
- message: `there is no service named '${name}'`,
455
- }
456
- })
457
- }
458
- })
459
- })
460
- }
461
-
462
- async function joinChannel(channelName, connection) {
463
- app.log('verbose', `Joining channel ${channelName} ${connection.id}`)
464
- const channelNames = JSON.parse(connection.channelNames)
465
- if (!channelNames.includes(channelName)) channelNames.push(channelName)
466
- await app.prisma.Connection.update({
467
- where: { id: connection.id },
468
- data: { channelNames: JSON.stringify(channelNames) },
469
- })
246
+ async function joinChannel(channelName, socket) {
247
+ app.log('verbose', `joining ${channelName}, ${JSON.stringify(socket.data)}`)
248
+ socket.join(channelName)
470
249
  }
471
250
 
472
- async function leaveChannel(channelName, connection) {
473
- app.log('verbose', `Leaving channel ${channelName} ${connection.id}`)
474
- const channelNames = JSON.parse(connection.channelNames).filter(name => name !== channelName)
475
- await app.prisma.Connection.update({
476
- where: { id },
477
- data: { channelNames: JSON.stringify(channelNames) },
478
- })
251
+ async function leaveChannel(channelName, socket) {
252
+ app.log('verbose', `leaving ${channelName}, ${JSON.stringify(socket.data)}`)
253
+ socket.leave(channelName)
479
254
  }
480
255
 
481
-
482
256
  // enhance `app` with objects and methods
483
257
  return Object.assign(app, {
484
- prisma,
485
- getSocket, setSocket,
486
- createDatabaseService,
258
+ io,
259
+ httpServer,
487
260
  createService,
488
261
  service,
489
262
  configure,
490
263
  hooks,
491
- addHttpRest,
492
- httpServer,
493
264
  joinChannel,
494
265
  leaveChannel,
495
266
  })
267
+
496
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
- }