@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 +75 -0
- package/package.json +19 -0
- package/pseudoenv.js +24 -0
- package/worker/rabbitmq.js +159 -0
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
|
+
}
|