@samanbayaka/core 0.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/index.mjs ADDED
@@ -0,0 +1,427 @@
1
+ import { ServiceBroker, Errors } from "moleculer"
2
+
3
+ import {SBK_GLOBAL_CONFIGS} from '#hUti/global-configs-validator.mjs'
4
+ import * as configHdl from '#hUti/config-handler.mjs'
5
+
6
+ import {AjvValidator} from "#hMol/AjvValidator.mjs"
7
+ import CustomLogger from "#hMol/CustomLogger.mjs"
8
+ import HybridCacher from "#hMol/HybridCacher.mjs"
9
+
10
+ import { gracefulShutdown, validateServiceName, ErrorFormatterMW } from '#hUti/error-handler.mjs'
11
+ import { createOpenTelemetryExporters, OpenTelemetryMW } from '#hUti/telemetry.mjs'
12
+ import { openApiToMoleculerParams } from '#hUti/openapi-to-mol-params.mjs'
13
+
14
+ /**
15
+ * Global Configs
16
+ */
17
+ const BROKER_CONFIG = await configHdl.getConfigs('/config/sbk/global/broker/yaml/nats')
18
+ const CATCHER_CONFIG = await configHdl.getConfigs('/config/sbk/global/catcher/yaml/redis')
19
+ const TELEMETRY_CONFIG = await configHdl.getConfigs('/config/sbk/global/telemetry/yaml/openobserve')
20
+
21
+ /**
22
+ * Local configs
23
+ */
24
+ const LC_LOG_LEVEL = await configHdl.getConfigs(`/config/sbk/members/${SBK_GLOBAL_CONFIGS.serviceDtls.name}/envs/LOG_LEVEL`)
25
+ const LC_TELEMETRY = await configHdl.getConfigs(`/config/sbk/members/${SBK_GLOBAL_CONFIGS.serviceDtls.name}/envs/TELEMETRY`)
26
+ const LC_MAX_L1_TTL = await configHdl.getConfigs(`/config/sbk/members/${SBK_GLOBAL_CONFIGS.serviceDtls.name}/envs/MAX_L1_TTL`)
27
+ const LC_MAX_L2_TTL = await configHdl.getConfigs(`/config/sbk/members/${SBK_GLOBAL_CONFIGS.serviceDtls.name}/envs/MAX_L2_TTL`)
28
+
29
+ /**
30
+ * Service Broker initialization params with default values
31
+ */
32
+ const LOG_LEVEL = LC_LOG_LEVEL || SBK_GLOBAL_CONFIGS.logLevel || "debug"
33
+ const TELEMETRY = LC_TELEMETRY || SBK_GLOBAL_CONFIGS.telemetry || true
34
+ const MAX_L1_TTL = LC_MAX_L1_TTL || SBK_GLOBAL_CONFIGS.maxL1TTL || 120
35
+ const MAX_L2_TTL = LC_MAX_L2_TTL || SBK_GLOBAL_CONFIGS.maxL2TTL || 600
36
+
37
+
38
+ /**
39
+ * Initialize OTLP metrics and telemetry exporter
40
+ */
41
+ const nodeSDK = await createOpenTelemetryExporters(TELEMETRY_CONFIG)
42
+ /**
43
+ * create file watcher instance
44
+ */
45
+ const fileWatcherHdl = SBK_GLOBAL_CONFIGS.createFileWatcher()
46
+ /**
47
+ * Declared broker
48
+ */
49
+ let broker
50
+
51
+ /**
52
+ * Listen for force stop signals
53
+ */
54
+ process.on("beforeExit", async () => {
55
+ await configHdl.client.close()
56
+ await nodeSDK.shutdown()
57
+ await fileWatcherHdl.close()
58
+ })
59
+
60
+ process.on("SIGINT", async (signal) => {
61
+ await gracefulShutdown(broker, signal)
62
+ }) // Ctrl+C
63
+ process.on("SIGTERM", async (signal) => {
64
+ await gracefulShutdown(broker, signal)
65
+ }) // kill command
66
+
67
+
68
+ /**
69
+ * Optional: handle uncaught errors
70
+ */
71
+ process.on("uncaughtException", async (err) => {
72
+ await gracefulShutdown(broker, "Uncaught Exception", err)
73
+ })
74
+ process.on("unhandledRejection", async (err) => {
75
+ await gracefulShutdown(broker, "Unhandled Rejection", err)
76
+ })
77
+
78
+
79
+ /**
80
+ * Event listner for configuration revision
81
+ * @type {Object}
82
+ */
83
+ const configRevisionListner = {
84
+ "config.revisions": (ctx) => {
85
+ setImmediate(async () => {
86
+ ctx.broker.logger.debug({tag: "SBK", message: "Config revision received"}, ctx.params)
87
+
88
+ /**
89
+ * Restart the service when the global configuration is updated
90
+ */
91
+ if( SBK_GLOBAL_CONFIGS.configRevision.pub != ctx.params.pub ){
92
+ ctx.broker.logger.warn({tag: "SBK", message: `Global configuration deployed by CONFIGADMIN and the service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" is restarting in progress...`})
93
+ await ctx.broker.stop()
94
+ process.exit(0)
95
+ }
96
+
97
+ /**
98
+ * Restart the service if it is an edge service in response to an edge configuration update
99
+ */
100
+ if(
101
+ SBK_GLOBAL_CONFIGS.configRevision.edg != ctx.params.edg
102
+ && schema?.settings?.port != undefined
103
+ ){
104
+ ctx.broker.logger.warn({tag: "SBK", message: `Edge configuration deployed by CONFIGADMIN and the service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" is restarting in progress...`})
105
+ await ctx.broker.stop()
106
+ process.exit(0)
107
+ }
108
+
109
+ /**
110
+ * Restart the service when the corresponding configuration is updated
111
+ */
112
+ if(
113
+ SBK_GLOBAL_CONFIGS.configRevision.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] != ctx.params.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name]
114
+ && ctx.params.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] >= 0
115
+ ){
116
+ ctx.broker.logger.warn({tag: "SBK", message: `Configuration deployed by CONFIGADMIN and the service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" is restarting in progress...`})
117
+ await ctx.broker.stop()
118
+ process.exit(0)
119
+ }
120
+
121
+ /**
122
+ * Stop the service when CONFIGADMIN removes it from the trusted list
123
+ */
124
+ if( ctx.params.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] < 0 && SBK_GLOBAL_CONFIGS.configRevision.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] >= 0 ){
125
+ ctx.broker.logger.error({tag: "SBK", message: `The service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" was stopped by CONFIGADMIN.`})
126
+ await ctx.broker.stop()
127
+ process.exit(1)
128
+ }
129
+ })
130
+ }
131
+
132
+ }
133
+
134
+ /**
135
+ * Initalized service broker, validate service and create service
136
+ */
137
+ const createSbkServices = async(schemas, isREPL = false) => {
138
+ /**
139
+ * Moleculer srvice broker configurations
140
+ * @type {ServiceBroker}
141
+ */
142
+ broker = new ServiceBroker({
143
+ ...BROKER_CONFIG,
144
+ namespace: SBK_GLOBAL_CONFIGS.namespaceTxt,
145
+ nodeID: SBK_GLOBAL_CONFIGS.serviceDtls.id,
146
+ validator: new AjvValidator(),
147
+ logLevel: LOG_LEVEL,
148
+ logger: CustomLogger({
149
+ logger: BROKER_CONFIG.logger,
150
+ logLevel: LOG_LEVEL
151
+ }),
152
+ cacher: new HybridCacher({
153
+ ...CATCHER_CONFIG,
154
+ ttl: [MAX_L2_TTL, MAX_L1_TTL]
155
+ }),
156
+ middlewares: [ErrorFormatterMW, OpenTelemetryMW(TELEMETRY)],
157
+ })
158
+
159
+ if(isREPL){
160
+ /**
161
+ * Start broker with repl mode
162
+ */
163
+ await broker.start().then(() => broker.repl())
164
+ }
165
+ else{
166
+ /**
167
+ * Start services
168
+ */
169
+ await broker.start()
170
+
171
+ const allServices = ( await broker.call("$node.services")).map(el => el.name )
172
+ if( allServices.includes("system") && SBK_GLOBAL_CONFIGS.serviceDtls.name == "system" ){
173
+ broker.logger.error({tag: "SBK", message: 'The service "system" is already registered in the Moleculer cluster.'})
174
+ await broker.stop()
175
+ process.exit(1)
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Creating moleculer services
181
+ */
182
+ schemas = Array.isArray(schemas) ? schemas : [schemas]
183
+ for ( const [index, schema] of schemas.entries() ) {
184
+
185
+ // /**
186
+ // * Listner for configuration revision
187
+ // * @type {Object}
188
+ // */
189
+ // const configRevisionListner = {
190
+ // "config.revisions": (ctx) => {
191
+ // setImmediate(async () => {
192
+ // ctx.broker.logger.debug({tag: "SBK", message: "Config revision received"}, ctx.params)
193
+
194
+ // /**
195
+ // * Restart the service when the global configuration is updated
196
+ // */
197
+ // if( SBK_GLOBAL_CONFIGS.configRevision.pub != ctx.params.pub ){
198
+ // ctx.broker.logger.warn({tag: "SBK", message: `Global configuration deployed by CONFIGADMIN and the service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" is restarting in progress...`})
199
+ // await ctx.broker.stop()
200
+ // process.exit(0)
201
+ // }
202
+
203
+ // /**
204
+ // * Restart the service if it is an edge service in response to an edge configuration update
205
+ // */
206
+ // if(
207
+ // SBK_GLOBAL_CONFIGS.configRevision.edg != ctx.params.edg
208
+ // && schema?.settings?.port != undefined
209
+ // ){
210
+ // ctx.broker.logger.warn({tag: "SBK", message: `Edge configuration deployed by CONFIGADMIN and the service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" is restarting in progress...`})
211
+ // await ctx.broker.stop()
212
+ // process.exit(0)
213
+ // }
214
+
215
+ // /**
216
+ // * Restart the service when the corresponding configuration is updated
217
+ // */
218
+ // if(
219
+ // SBK_GLOBAL_CONFIGS.configRevision.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] != ctx.params.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name]
220
+ // && ctx.params.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] >= 0
221
+ // ){
222
+ // ctx.broker.logger.warn({tag: "SBK", message: `Configuration deployed by CONFIGADMIN and the service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" is restarting in progress...`})
223
+ // await ctx.broker.stop()
224
+ // process.exit(0)
225
+ // }
226
+
227
+ // /**
228
+ // * Stop the service when CONFIGADMIN removes it from the trusted list
229
+ // */
230
+ // if( ctx.params.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] < 0 && SBK_GLOBAL_CONFIGS.configRevision.mem[SBK_GLOBAL_CONFIGS.serviceDtls.name] >= 0 ){
231
+ // ctx.broker.logger.error({tag: "SBK", message: `The service "${SBK_GLOBAL_CONFIGS.serviceDtls.name}" was stopped by CONFIGADMIN.`})
232
+ // await ctx.broker.stop()
233
+ // process.exit(1)
234
+ // }
235
+ // })
236
+ // }
237
+
238
+ // }
239
+
240
+ /**
241
+ * merge configuration listner with user defined events
242
+ * @type {Object}
243
+ */
244
+ schema.events = {
245
+ ...schema?.events,
246
+ ...configRevisionListner
247
+ }
248
+
249
+ /**
250
+ * Override params according to the OpenAPI schema definition
251
+ */
252
+ if( schema?.actions ) {
253
+ Object.keys(schema.actions).map((el) => {
254
+ if( schema.actions[el] ){
255
+ openApiToMoleculerParams(schema.actions[el])
256
+
257
+ }
258
+ })
259
+ }
260
+
261
+
262
+ validateServiceName(
263
+ schema,
264
+ (index == 0
265
+ ? SBK_GLOBAL_CONFIGS.serviceDtls.name
266
+ : schema.name
267
+ )
268
+ )
269
+ broker.createService(schema)
270
+ }
271
+
272
+
273
+ /**
274
+ * Notify on changes to files in the service directory.
275
+ */
276
+ fileWatcherHdl.on("add", (fp) => {
277
+ broker.logger.warn({tag: "SBK", message: `The file "${fp}" has been added to your service folder. Restart your service for the changes to take effect.`})
278
+ })
279
+ fileWatcherHdl.on("addDir", (fp) => {
280
+ broker.logger.warn({tag: "SBK", message: `The DIR "${fp}" has been added to your service folder. Restart your service for the changes to take effect.`})
281
+ })
282
+ fileWatcherHdl.on("change", (fp) => {
283
+ broker.logger.warn({tag: "SBK", message: `The file "${fp}" in your service folder has been modified. Restart your service for the changes to take effect.`})
284
+ })
285
+ fileWatcherHdl.on("unlink", (fp) => {
286
+ broker.logger.warn({tag: "SBK", message: `The file "${fp}" in your service folder has been removed. Restart your service for the changes to take effect.`})
287
+ })
288
+ fileWatcherHdl.on("unlinkDir", (fp) => {
289
+ broker.logger.warn({tag: "SBK", message: `The DIR "${fp}" in your service folder has been removed. Restart your service for the changes to take effect.`})
290
+ })
291
+
292
+ }
293
+
294
+
295
+ /**
296
+ * Export samanbayaka object
297
+ */
298
+ export default {
299
+ Errors,
300
+ getConfigs: configHdl.getConfigs,
301
+ createSbkServices,
302
+
303
+ // /**
304
+ // * Loading Demo
305
+ // */
306
+ // createDemo: async() => {
307
+ // const { default: apiGateway } = await import('#sApi/index.mjs')
308
+ // const demo = await import('#sDmo/index.mjs')
309
+
310
+ // const gtWy = {...apiGateway}
311
+ // gtWy.name = "demo"
312
+ // gtWy.settings.port = 3000
313
+ // gtWy.settings.routes = gtWy.settings.routes.map((rt) => {
314
+ // rt.path = `/demo${rt.path}`
315
+ // return rt
316
+ // })
317
+ // createSbkServices([
318
+ // gtWy,
319
+ // demo.user,
320
+ // demo.role,
321
+ // demo.mail,
322
+ // demo.sms
323
+ // ], true)
324
+
325
+ // },
326
+
327
+ // /**
328
+ // * system services
329
+ // * @return {void}
330
+ // */
331
+ // registerSystemAPI: async () => {
332
+ // const { default: sysApi } = await import('#sSys/index.mjs')
333
+ // const { default: authMgr } = await import('#sAut/auth.mjs')
334
+ // const { default: oidc } = await import('#sYdc/index.mjs')
335
+ // const { default: asset } = await import('#sAst/index.mjs')
336
+ // const { default: dbs } = await import('#sDbs/index.mjs')
337
+
338
+ // const pgDb = await dbs('pg', {})
339
+
340
+ // createSbkServices([sysApi, authMgr, oidc, asset, pgDb])
341
+ // // createSbkServices([sysApi, authMgr, asset])
342
+
343
+ // },
344
+
345
+ // registerAuthAPI: async () => {
346
+ // const { default: coreAuth } = await import('#sAut/index.mjs')
347
+ // createSbkServices(coreAuth)
348
+
349
+ // },
350
+
351
+ // registerEdgeAPI: async () => {
352
+ // const { default: apiGateway } = await import('#sApi/index.mjs')
353
+ // createSbkServices(apiGateway)
354
+
355
+ // },
356
+
357
+
358
+ // registerServices: async (schema) => {
359
+ // createSbkServices(schema)
360
+
361
+ // },
362
+
363
+
364
+ // registerBridgeServices: async( type, brokerOpts = {}, callback = () => {}) => {
365
+ // const { auxBrokerParamsValidator } = await import('#hUti/aux-broker-params-validator.mjs')
366
+ // const {logLevel, gzip, msgPack, rest, ...opts} = brokerOpts
367
+ // const paramObj = {type, opts, callback}
368
+
369
+ // auxBrokerParamsValidator(paramObj)
370
+ // callback = paramObj.callback
371
+
372
+ // const auxBroker = /kafka/.test(type)
373
+ // ? await import('#sKfk/index.mjs')
374
+ // : /mqtt/.test(type)
375
+ // ? undefined // await import('#sKfk/index.mjs')
376
+ // : /amqp/.test(type)
377
+ // ? undefined // await import('#sKfk/index.mjs')
378
+ // : undefined
379
+
380
+ // if ( !auxBroker ) {
381
+ // // throw new Error("The \"type\" value must be either \"kafka\" or \"mqtt\" or \"amqp\"")
382
+ // throw new Error("The \"type\" value must be either \"kafka\"")
383
+ // }
384
+
385
+ // opts.logLevel = logLevel
386
+ // opts.gzip = gzip
387
+ // opts.msgPack = msgPack
388
+ // opts.rest = rest
389
+
390
+ // /**
391
+ // * Service create for kafka producer
392
+ // * @type {object}
393
+ // */
394
+ // if( opts.name.startsWith("producer") ){
395
+ // createSbkServices(auxBroker.producer( opts, callback ))
396
+
397
+ // }
398
+ // else {
399
+ // createSbkServices(auxBroker.consumer( opts, callback ))
400
+
401
+ // }
402
+
403
+ // },
404
+
405
+ // registerAuthAPI: async () => {
406
+ // const { default: authMgr } = await import('#sAut/index.mjs')
407
+ // createSbkServices([authMgr])
408
+
409
+ // },
410
+
411
+ // registerYdcAPI: async () => {
412
+ // const { default: oidc } = await import('#sYdc/index.mjs')
413
+ // createSbkServices([oidc])
414
+
415
+ // },
416
+
417
+ // registerDbsAPI: async (type, opts) => {
418
+ // const { default: dbs } = await import('#sDbs/index.mjs')
419
+ // createSbkServices(await dbs(type, opts))
420
+
421
+ // },
422
+
423
+ }
424
+
425
+
426
+ // rm -rf node_modules/samanbayaka/ && cp -r ../../samanbayaka/. node_modules/samanbayaka/ && SBK_CONFIG_CRED='<your_etcd_user> <your_etcd_pass> <your_etcd_port>' node index.mjs
427
+ // pm2 stop "$(basename "$PWD")" && rm -rf node_modules/samanbayaka/ && cp -r ../../samanbayaka/. node_modules/samanbayaka/ && pm2 start "$(basename "$PWD")" && pm2 logs | grep '━━━━━━━❖❖ SBK ❖❖━━━━━━━'
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@samanbayaka/core",
3
+ "version": "0.0.1",
4
+ "description": "Moleculer Gateway service with kafka transporter",
5
+ "homepage": "https://gitlab.com/dalal.suvendu/samanbayaka#readme",
6
+ "bugs": {
7
+ "url": "https://gitlab.com/dalal.suvendu/samanbayaka/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://gitlab.com/dalal.suvendu/samanbayaka.git"
12
+ },
13
+ "license": "ISC",
14
+ "author": "dalal.suvendu",
15
+ "type": "module",
16
+ "imports": {
17
+ "#hFil/*": "./helper/file/*",
18
+ "#hUti/*": "./helper/utility/*",
19
+ "#hMol/*": "./helper/mol-built-in/*",
20
+ "#sAut/*": "./services/auth/*",
21
+ "#sApi/*": "./services/gateway/*",
22
+ "#sDmo/*": "./services/demo/*",
23
+ "#sKfk/*": "./services/kafkajs/*",
24
+ "#sSys/*": "./services/system/*",
25
+ "#sYdc/*": "./services/oidc/*",
26
+ "#sDbs/*": "./services/dbs/*",
27
+ "#sAst/*": "./services/asset/*"
28
+ },
29
+ "main": "index.mjs",
30
+ "dependencies": {
31
+ "@aws-sdk/client-s3": "^3.1112.0",
32
+ "@opentelemetry/api": "^1.9.1",
33
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.216.0",
34
+ "@opentelemetry/exporter-trace-otlp-http": "^0.216.0",
35
+ "@opentelemetry/resources": "^2.7.1",
36
+ "@opentelemetry/sdk-metrics": "^2.7.1",
37
+ "@opentelemetry/sdk-node": "^0.216.0",
38
+ "@opentelemetry/sdk-trace-base": "^2.7.1",
39
+ "@opentelemetry/semantic-conventions": "^1.40.0",
40
+ "@smithy/node-http-handler": "^4.11.3",
41
+ "acorn": "^8.18.0",
42
+ "ajv": "^8.18.0",
43
+ "ajv-formats": "^3.0.1",
44
+ "approve-builds": "^1.0.0",
45
+ "body-parser": "^2.3.0",
46
+ "chokidar": "^5.0.0",
47
+ "compression": "^1.8.1",
48
+ "cookie-parser": "^1.4.7",
49
+ "etcd3": "^1.1.2",
50
+ "helmet": "^8.1.0",
51
+ "ioredis": "^5.10.1",
52
+ "jsonwebtoken": "^9.0.3",
53
+ "jwks-rsa": "^4.0.1",
54
+ "kafkajs": "^2.2.4",
55
+ "lru-cache": "^11.3.5",
56
+ "moleculer": "0.15.0",
57
+ "moleculer-auto-openapi": "^1.1.7",
58
+ "moleculer-repl": "^0.8.0",
59
+ "moleculer-web": "^0.11.0",
60
+ "msgpack5": "^6.0.2",
61
+ "pg": "^8.23.0",
62
+ "pino": "^10.3.1",
63
+ "pnpm": "^10.34.1",
64
+ "sinon": "^21.1.2",
65
+ "swagger-stats": "^0.99.7",
66
+ "uuid": "^14.0.1",
67
+ "yaml": "^2.9.0"
68
+ },
69
+ "devDependencies": {
70
+ "chai": "^6.2.2",
71
+ "husky": "^9.1.7",
72
+ "mocha": "^11.7.5"
73
+ },
74
+ "scripts": {
75
+ "test": "mocha",
76
+ "start": "node index.mjs"
77
+ }
78
+ }