@everneed/worker 2.1.0 → 2.2.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/index.js CHANGED
@@ -67,7 +67,7 @@ import pseudoEnv from "./pseudoenv.js"
67
67
  url: `redis://${pseudoEnv.process.env.REDIS_USERNAME}:${pseudoEnv.process.env.REDIS_PASSWORD}@${pseudoEnv.process.env.REDIS_HOST}:${pseudoEnv.process.env.REDIS_PORT}`
68
68
  } */)
69
69
  .on("error", err => console.log("Redis Client Error", err))
70
- // .connect()
70
+ .connect()
71
71
  }
72
72
  /* Socketio */
73
73
  let socketioInstance = ()=>{}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everneed/worker",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -3,12 +3,13 @@ import fs from "fs"
3
3
  import crypto from "crypto"
4
4
  import amqp from "amqplib/callback_api.js"
5
5
  import moment from "moment"
6
+ import EventEmitter from "events"
6
7
 
7
8
  import pseudoEnv from "@everneed/worker/pseudoenv.js"
8
9
  import { createHmac } from "crypto"
9
10
  import { redis } from "@everneed/worker"
10
11
  import { ResponseCode } from "@everneed/responsecode"
11
- import { pipe, switchPourStr, randomStr } from "@everneed/helper"
12
+ import { pipe, switchPourStr, randomStr, extractUserInfo } from "@everneed/helper"
12
13
 
13
14
  class VersionManager{
14
15
  #currentVer
@@ -181,9 +182,28 @@ class VersionManager{
181
182
  }
182
183
  const version = new VersionManager()
183
184
  version.init()
185
+ const bus = new EventEmitter()
184
186
 
185
- export default {
186
- config: function(object){
187
+ class RabbitMQ{
188
+ connection = null
189
+ channel = null
190
+
191
+ async init(){
192
+ await new Promise((resolve, reject)=>{
193
+ amqp.connect(pseudoEnv.process.env.RABBIT_HOST, (error0, connection)=>{
194
+ if(error0){ throw error0 }
195
+ this.connection = connection
196
+
197
+ this.connection.createChannel((error1, channel)=>{
198
+ if (error1){ throw error1 }
199
+ this.channel = channel
200
+
201
+ resolve("continue")
202
+ })
203
+ })
204
+ })
205
+ }
206
+ config(object){
187
207
  // config({
188
208
  // ENV: value,
189
209
  // ...
@@ -205,105 +225,105 @@ export default {
205
225
  }
206
226
  /* Env Add Process */
207
227
  pseudoEnv.config(object)
208
- },
209
- start: function(){
210
- /* RabbitMQ Engine */
211
- amqp.connect(pseudoEnv.process.env.RABBIT_HOST, async (error0, connection)=>{
212
- if(error0) throw error0
213
-
214
- /* Initiate header variables */
215
- const channel = connection.createChannel((error1, channel)=>{
216
- if (error1){ throw error1 }
217
-
218
- return channel
219
- })
220
- const rabbitmqGateway = fs.readdirSync(path.join(process.cwd(), "/src/mq-gateway/"))
221
- /* Generate consumer */
222
- // iterating every files in /src/mq-gateway
223
- // and its function to be built as rabbitmq
224
- // subscriber (consumer)
225
- for(const file of rabbitmqGateway){
226
- const moduleSpace = file.slice(0, -3)
227
- const main = await import(`file://${path.join(process.cwd(), `/src/mq-gateway/${moduleSpace}`)}.js`)
228
- // const main = await import(`#@/mq-gateway/${moduleSpace}.js`)
229
-
230
- for(const endpoint in main.default){
231
- /* Generate consumer */
232
- channel.assertQueue(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`, {
233
- durable: true
228
+ }
229
+ async start(){
230
+ const rabbitmqGateway = fs.readdirSync(path.join(process.cwd(), "/src/mq-gateway/"))
231
+ /* Generate consumer */
232
+ // iterating every files in /src/mq-gateway
233
+ // and its function to be built as rabbitmq
234
+ // subscriber (consumer)
235
+ for(const file of rabbitmqGateway){
236
+ const moduleSpace = file.slice(0, -3)
237
+ const main = await import(`file://${path.join(process.cwd(), `/src/mq-gateway/${moduleSpace}`)}.js`)
238
+ // const main = await import(`#@/mq-gateway/${moduleSpace}.js`)
239
+
240
+ for(const endpoint in main.default){
241
+ /* Generate consumer */
242
+ this.channel.assertQueue(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`, {
243
+ durable: false
244
+ })
245
+ this.channel.consume(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`, async (message)=>{
246
+ const mqHeader = message.properties.headers
247
+ const mqReq = await this.#decryptMessage({
248
+ message: message.content.toString(),
249
+ ...(mqHeader.auth && { token: mqHeader.auth.token }),
250
+ ...(mqHeader.auth && { userAgent: mqHeader.auth.userAgent })
234
251
  })
235
- channel.consume(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`, async (message)=>{
236
- const mqHeader = message.properties.headers
237
- const mqReq = await this.decryptMessage({
238
- message: message.content.toString(),
239
- ...(mqHeader.auth && { token: mqHeader.auth.token }),
240
- ...(mqHeader.auth && { userAgent: mqHeader.auth.userAgent })
252
+ const trueVersion = mqHeader.version ?
253
+ version.trueVersion({
254
+ endpoint: `/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`,
255
+ version: mqHeader.version
241
256
  })
242
- const trueVersion = mqHeader.version ?
243
- version.trueVersion({
244
- endpoint: `/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`,
245
- version: mqHeader.version
246
- })
247
- :
248
- version.latestVersion()
249
-
250
- const prep = {
251
- message: null
252
- }
257
+ :
258
+ version.latestVersion()
259
+ const prep = {
260
+ message: null
261
+ }
262
+ let timeoutCounter = false
263
+
264
+ // Start Timeout Timer
265
+ const timeout = setTimeout(()=>{ timeoutCounter = true }, main.default[endpoint].timeout || 10000)
253
266
 
254
- try{
255
- /* Active Endpoint Filter */
256
- if(!main.default[endpoint].active){
257
- const response = new ResponseCode()
258
- response.pushCode("INVALID_ENDPOINT")
259
- prep["message"] = await this.encryptMessage({
260
- message: response.result
261
- })
262
- throw "invalid_endpoint"
263
- }
264
- /* API enforce Auth */
265
- if(main.default[endpoint].authentication){
266
- if(!mqHeader.auth){
267
- const response = new ResponseCode()
268
- response.pushCode("NOT_AUTHENTICATED")
269
- prep["message"] = await this.encryptMessage({
270
- message: response.result
271
- })
272
- throw "not_authenticated"
273
- }
267
+ try{
268
+ /* Active Endpoint Filter */
269
+ if(!main.default[endpoint].active) throw "invalid_endpoint"
270
+ /* API enforce Auth */
271
+ if(main.default[endpoint].authentication){
272
+ if(!mqHeader.auth) throw "not_authenticated"
273
+ }
274
+ /* Main Process */
275
+ const result = await main.default[endpoint].function[`v${trueVersion}`](mqHeader.auth || false, mqReq.payload)
276
+ prep["message"] = await this.#encryptMessage({
277
+ message: result,
278
+ ...(mqHeader.auth && { token: mqHeader.auth.token }),
279
+ ...(mqHeader.auth && { userAgent: mqHeader.auth.userAgent })
280
+ })
281
+ /* Resolve */
282
+ if(timeoutCounter) throw "timeout"
283
+ else throw "finish"
284
+ }
285
+ catch(something){
286
+ /* Imbue response code according to throw */
287
+ if(something == "finish") clearTimeout(timeout)
288
+ if(something != "finish" && mqReq.trip == "returning"){
289
+ const response = new ResponseCode()
290
+ switch(something){
291
+ case "invalid_endpoint": response.pushCode("INVALID_ENDPOINT")
292
+ break;
293
+ case "not_authenticated": response.pushCode("NOT_AUTHENTICATED")
294
+ break;
295
+ case "timeout": response.pushCode("HTTP_TIMEOUT")
296
+ break;
297
+ default: response.pushCode("MQ_ERROR")
298
+ break;
274
299
  }
275
- /* Main Process */
276
- const result = await main.default[endpoint].function[`v${trueVersion}`](mqHeader.auth || false, mqReq.payload)
277
- prep["message"] = await this.encryptMessage({
278
- message: result,
300
+
301
+ prep["message"] = await this.#encryptMessage({
302
+ message: response.result,
279
303
  ...(mqHeader.auth && { token: mqHeader.auth.token }),
280
304
  ...(mqHeader.auth && { userAgent: mqHeader.auth.userAgent })
281
305
  })
282
-
283
- throw "finish"
284
- }
285
- catch(something){
286
- if(mqReq.trip == "returning"){
287
- channel.sendToQueue(message.properties.replyTo, Buffer.from(prep.message), {
288
- correlationId: message.properties.correlationId,
289
- headers:{
290
- ...(mqHeader.version && { version: mqHeader.version }),
291
- ...(mqHeader.auth && { auth: mqHeader.auth })
292
- },
293
- })
294
- channel.ack(message)
295
- }
296
- }
297
- },{
298
- noAck: false
299
- })
300
- }
306
+ }
307
+ /* Returning Trip */
308
+ if(mqReq.trip == "returning"){
309
+ this.channel.sendToQueue(message.properties.replyTo, Buffer.from(prep.message), {
310
+ headers:{
311
+ ...(mqHeader.version && { version: mqHeader.version }),
312
+ ...(mqHeader.auth && { auth: mqHeader.auth })
313
+ }
314
+ })
315
+ }
316
+ /* Ack Compose */
317
+ if(something == "finish") this.channel.ack(message)
318
+ else this.channel.nack(message, false, false)
319
+ }
320
+ },{
321
+ noAck: false
322
+ },(error, ok)=>{ if(error) throw error })
301
323
  }
302
-
303
- // connection.close()
304
- })
305
- },
306
- publish: function({topic, version, auth=null, trip="passby", data}){
324
+ }
325
+ }
326
+ publish({topic, version, auth=null, trip="passby", data}){
307
327
  /* Usage */
308
328
  // publish({
309
329
  // topic: <topic start at svc :String>,
@@ -321,117 +341,103 @@ export default {
321
341
 
322
342
  const response = new ResponseCode()
323
343
  return new Promise(async (resolve, reject)=>{
324
- /* RabbitMQ Engine */
325
- amqp.connect(pseudoEnv.process.env.RABBIT_HOST, async (error0, connection)=>{
326
- if(error0){ throw error0 }
327
-
328
- try{
329
- /* Creates connection & channel */
330
- const channel = connection.createChannel((error1, channel)=>{
331
- if (error1){ throw error1 }
332
- return channel
333
- })
334
-
335
- /* Build payload structure */
336
- // like topic, address, etc.
337
- const userName = auth ? this.getUserInfo({tokenBody: auth.token.split(".")[0]}).userName : `guest${randomStr({length: 12})}`
338
- const prep = {
339
- topic: `req/${topic}`,
340
- message:{
341
- trip: trip,
342
- senderAddress: `${createHmac("sha256", userName).digest("hex").slice(0, 12)}${moment().utc().format("HHmmssSSS")}`,
343
- payload: data
344
- }
344
+ try{
345
+ /* Build payload structure */
346
+ // like topic, address, etc.
347
+ const userName = auth ? extractUserInfo({token: auth.token}).userName : `guest${randomStr({length: 12})}`
348
+ const prep = {
349
+ topic: `req/${topic}`,
350
+ message:{
351
+ trip: trip,
352
+ payload: data
345
353
  }
346
- /* Validate Auth */
347
- if(auth){
348
- const validate = await this.publish({
349
- topic: "authenthor/session/svVerifyToken",
350
- trip: "returning",
351
- data:{
352
- token: auth.token,
353
- userAgent: auth.userAgent,
354
- noRefresh: true
355
- }
356
- })
354
+ }
355
+ let senderAddress
356
+ /* Validate Auth */
357
+ if(auth){
358
+ const validate = await this.publish({
359
+ topic: "authenthor/session/svVerifyToken",
360
+ trip: "returning",
361
+ data:{
362
+ token: auth.token,
363
+ userAgent: auth.userAgent,
364
+ noRefresh: true
365
+ }
366
+ })
357
367
 
358
- response.createNew(validate)
359
- if(response.checkError()) throw "abort"
360
- else response.reset()
368
+ if(validate.checkError()){
369
+ response.createNew(validate.result)
370
+ throw "abort"
361
371
  }
362
- /* Main publisher */
363
- const mqReq = await this.encryptMessage({
364
- message: prep.message,
365
- ...(auth && { token: auth.token }),
366
- ...(auth && { userAgent: auth.userAgent })
367
- })
368
-
369
- channel.assertQueue(prep.topic, { durable: true })
370
- channel.sendToQueue(prep.topic, Buffer.from(mqReq), {
371
- headers:{
372
- ...(version && { version: version }),
373
- ...(auth && { auth: auth })
374
- },
375
- ...(trip == "returning" && { correlationId: prep.message.senderAddress }),
376
- ...(trip == "returning" && { replyTo: `res/${topic}` }),
377
- })
378
-
379
- /* Resolvation According to Trip */
380
- if(trip == "returning"){
381
- /* Creates consumer for returning messages */
382
- channel.assertQueue(`res/${topic}`, { durable: true })
383
- channel.consume(`res/${topic}`, async (message)=>{
384
- const mqRes = await this.decryptMessage({
385
- message: message.content.toString(),
386
- ...(auth && { token: auth.token }),
387
- ...(auth && { userAgent: auth.userAgent })
388
- })
389
-
390
- if(message.properties.correlationId == prep.message.senderAddress){
391
- response.createNew(mqRes)
392
- channel.ack(message)
393
- setTimeout(()=>{
394
- connection.close()
395
- }, 500)
396
-
397
- resolve(response)
398
- }
399
- }, {
400
- noAck: false
372
+ }
373
+ /* Prepare Listener */
374
+ if(trip == "returning"){
375
+ /* Creates consumer for returning messages */
376
+ senderAddress = await new Promise((resolva, rejecta)=>{
377
+ this.channel.assertQueue("", { exclusive: true, autoDelete: true }, (error, ok)=>{
378
+ resolva(ok.queue)
379
+ })
380
+ })
381
+ /* Returning Message's Sub-Consumer */
382
+ this.channel.consume(senderAddress, async (message)=>{
383
+ const mqRes = await this.#decryptMessage({
384
+ message: message.content.toString(),
385
+ ...(auth && { token: auth.token }),
386
+ ...(auth && { userAgent: auth.userAgent })
401
387
  })
402
- }
403
- else if(trip == "passby"){
404
- response.pushCode("GENERAL_OK")
405
-
406
- setTimeout(()=>{
407
- connection.close()
408
- }, 500)
409
388
 
389
+ this.channel.ack(message)
390
+ response.createNew(mqRes)
391
+ this.channel.cancel(senderAddress)
410
392
  resolve(response)
411
- }
412
- }
413
- catch(something){
414
- response.createNew(response.result)
415
- if(something != "abort"){
416
- // err: other error
417
- response.pushCode("LOGIC_ERROR")
418
- response.pushTrace({code: "LOGIC_ERROR", trace: `Unexpected error on queue publisher`})
419
- }
420
-
421
- resolve(response)
393
+ }, {
394
+ noAck: false,
395
+ consumerTag: senderAddress
396
+ },(error, ok)=>{ if(error) throw error })
397
+ }
398
+ /* Main publisher */
399
+ const mqReq = await this.#encryptMessage({
400
+ message: prep.message,
401
+ ...(auth && { token: auth.token }),
402
+ ...(auth && { userAgent: auth.userAgent })
403
+ })
404
+
405
+ this.channel.assertQueue(prep.topic, {
406
+ durable: false
407
+ })
408
+ this.channel.sendToQueue(prep.topic, Buffer.from(mqReq), {
409
+ headers:{
410
+ ...(version && { version: version }),
411
+ ...(auth && { auth: auth })
412
+ },
413
+ ...(trip == "returning" && { replyTo: senderAddress })
414
+ })
415
+ /* Non-returning Trip Resolvation */
416
+ if(trip == "passby"){
417
+ response.pushCode("GENERAL_OK")
418
+
419
+ resolve(response)
420
+ }
421
+ }
422
+ catch(something){
423
+ response.createNew(response.result)
424
+ if(something != "abort"){
425
+ // err: other error
426
+ response.pushCode("LOGIC_ERROR")
427
+ response.pushTrace({code: "LOGIC_ERROR", trace: `Unexpected error on queue publisher`})
422
428
  }
423
- // connection.close()
424
- })
429
+
430
+ resolve(response)
431
+ }
425
432
  })
426
- },
427
- encryptMessage: function({message, token=null, userAgent=null}){
433
+ }
434
+ #encryptMessage({message, token=null, userAgent=null}){
428
435
  return new Promise(async (resolve, reject)=>{
429
436
  /* Active Auth Session */
430
437
  if(token && userAgent){
431
- const { userId } = this.getUserInfo({ tokenBody: token.split(".")[0] })
438
+ const { userId } = extractUserInfo({ token: token })
432
439
  const uaHash = crypto.hash("sha256", switchPourStr(userAgent, pseudoEnv.process.env.TOKEN_UA_SALT))
433
- await redis.connect().catch(error => {})
434
- const authKey = await redis.get(`auth:session:long:${userId}:${uaHash}`)
440
+ const authKey = await redis.get(`auth:session:long:${userId}:${uaHash}`)
435
441
  const hash = crypto.hash("sha256", switchPourStr(authKey || "kontol", pseudoEnv.process.env.RABBIT_MESSAGE_SALT))
436
442
  const key = hash.substring(0, 16)
437
443
  const iv = hash.substring(48)
@@ -449,15 +455,14 @@ export default {
449
455
  return resolve(encrypted)
450
456
  }
451
457
  })
452
- },
453
- decryptMessage: function({message, token=null, userAgent=null}){
458
+ }
459
+ #decryptMessage({message, token=null, userAgent=null}){
454
460
  return new Promise(async (resolve, reject)=>{
455
461
  /* Active Auth Session */
456
462
  if(token && userAgent){
457
- const { userId } = this.getUserInfo({ tokenBody: token.split(".")[0] })
463
+ const { userId } = extractUserInfo({ token: token })
458
464
  const uaHash = crypto.hash("sha256", switchPourStr(userAgent, pseudoEnv.process.env.TOKEN_UA_SALT))
459
- await redis.connect().catch(error => {})
460
- const authKey = await redis.get(`auth:session:long:${userId}:${uaHash}`)
465
+ const authKey = await redis.get(`auth:session:long:${userId}:${uaHash}`)
461
466
  const hash = crypto.hash("sha256", switchPourStr(authKey || "kontol", pseudoEnv.process.env.RABBIT_MESSAGE_SALT))
462
467
  const key = hash.substring(0, 16)
463
468
  const iv = hash.substring(48)
@@ -475,25 +480,12 @@ export default {
475
480
  return resolve(JSON.parse(decrypted))
476
481
  }
477
482
  })
478
- },
479
- getUserInfo: function({tokenBody}){
480
- return pipe(tokenBody)
481
- .then(aes=>{
482
- const decipher = crypto.createDecipheriv("aes-128-cbc", Buffer.from(pseudoEnv.process.env.TOKEN_KEY), Buffer.from(pseudoEnv.process.env.TOKEN_IV))
483
- let decrypted = decipher.update(aes, "base64", "utf-8")
484
- return decrypted += decipher.final("utf-8")
485
- })
486
- .then(json=> JSON.parse(json))
487
- .then(circular=> circular.user)
488
- .result // { userId, userName, displayName }
489
- },
490
- update: function(){
483
+ }
484
+ update(){
491
485
  version.update()
492
- },
493
- apiVer: function(){
494
- return version.deprecatedVersion()+1
495
- },
496
- latestVer: function(){
497
- return version.latestVersion()
498
486
  }
499
- }
487
+ }
488
+
489
+ const instance = new RabbitMQ()
490
+ await instance.init()
491
+ export default instance