@everneed/worker 1.1.0 → 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/index.js CHANGED
@@ -89,6 +89,6 @@ import pseudoEnv from "./pseudoenv.js"
89
89
 
90
90
  export { rabbitmq } // preserve seperate-dir for example
91
91
  export { postgresInstance as postgres }
92
- // export { mongooseInstance as mongoose }
92
+ export { mongooseInstance as mongoose }
93
93
  export { redisInstance as redis }
94
94
  export { socketioInstance as socketio }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everneed/worker",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -1,26 +1,212 @@
1
1
  import path from "path"
2
2
  import fs from "fs"
3
+ import crypto from "crypto"
3
4
  import amqp from "amqplib/callback_api.js"
4
5
  import moment from "moment"
5
6
 
6
7
  import pseudoEnv from "@everneed/worker/pseudoenv.js"
7
8
  import { createHmac } from "crypto"
9
+ import { redis } from "@everneed/worker"
10
+ import { ResponseCode } from "@everneed/responsecode"
11
+ import { pipe, switchPourStr, randomStr } from "@everneed/helper"
12
+
13
+ class VersionManager{
14
+ #currentVer
15
+ #deprecated
16
+ #configPath = null
17
+ #endpointMap
18
+
19
+ // 🗑️
20
+ #existingBucket = []
21
+ #loadedBucket = []
22
+
23
+ constructor(){
24
+ this.init()
25
+ this.#load()
26
+ }
27
+ init(){
28
+ this.#load()
29
+ }
30
+ config(configPath){
31
+ this.#configPath = path.join("../../..", configPath)
32
+
33
+ /* Re-init */
34
+ this.init()
35
+ }
36
+ #clean(){
37
+ this.#currentVer = null
38
+ this.#configPath = null
39
+ this.#endpointMap = {}
40
+ this.#existingBucket = []
41
+ this.#loadedBucket = []
42
+ }
43
+ #load(){
44
+ // json into endpointMaps
45
+ let json
46
+ if(!this.#configPath){
47
+ try{
48
+ // json = require("../../config/versionmap.json")
49
+ json = fs.readFileSync(path.join(process.cwd(), "/config/versionmap.json"))
50
+ }
51
+ catch(err){
52
+ json = `{"/endpoint/one":{"latest": 1, "map":{"1":1}}}`
53
+ }
54
+ }
55
+ else{
56
+ json = fs.readFileSync(path.resolve(this.#configPath))
57
+ const fs = require("fs")
58
+ }
59
+
60
+ const config = pipe(json)
61
+ .then(json=> JSON.parse(json))
62
+ .result
63
+ this.#endpointMap = config.endpoint
64
+ this.#currentVer = config.apiVer
65
+ this.#deprecated = config.deprecatedVer
66
+ }
67
+ async update(){
68
+ // Define new version
69
+ const newVer = this.#currentVer+1
70
+ // Load all existing running API and stage them
71
+ // in existingBucket
72
+ const files = fs.readdirSync(`./src/mq-gateway`)
73
+ for(const file of files){
74
+ const module = file.replace(/.js|.ts/g, ``).toLowerCase()
75
+ const functs = await import(`file://${path.join(process.cwd(), `/src/mq-gateway/${file}`)}`)
76
+
77
+ for(const funct in functs.default){
78
+ for(const ver in functs.default[funct].function){
79
+ this.#existingBucket.push(`/${pseudoEnv.process.env.SVC_NAME}/${module}/${funct}/${ver}`)
80
+ }
81
+ }
82
+ }
83
+
84
+ // Prep irregular changes and unchanges 🔪🌽🍍🥕
85
+ // into more consumable iterable form
86
+ // will output the elements from existing bucket that are not in the loaded bucket
87
+ const addition = this.#existingBucket.filter(x => !this.#loadedBucket.includes(x))
88
+ const updation = pipe(addition)
89
+ .then(addition=>{
90
+ return {
91
+ addition: addition.map(fullEndpoint=> fullEndpoint.split(/\/v([0-9]+)$/, 2)[0]),
92
+ all: this.#existingBucket.map(fullEndpoint=> fullEndpoint.split(/\/v([0-9]+)$/, 2)[0])
93
+ }
94
+ })
95
+ .then(({addition, all})=> new Set(all).difference(new Set(addition)))
96
+ .then(set=> [...set])
97
+ .result
98
+
99
+ // Process to then stash result to head variable 🥣
100
+ for(const endpoint of addition){
101
+ // "/endpoint/one/v1" -> [/endpoint/one, 1]
102
+ const [pureEndpoint, versionInt] = endpoint.split(/\/v([0-9]+)$/, 2)
103
+
104
+ // new endpoint
105
+ if(!this.#endpointMap[pureEndpoint]){
106
+ this.#endpointMap[pureEndpoint] = {
107
+ latest: 1,
108
+ map: {}
109
+ }
110
+
111
+ this.#endpointMap[pureEndpoint]["map"][`v${newVer}`] = 1
112
+ }
113
+
114
+ this.#endpointMap[pureEndpoint].latest = Number(versionInt)
115
+ this.#endpointMap[pureEndpoint].map[`v${newVer}`] = Number(versionInt)
116
+ // /pure/endpoint:{
117
+ // latest: (2), <--- updated
118
+ // map:{
119
+ // v1: 1,
120
+ // v2: 1,
121
+ // v3: 2 <-- addition
122
+ // }
123
+ // }
124
+ }
125
+ for(const endpoint in updation){
126
+ this.#endpointMap[endpoint].map[`v${newVer}`] = this.#endpointMap[endpoint].latest
127
+ // /pure/endpoint:{
128
+ // latest: 1,
129
+ // map:{
130
+ // v1: 1,
131
+ // v2: 1,
132
+ // v3: 1 <-- updated
133
+ // }
134
+ // }
135
+ }
136
+
137
+ // Save head variable into config file (.json)
138
+ const json = JSON.stringify({
139
+ apiVer: newVer,
140
+ lastUpdate: moment().utc().format(),
141
+ endpoint: this.#endpointMap
142
+ }, null, 4)
143
+ if(!this.#configPath){
144
+ try{
145
+ fs.writeFileSync(path.join(process.cwd(), "/config/versionmap.json"), json)
146
+ }
147
+ catch(err){
148
+ console.log(err)
149
+ }
150
+ }
151
+ else{
152
+ fs.writeFileSync(path.resolve(__dirname, this.#configPath), json)
153
+ }
154
+
155
+ // re-load
156
+ this.#clean()
157
+ this.#load()
158
+ }
159
+ allVersion(endpoint){
160
+ // returns array of available version map
161
+ // in respecting order (index 0 should be null)
162
+ // Array due to be iterated nature
163
+
164
+ const result = [null]
165
+
166
+ for(const majorVer in this.#endpointMap[endpoint].map){
167
+ result[majorVer] = this.#endpointMap[endpoint].map[majorVer] // major = individual
168
+ }
169
+
170
+ return result // [null, individual ver, individual ver]
171
+ }
172
+ trueVersion({endpoint, version}){
173
+ return this.#endpointMap[endpoint].map[`v${version}`]
174
+ }
175
+ latestVersion(){
176
+ return this.#currentVer
177
+ }
178
+ deprecatedVersion(){
179
+ return this.#deprecated
180
+ }
181
+ }
182
+ const version = new VersionManager()
183
+ version.init()
8
184
 
9
185
  export default {
10
- config: (object)=>{
186
+ config: function(object){
11
187
  // config({
12
188
  // ENV: value,
13
189
  // ...
14
190
  // })
15
191
  /* Param Validation */
16
- const allowed = new Set(["SVC_NAME", "RABBIT_HOST"])
192
+ const allowed = new Set([
193
+ "SVC_NAME",
194
+ "SVC_DISPLAY",
195
+ "RABBIT_HOST",
196
+ "RABBIT_MESSAGE_KEY",
197
+ "RABBIT_MESSAGE_IV",
198
+ "RABBIT_MESSAGE_SALT",
199
+ "TOKEN_UA_SALT",
200
+ "TOKEN_KEY",
201
+ "TOKEN_IV"
202
+ ])
17
203
  if(new Set(Object.keys(object)).difference(allowed).size){
18
204
  throw `illegal key on rabbitmq.config(): Only ${Array.from(allowed).join(", ")} are allowed`
19
205
  }
20
206
  /* Env Add Process */
21
207
  pseudoEnv.config(object)
22
208
  },
23
- start: ()=>{
209
+ start: function(){
24
210
  /* RabbitMQ Engine */
25
211
  amqp.connect(pseudoEnv.process.env.RABBIT_HOST, async (error0, connection)=>{
26
212
  if(error0) throw error0
@@ -41,24 +227,106 @@ export default {
41
227
  const main = await import(`file://${path.join(process.cwd(), `/src/mq-gateway/${moduleSpace}`)}.js`)
42
228
  // const main = await import(`#@/mq-gateway/${moduleSpace}.js`)
43
229
 
44
- for(const functName in main.default){
230
+ for(const endpoint in main.default){
45
231
  /* Generate consumer */
46
- channel.assertQueue(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${functName}`, {
232
+ channel.assertQueue(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`, {
47
233
  durable: true
48
234
  })
49
- channel.consume(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${functName}`, async (message)=>{
50
- const mqReq = pipe(message)
51
- .then(message=> JSON.parse(message.content))
52
- .then(mqReq=> nullCleanser(mqReq))
53
- .result
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
+ mode: mqHeader.mode,
239
+ message: message.content.toString(),
240
+ ...(mqHeader.auth && { token: mqHeader.auth.token }),
241
+ ...(mqHeader.auth && { userAgent: mqHeader.auth.userAgent })
242
+ })
243
+ const trueVersion = mqHeader.version ?
244
+ version.trueVersion({
245
+ endpoint: `/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${endpoint}`,
246
+ version: mqHeader.version
247
+ })
248
+ :
249
+ version.latestVersion()
54
250
 
55
- const result = await main.default[functName](mqReq.payload)
251
+ const prep = {
252
+ message: null
253
+ }
56
254
 
57
- if(mqReq.trip == "returning"){
58
- channel.sendToQueue(message.properties.replyTo, Buffer.from(JSON.stringify(result)), {
59
- correlationId: message.properties.correlationId
60
- })
61
- channel.ack(message)
255
+ try{
256
+ /* Active Endpoint Filter */
257
+ if(!main.default[endpoint].active){
258
+ const response = new ResponseCode()
259
+ response.pushCode("INVALID_ENDPOINT")
260
+ prep["message"] = await this.encryptMessage({
261
+ mode: mqHeader.mode,
262
+ message: response.result
263
+ })
264
+ throw "invalid_endpoint"
265
+ }
266
+ /* API enforce Auth */
267
+ if(main.default[endpoint].authentication){
268
+ if(!mqHeader.auth && mqHeader.mode != "auth"){
269
+ const response = new ResponseCode()
270
+ response.pushCode("NOT_AUTHENTICATED")
271
+ prep["message"] = await this.encryptMessage({
272
+ mode: mqHeader.mode,
273
+ message: response.result
274
+ })
275
+ throw "not_authenticated"
276
+ }
277
+ }
278
+
279
+ /* With Auth */
280
+ if(mqHeader.auth && mqHeader.mode == "auth"){
281
+ const { token, userAgent } = mqHeader.auth
282
+ const auth = await this.verifyToken({
283
+ token: token,
284
+ userAgent: userAgent
285
+ })
286
+
287
+ if(auth.checkError()){ // NOT_AUTHENTICATED || SESSION_EXPIRED
288
+ prep["message"] = await this.encryptMessage({
289
+ mode: mqHeader.mode,
290
+ message: auth.result
291
+ })
292
+ }
293
+ else{ // AUTHENTICATED || REFRESH_SESSION
294
+ const result = await main.default[endpoint].function[`v${trueVersion}`](mqHeader.auth, mqReq.payload)
295
+ const response = new ResponseCode(result)
296
+ if(auth.hasCode("REFRESH_SESSION")) response.pushRefresh(auth.result.data)
297
+ prep["message"] = await this.encryptMessage({
298
+ mode: mqHeader.mode,
299
+ message: response.result,
300
+ token: token,
301
+ userAgent: userAgent
302
+ })
303
+ }
304
+ }
305
+ /* Without Auth */
306
+ else{
307
+ const result = await main.default[endpoint].function[`v${trueVersion}`](false, mqReq.payload)
308
+ prep["message"] = await this.encryptMessage({
309
+ mode: mqHeader.mode,
310
+ message: result,
311
+ ...(mqHeader.auth && { token: mqHeader.auth.token }),
312
+ ...(mqHeader.auth && { userAgent: mqHeader.auth.userAgent })
313
+ })
314
+ }
315
+
316
+ throw "finish"
317
+ }
318
+ catch(something){
319
+ if(mqReq.trip == "returning"){
320
+ channel.sendToQueue(message.properties.replyTo, Buffer.from(prep.message), {
321
+ correlationId: message.properties.correlationId,
322
+ headers:{
323
+ mode: mqHeader.mode,
324
+ ...(mqHeader.version && { version: mqHeader.version }),
325
+ ...(mqHeader.auth && { auth: mqHeader.auth })
326
+ },
327
+ })
328
+ channel.ack(message)
329
+ }
62
330
  }
63
331
  },{
64
332
  noAck: false
@@ -69,91 +337,283 @@ export default {
69
337
  // connection.close()
70
338
  })
71
339
  },
72
- publish: ({topic, userName, trip = "passby", data})=>{
340
+ publish: function({topic, version, mode="auth", auth=null, trip="passby", data}){
73
341
  /* Usage */
74
342
  // publish({
75
343
  // topic: <topic start at svc :String>,
76
- // userName: <userName or guest :String>,
344
+ // mode: <"auth"|"no auth" :String>
345
+ // auth:{
346
+ // token: <auth token :String>,
347
+ // userAgent: <ua string :String>,
348
+ // }
77
349
  // trip: <"returning"|"passby" :String>,
78
350
  // data: <message :String>
79
351
  // })
80
352
 
81
353
  // topic: <req|res>/<short sha username + random string>/<svc>/<endpoint>
82
- // ex: req/cd6f01UjkB7E/rolling-tempat-duduk/class/get-all
83
- // ex: res/cd6f01UjkB7E/rolling-tempat-duduk/class/get-all
354
+ // ex: req/cd6f01UjkB7E/rolling-tempat-duduk/class/getAll
355
+ // ex: res/cd6f01UjkB7E/rolling-tempat-duduk/class/getAll
84
356
 
85
357
  return new Promise(async (resolve, reject)=>{
86
358
  /* RabbitMQ Engine */
87
- amqp.connect(pseudoEnv.process.env.RABBIT_HOST, (error0, connection)=>{
359
+ amqp.connect(pseudoEnv.process.env.RABBIT_HOST, async (error0, connection)=>{
88
360
  if(error0){ throw error0 }
89
361
 
90
362
  /* Creates connection & channel */
91
- const channel = connection.createChannel((error1, channel)=>{
92
- if (error1){ throw error1 }
93
-
94
- return channel
95
- })
363
+ const channel = connection.createChannel((error1, channel)=>{
364
+ if (error1){ throw error1 }
365
+ return channel
366
+ })
96
367
 
97
368
  /* Build payload structure */
98
369
  // like topic, address, etc.
99
- const prep = {
100
- topic: `req/${topic}`,
101
- message:{
102
- trip: trip,
103
- senderAddress: `${createHmac("sha256", userName).digest("hex").slice(0, 6)}${moment().utc().format("HHmmssSSS")}`,
104
- payload: data
105
- }
106
- }
107
-
108
- if(trip == "returning"){
109
- /* Reshape topic into response shape */
110
- let resTopic = `res/${topic}`
111
-
112
- /* Creates consumer for returning messages */
113
- channel.assertQueue(resTopic, {
114
- durable: true
115
- })
116
- channel.consume(resTopic, (message)=>{
117
- const mqRes = JSON.parse(message.content.toString())
370
+ const userName = auth ? this.getUserInfo({tokenBody: auth.token.split(".")[0]}).userName : `guest${randomStr({length: 12})}`
371
+ const prep = {
372
+ topic: `req/${topic}`,
373
+ message:{
374
+ trip: trip,
375
+ senderAddress: `${createHmac("sha256", userName).digest("hex").slice(0, 12)}${moment().utc().format("HHmmssSSS")}`,
376
+ payload: data
377
+ }
378
+ }
379
+ /* Main publisher */
380
+ const mqReq = await this.encryptMessage({
381
+ mode: mode,
382
+ message: prep.message,
383
+ ...(auth && { token: auth.token }),
384
+ ...(auth && { userAgent: auth.userAgent })
385
+ })
386
+
387
+ channel.assertQueue(prep.topic, { durable: true })
388
+ channel.sendToQueue(prep.topic, Buffer.from(mqReq), {
389
+ headers:{
390
+ ...(version && { version: version }),
391
+ mode: mode,
392
+ ...(auth && { auth: auth })
393
+ },
394
+ ...(trip == "returning" && { correlationId: prep.message.senderAddress }),
395
+ ...(trip == "returning" && { replyTo: `res/${topic}` }),
396
+ })
397
+
398
+ /* Resolvation According to Trip */
399
+ if(trip == "returning"){
400
+ /* Creates consumer for returning messages */
401
+ channel.assertQueue(`res/${topic}`, { durable: true })
402
+ channel.consume(`res/${topic}`, async (message)=>{
403
+ /* Differentiate normal flow with refresh session flow */
404
+ const sessionRefresh = ()=>{
405
+ const response = new ResponseCode(message.content.toString())
406
+ if(response.hasCode("REFRESH_SESSION")) return true
407
+ else return false
408
+ }
409
+ const mqRes = await this.decryptMessage({
410
+ mode: mode,
411
+ message: message.content.toString(),
412
+ ...((auth && !sessionRefresh()) && { token: auth.token }),
413
+ ...((auth && sessionRefresh()) && { token: message.content.toString().data.token }),
414
+ ...(auth && { userAgent: auth.userAgent })
415
+ })
118
416
 
119
- if(message.properties.correlationId == prep.message.senderAddress){
120
- resolve(mqRes)
121
- channel.ack(message)
417
+ if(message.properties.correlationId == prep.message.senderAddress){
418
+ resolve(mqRes)
419
+ channel.ack(message)
420
+ setTimeout(()=>{
421
+ connection.close()
422
+ }, 500)
423
+ }
424
+ }, {
425
+ noAck: false
426
+ })
427
+ }
428
+ else if(trip == "passby"){
429
+ resolve("sent")
430
+
122
431
  setTimeout(()=>{
123
432
  connection.close()
124
433
  }, 500)
125
434
  }
126
- }, {
127
- noAck: false
128
- })
129
-
130
- /* Main publisher (with correlation id) */
131
- channel.assertQueue(prep.topic,{
132
- durable: true
133
- })
134
- channel.sendToQueue(prep.topic, Buffer.from(JSON.stringify(prep.message)), {
135
- correlationId: prep.message.senderAddress,
136
- replyTo: resTopic
137
- })
138
- }
139
- else if(trip == "passby"){
140
- /* Main publisher */
141
- // pushes without senderAddress
142
- prep.message["senderAddress"] = null
143
-
144
- channel.assertQueue(prep.topic,{
145
- durable: true
146
- })
147
- channel.sendToQueue(prep.topic, Buffer.from(JSON.stringify(prep.message)))
148
- resolve("sent")
149
-
150
- setTimeout(()=>{
151
- connection.close()
152
- }, 500)
153
- }
154
435
 
155
436
  // connection.close()
156
437
  })
157
438
  })
158
439
  },
440
+ encryptMessage: function({mode="auth", message, token=null, userAgent=null}){
441
+ return new Promise(async (resolve, reject)=>{
442
+ if(mode == "auth"){
443
+ /* Active Auth Session */
444
+ if(token && userAgent){
445
+ const { userId } = this.getUserInfo({ tokenBody: token.split(".")[0] })
446
+ const uaHash = crypto.hash("sha256", switchPourStr(userAgent, pseudoEnv.process.env.TOKEN_UA_SALT))
447
+ await redis.connect().catch(error => {})
448
+ const authKey = await redis.get(`auth:session:long:${userId}:${uaHash}`)
449
+ const hash = crypto.hash("sha256", switchPourStr(authKey, pseudoEnv.process.env.RABBIT_MESSAGE_SALT))
450
+ const key = hash.substring(0, 16)
451
+ const iv = hash.substring(48)
452
+
453
+ const cipher = crypto.createCipheriv("aes-128-cbc", Buffer.from(key), Buffer.from(iv))
454
+ let encrypted = cipher.update(JSON.stringify(message), "utf-8", "base64")
455
+ encrypted += cipher.final("base64")
456
+ return resolve(encrypted)
457
+ }
458
+ /* No Auth */
459
+ else{
460
+ const cipher = crypto.createCipheriv("aes-128-cbc", Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_KEY), Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_IV))
461
+ let encrypted = cipher.update(JSON.stringify(message), "utf-8", "base64")
462
+ encrypted += cipher.final("base64")
463
+ return resolve(encrypted)
464
+ }
465
+ }
466
+ else if(mode == "no auth"){
467
+ /* Unchecked Token (Illegal/Refresh) */
468
+ if(token){
469
+ const {key, iv} = pipe(token)
470
+ .then(token=> token.split(".")[0])
471
+ .then(tokenBody=> this.getUserInfo({tokenBody: tokenBody}))
472
+ .then(({userName, userId, displayName})=>{
473
+ return switchPourStr(
474
+ userName,
475
+ pseudoEnv.process.env.RABBIT_MESSAGE_KEY,
476
+ userId,
477
+ pseudoEnv.process.env.RABBIT_MESSAGE_IV,
478
+ displayName,
479
+ )
480
+ })
481
+ .then(mixWord=> crypto.hash("sha256", mixWord))
482
+ .then(hash=>{
483
+ return {
484
+ key: hash.substring(0, 16),
485
+ iv: hash.substring(48)
486
+ }
487
+ })
488
+ .result
489
+
490
+ const cipher = crypto.createCipheriv("aes-128-cbc", Buffer.from(key), Buffer.from(iv))
491
+ let encrypted = cipher.update(JSON.stringify(message), "utf-8", "base64")
492
+ encrypted += cipher.final("base64")
493
+ return resolve(encrypted)
494
+ }
495
+ /* No Auth */
496
+ else{
497
+ const cipher = crypto.createCipheriv("aes-128-cbc", Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_KEY), Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_IV))
498
+ let encrypted = cipher.update(JSON.stringify(message), "utf-8", "base64")
499
+ encrypted += cipher.final("base64")
500
+ return resolve(encrypted)
501
+ }
502
+ }
503
+ })
504
+ },
505
+ decryptMessage: function({mode="auth", message, token=null, userAgent=null}){
506
+ return new Promise(async (resolve, reject)=>{
507
+ if(mode == "auth"){
508
+ /* Active Auth Session */
509
+ if(token && userAgent){
510
+ const { userId } = this.getUserInfo({ tokenBody: token.split(".")[0] })
511
+ const uaHash = crypto.hash("sha256", switchPourStr(userAgent, pseudoEnv.process.env.TOKEN_UA_SALT))
512
+ await redis.connect().catch(error => {})
513
+ const authKey = await redis.get(`auth:session:long:${userId}:${uaHash}`)
514
+ const hash = crypto.hash("sha256", switchPourStr(authKey, pseudoEnv.process.env.RABBIT_MESSAGE_SALT))
515
+ const key = hash.substring(0, 16)
516
+ const iv = hash.substring(48)
517
+
518
+ const decipher = crypto.createDecipheriv("aes-128-cbc", Buffer.from(key), Buffer.from(iv))
519
+ let decrypted = decipher.update(message, "base64", "utf-8")
520
+ decrypted += decipher.final("utf-8")
521
+ return resolve(JSON.parse(decrypted))
522
+ }
523
+ /* No Auth */
524
+ else{
525
+ const decipher = crypto.createDecipheriv("aes-128-cbc", Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_KEY), Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_IV))
526
+ let decrypted = decipher.update(message, "base64", "utf-8")
527
+ decrypted += decipher.final("utf-8")
528
+ return resolve(JSON.parse(decrypted))
529
+ }
530
+ }
531
+ else if(mode == "no auth"){
532
+ /* Unchecked Token (Illegal/Refresh) */
533
+ if(token){
534
+ const {key, iv} = pipe(token)
535
+ .then(token=> token.split(".")[0])
536
+ .then(tokenBody=> this.getUserInfo({tokenBody: tokenBody}))
537
+ .then(({userName, userId, displayName})=>{
538
+ return switchPourStr(
539
+ userName,
540
+ pseudoEnv.process.env.RABBIT_MESSAGE_KEY,
541
+ userId,
542
+ pseudoEnv.process.env.RABBIT_MESSAGE_IV,
543
+ displayName,
544
+ )
545
+ })
546
+ .then(mixWord=> crypto.hash("sha256", mixWord))
547
+ .then(hash=>{
548
+ return {
549
+ key: hash.substring(0, 16),
550
+ iv: hash.substring(48)
551
+ }
552
+ })
553
+ .result
554
+
555
+ const decipher = crypto.createDecipheriv("aes-128-cbc", Buffer.from(key), Buffer.from(iv))
556
+ let decrypted = decipher.update(message, "base64", "utf-8")
557
+ decrypted += decipher.final("utf-8")
558
+ return resolve(JSON.parse(decrypted))
559
+ }
560
+ /* No Auth */
561
+ else{
562
+ const decipher = crypto.createDecipheriv("aes-128-cbc", Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_KEY), Buffer.from(pseudoEnv.process.env.RABBIT_MESSAGE_IV))
563
+ let decrypted = decipher.update(message, "base64", "utf-8")
564
+ decrypted += decipher.final("utf-8")
565
+ return resolve(JSON.parse(decrypted))
566
+ }
567
+ }
568
+ })
569
+ },
570
+ verifyToken: function({token, userAgent}){
571
+ const response = new ResponseCode()
572
+ return new Promise(async (resolve, reject)=>{
573
+ try{
574
+ await this.publish({
575
+ topic: "authenthor/session/svVerifyToken",
576
+ mode: "no auth",
577
+ trip: "returning",
578
+ data:{
579
+ token: token,
580
+ userAgent: userAgent
581
+ }
582
+ })
583
+ .then(apiRes=>{
584
+ response.createNew(apiRes)
585
+ throw "abort"
586
+ })
587
+ }
588
+ catch(something){
589
+ if(something != "abort"){
590
+ // err: other error
591
+ response.pushCode("LOGIC_ERROR")
592
+ response.pushTrace({code: "LOGIC_ERROR", trace: `Unexpected error on ${pseudoEnv.process.env.SVC_DISPLAY} svCreateToken() function`})
593
+ }
594
+
595
+ resolve(response)
596
+ }
597
+ })
598
+ },
599
+ getUserInfo: function({tokenBody}){
600
+ return pipe(tokenBody)
601
+ .then(aes=>{
602
+ const decipher = crypto.createDecipheriv("aes-128-cbc", Buffer.from(pseudoEnv.process.env.TOKEN_KEY), Buffer.from(pseudoEnv.process.env.TOKEN_IV))
603
+ let decrypted = decipher.update(aes, "base64", "utf-8")
604
+ return decrypted += decipher.final("utf-8")
605
+ })
606
+ .then(json=> JSON.parse(json))
607
+ .then(circular=> circular.user)
608
+ .result // { userId, userName, displayName }
609
+ },
610
+ update: function(){
611
+ version.update()
612
+ },
613
+ apiVer: function(){
614
+ return version.deprecatedVersion()+1
615
+ },
616
+ latestVer: function(){
617
+ return version.latestVersion()
618
+ }
159
619
  }