@everneed/worker 1.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 ADDED
@@ -0,0 +1,75 @@
1
+ import postgres from "postgres"
2
+ import mongoose from "mongoose"
3
+ import { createClient } from "redis"
4
+
5
+ import rabbitmq from "./worker/rabbitmq.js"
6
+ import pseudoEnv from "./pseudoenv.js"
7
+
8
+
9
+ /* Postgres */
10
+ let postgresInstance = ()=>{}
11
+ postgresInstance.config = (object)=>{
12
+ // config({
13
+ // ENV: value,
14
+ // ...
15
+ // })
16
+ /* Param Validation */
17
+ const allowed = new Set(["PG_HOST", "PG_PORT", "PG_DB", "PG_USER", "PG_PASSWORD", "PG_SCHEMA"])
18
+ if(new Set(Object.keys(object)).difference(allowed).size){
19
+ throw `illegal key on postgres.config(): Only ${Array.from(allowed).join(", ")} are allowed`
20
+ }
21
+ /* Env Add Process */
22
+ pseudoEnv.config(object)
23
+ /* Refresh Instance (force re-export) */
24
+ postgresInstance = postgres({
25
+ host: pseudoEnv.process.env.PG_HOST,
26
+ port: pseudoEnv.process.env.PG_PORT,
27
+ database: pseudoEnv.process.env.PG_DB,
28
+ username: pseudoEnv.process.env.PG_USER,
29
+ password: pseudoEnv.process.env.PG_PASSWORD,
30
+ prepare: true,
31
+ })
32
+ }
33
+ /* mongoose */
34
+ let mongooseInstance = ()=>{}
35
+ mongooseInstance.config = (object)=>{
36
+ // config({
37
+ // ENV: value,
38
+ // ...
39
+ // })
40
+ /* Param Validation */
41
+ const allowed = new Set(["MONGO_HOST", "MONGO_PORT", "MONGO_DATABASE"])
42
+ if(new Set(Object.keys(object)).difference(allowed).size){
43
+ throw `illegal key on mongoose.config(): Only ${Array.from(allowed).join(", ")} are allowed`
44
+ }
45
+ /* Env Add Process */
46
+ pseudoEnv.config(object)
47
+ /* Refresh Instance (force re-export) */
48
+ mongooseInstance = await mongoose.connect(`mongodb://${pseudoEnv.process.env.MONGO_HOST}:${pseudoEnv.process.env.MONGO_PORT}/${pseudoEnv.process.env.MONGO_DATABASE}`)
49
+ }
50
+ /* Redis */
51
+ let redisInstance = ()=>{}
52
+ redisInstance.config = async (object)=>{
53
+ // config({
54
+ // ENV: value,
55
+ // ...
56
+ // })
57
+ /* Param Validation */
58
+ const allowed = new Set(["REDIS_USERNAME", "REDIS_PASSWORD", "REDIS_HOST", "REDIS_PORT"])
59
+ if(new Set(Object.keys(object)).difference(allowed).size){
60
+ throw `illegal key on redis.config(): Only ${Array.from(allowed).join(", ")} are allowed`
61
+ }
62
+ /* Env Add Process */
63
+ pseudoEnv.config(object)
64
+ /* Refresh Instance (force re-export) */
65
+ redisInstance = await createClient(/* {
66
+ url: `redis://${pseudoEnv.process.env.REDIS_USERNAME}:${pseudoEnv.process.env.REDIS_PASSWORD}@${pseudoEnv.process.env.REDIS_HOST}:${pseudoEnv.process.env.REDIS_PORT}`
67
+ } */)
68
+ .on("error", err => console.log("Redis Client Error", err))
69
+ // .connect()
70
+ }
71
+
72
+ export { rabbitmq } // preserve seperate-dir for example
73
+ export { postgresInstance as postgres }
74
+ // export { mongooseInstance as mongoose }
75
+ export { redisInstance as redis }
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@everneed/worker",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/everneed/npm-worker.git"
11
+ },
12
+ "author": "everneed",
13
+ "license": "ISC",
14
+ "bugs": {
15
+ "url": "https://github.com/everneed/npm-worker/issues"
16
+ },
17
+ "homepage": "https://github.com/everneed/npm-worker#readme",
18
+ "description": ""
19
+ }
package/pseudoenv.js ADDED
@@ -0,0 +1,24 @@
1
+ class Config {
2
+ process = {
3
+ env: {
4
+ // ENV_NAME: value,
5
+ // ...
6
+ }
7
+ }
8
+
9
+ config(envObject){
10
+ // for(const env of envObject){
11
+ // this.process.env[env] = envObject[env]
12
+ // }
13
+
14
+ // this.process.env = {
15
+ // ...this.process.env,
16
+ // ...envObject
17
+ // }
18
+ Object.assign(this.process.env, envObject)
19
+ }
20
+ }
21
+
22
+ const pseudoEnv = new Config()
23
+
24
+ export default pseudoEnv
@@ -0,0 +1,159 @@
1
+ import path from "path"
2
+ import fs from "fs"
3
+ import amqp from "amqplib/callback_api.js"
4
+ import moment from "moment"
5
+
6
+ import pseudoEnv from "@everneed/worker/pseudoenv.js"
7
+ import { createHmac } from "crypto"
8
+
9
+ export default {
10
+ config: (object)=>{
11
+ // config({
12
+ // ENV: value,
13
+ // ...
14
+ // })
15
+ /* Param Validation */
16
+ const allowed = new Set(["SVC_NAME", "RABBIT_HOST"])
17
+ if(new Set(Object.keys(object)).difference(allowed).size){
18
+ throw `illegal key on rabbitmq.config(): Only ${Array.from(allowed).join(", ")} are allowed`
19
+ }
20
+ /* Env Add Process */
21
+ pseudoEnv.config(object)
22
+ },
23
+ start: ()=>{
24
+ /* RabbitMQ Engine */
25
+ amqp.connect(pseudoEnv.process.env.RABBIT_HOST, async (error0, connection)=>{
26
+ if(error0) throw error0
27
+
28
+ /* Initiate header variables */
29
+ const channel = connection.createChannel((error1, channel)=>{
30
+ if (error1){ throw error1 }
31
+
32
+ return channel
33
+ })
34
+ const rabbitmqGateway = fs.readdirSync(path.join(process.cwd(), "/src/mq-gateway/"))
35
+ /* Generate consumer */
36
+ // iterating every files in /src/mq-gateway
37
+ // and its function to be built as rabbitmq
38
+ // subscriber (consumer)
39
+ for(const file of rabbitmqGateway){
40
+ const moduleSpace = file.slice(0, -3)
41
+ const main = await import(`file://${path.join(process.cwd(), `/src/mq-gateway/${moduleSpace}`)}.js`)
42
+ // const main = await import(`#@/mq-gateway/${moduleSpace}.js`)
43
+
44
+ for(const functName in main.default){
45
+ /* Generate consumer */
46
+ channel.assertQueue(`req/${pseudoEnv.process.env.SVC_NAME}/${moduleSpace}/${functName}`, {
47
+ durable: true
48
+ })
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
54
+
55
+ const result = await main.default[functName](mqReq.payload)
56
+
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)
62
+ }
63
+ },{
64
+ noAck: false
65
+ })
66
+ }
67
+ }
68
+
69
+ // connection.close()
70
+ })
71
+ },
72
+ publish: ({topic, userName, trip = "passby", data})=>{
73
+ /* Usage */
74
+ // publish({
75
+ // topic: <topic start at svc :String>,
76
+ // userName: <userName or guest :String>,
77
+ // trip: <"returning"|"passby" :String>,
78
+ // data: <message :String>
79
+ // })
80
+
81
+ // 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
84
+
85
+ return new Promise(async (resolve, reject)=>{
86
+ /* RabbitMQ Engine */
87
+ amqp.connect(pseudoEnv.process.env.RABBIT_HOST, (error0, connection)=>{
88
+ if(error0){ throw error0 }
89
+
90
+ /* Creates connection & channel */
91
+ const channel = connection.createChannel((error1, channel)=>{
92
+ if (error1){ throw error1 }
93
+
94
+ return channel
95
+ })
96
+
97
+ /* Build payload structure */
98
+ // 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())
118
+
119
+ if(message.properties.correlationId == prep.message.senderAddress){
120
+ resolve(mqRes)
121
+ channel.ack(message)
122
+ setTimeout(()=>{
123
+ connection.close()
124
+ }, 500)
125
+ }
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
+
155
+ // connection.close()
156
+ })
157
+ })
158
+ },
159
+ }