samanbayaka 0.0.2
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/README.md +97 -0
- package/bash/sbk.sh +11 -0
- package/config/broker.mjs +159 -0
- package/config/kafkajs.mjs +54 -0
- package/config/logger.mjs +35 -0
- package/config/nats.mjs +26 -0
- package/config/server.mjs +26 -0
- package/gateway.mjs +140 -0
- package/helper/file/esm-loading.mjs +181 -0
- package/helper/transporter/RedpandaTransporter.mjs +235 -0
- package/helper/transporter/RedpandaTransporter.mjs.bk +230 -0
- package/helper/transporter/rpk-commands.txt +8 -0
- package/helper/utility/format-errors.mjs +72 -0
- package/helper/utility/node-argv.mjs +55 -0
- package/helper/validator/AjvValidator.mjs +65 -0
- package/index-argv.mjs +92 -0
- package/index.mjs +121 -0
- package/metrics.mjs +49 -0
- package/package.json +43 -0
- package/package.json.bk +32 -0
- package/pnpm-workspace.yaml +2 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +30 -0
- package/public/favicon2.ico +0 -0
- package/public/index.html +115 -0
- package/public/scalar/js/standalone.js +44 -0
- package/public/scalar.html +28 -0
- package/samanbayaka-0.0.1.tgz +0 -0
- package/services/gateway/index.mjs +136 -0
- package/services/mail/index.mjs +12 -0
- package/services/sms/index.mjs +16 -0
- package/services/system/openapi.mjs +49 -0
- package/services/system/scalar.mjs +31 -0
- package/services/system/sw-stats-mw.mjs +12 -0
- package/services/system/system.service.mjs +212 -0
- package/services/tst/index.mjs +67 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*format-error.mjs*/
|
|
2
|
+
|
|
3
|
+
import { Errors } from "moleculer"
|
|
4
|
+
|
|
5
|
+
export const formatBrokerErrors = (err, info)=>{
|
|
6
|
+
|
|
7
|
+
const traceId = info?.ctx?.requestID
|
|
8
|
+
const status = err?.code || 500
|
|
9
|
+
const type = err?.type || 'INTERNAL_ERROR'
|
|
10
|
+
const instance = info?.ctx?.action?.name
|
|
11
|
+
? `${info?.ctx?.action?.name}`
|
|
12
|
+
: `event.${info?.ctx?.event?.name}`
|
|
13
|
+
const title = err?.message || ''
|
|
14
|
+
const detail = (err?.data && Array.isArray(err?.data))
|
|
15
|
+
? err.data.map(el=>{
|
|
16
|
+
delete el.nodeID
|
|
17
|
+
delete el.action
|
|
18
|
+
return el
|
|
19
|
+
})
|
|
20
|
+
: err?.data || ""
|
|
21
|
+
|
|
22
|
+
const stack = err?.stack || ""
|
|
23
|
+
const timestamp = new Date().toISOString()
|
|
24
|
+
|
|
25
|
+
if(err.nodeID === undefined){
|
|
26
|
+
if(err.code){
|
|
27
|
+
info.service.broker.logger.error('❖ SBK', {traceId, type, instance, detail, stack: ""})
|
|
28
|
+
}
|
|
29
|
+
else{
|
|
30
|
+
info.service.broker.logger.error('❖ SBK', {traceId, type, instance, detail, stack})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* If it's an event, do not throw. Just log and stop.
|
|
35
|
+
*/
|
|
36
|
+
if (info?.ctx?.event) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
throw new Errors.MoleculerError(
|
|
41
|
+
title,
|
|
42
|
+
status,
|
|
43
|
+
type,
|
|
44
|
+
{
|
|
45
|
+
traceId,
|
|
46
|
+
status,
|
|
47
|
+
type,
|
|
48
|
+
title,
|
|
49
|
+
detail,
|
|
50
|
+
timestamp
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
throw err
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const formatApiGwErrors = (req, res, err)=>{
|
|
58
|
+
const code = err.code || 500
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Handle only server errors (>=500)
|
|
62
|
+
*/
|
|
63
|
+
res.setHeader("Content-Type", "application/json")
|
|
64
|
+
res.writeHead(code)
|
|
65
|
+
res.end(JSON.stringify({
|
|
66
|
+
error: {
|
|
67
|
+
...err?.data || {message: err.message},
|
|
68
|
+
...{instance: `${req.method} ${req.url}`}
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*node-argv.mjs*/
|
|
2
|
+
|
|
3
|
+
import os from "os"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Remove the first two elements that used by the node itself from
|
|
7
|
+
* the arguments and define it as a constant
|
|
8
|
+
* @type {array}
|
|
9
|
+
*/
|
|
10
|
+
const argv = process.argv.slice(2)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extract the service name from the arguments.
|
|
15
|
+
* @type {string}
|
|
16
|
+
*/
|
|
17
|
+
export const serviceName = argv.find(el => /^[a-z0-9]+(-[a-z0-9]+)*$/i.test(el))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* An error is logged when the argument does not contain a valid service name.
|
|
22
|
+
*/
|
|
23
|
+
if (serviceName === undefined) {
|
|
24
|
+
console.error("Error: Service name is missing.")
|
|
25
|
+
console.log("Please run one of the following commands:\n $node your_file.mjs <service_name> -h (for hot reloading)\n $node your_file.mjs <service_name> --hot (for hot reloading)\n or \n $node your_file.mjs <service_name>")
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extract the hot-reloading switch from the arguments and define
|
|
32
|
+
* it as a constant
|
|
33
|
+
* @type {Boolean}
|
|
34
|
+
*/
|
|
35
|
+
export const isHotReloadEnabled = (argv.includes("-h") || argv.includes("--hot")) || false
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extract the REPL switch from the arguments and define
|
|
40
|
+
* it as a constant
|
|
41
|
+
* @type {Boolean}
|
|
42
|
+
*/
|
|
43
|
+
export const isReplEnabled = (argv.includes("-r") || argv.includes("--repl")) || false
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate a unique ID for each node instance.
|
|
48
|
+
* Combine the service name, host name, and process ID
|
|
49
|
+
* @type {string}
|
|
50
|
+
*/
|
|
51
|
+
export const nodeUid = [
|
|
52
|
+
isHotReloadEnabled ? `${serviceName}^` : serviceName,
|
|
53
|
+
os.hostname(),
|
|
54
|
+
process.pid
|
|
55
|
+
].join(".")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*AjvValidator.mjs*/
|
|
2
|
+
|
|
3
|
+
import { randomUUID } from "crypto"
|
|
4
|
+
|
|
5
|
+
import Ajv from "ajv"
|
|
6
|
+
import { Validators, Errors } from "moleculer"
|
|
7
|
+
|
|
8
|
+
export default class AjvValidator extends Validators.Base {
|
|
9
|
+
constructor() {
|
|
10
|
+
super()
|
|
11
|
+
this.ajv = new Ajv({
|
|
12
|
+
allErrors: true,
|
|
13
|
+
strict: false,
|
|
14
|
+
coerceTypes: true //true auto fit data to integer data type like 12 if supplied "12"
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
compile(schema) {
|
|
19
|
+
const validate = this.ajv.compile(schema)
|
|
20
|
+
|
|
21
|
+
return (params) => {
|
|
22
|
+
const valid = validate(params)
|
|
23
|
+
|
|
24
|
+
if (!valid) {
|
|
25
|
+
return formatAjvErrors(validate.errors, params)
|
|
26
|
+
}
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Format AJV Errors
|
|
35
|
+
* @param {error object}
|
|
36
|
+
* @param {params object}
|
|
37
|
+
* @return {object}
|
|
38
|
+
*/
|
|
39
|
+
const formatAjvErrors = (errors, params) => {
|
|
40
|
+
if(!errors){
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return errors.map(err => {
|
|
45
|
+
/**
|
|
46
|
+
* Remove leading "/" and subsequent "/" to "."
|
|
47
|
+
* @type {string}
|
|
48
|
+
*/
|
|
49
|
+
const ky = err.instancePath
|
|
50
|
+
.replace(/^\/+/, '')
|
|
51
|
+
.replace(/\//g, '.')
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Getting value from nested object
|
|
55
|
+
* @param {string} like query.name.lastName
|
|
56
|
+
* @return {string}
|
|
57
|
+
*/
|
|
58
|
+
const val = ky.split('.').reduce((acc, key) => acc?.[key], params)
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
field: ky,
|
|
62
|
+
message: `The value '${val}' ${err.message}`
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
}
|
package/index-argv.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*index.mjs*/
|
|
2
|
+
import path from "path"
|
|
3
|
+
import url from "url"
|
|
4
|
+
import fs from "fs"
|
|
5
|
+
|
|
6
|
+
import { ServiceBroker } from "moleculer"
|
|
7
|
+
import { execSync } from "child_process"
|
|
8
|
+
|
|
9
|
+
import {isHotReloadEnabled, serviceName, nodeUid} from '#hUti/node-argv.mjs'
|
|
10
|
+
import {formatBrokerErrors} from '#hUti/format-errors.mjs'
|
|
11
|
+
import {getConfig, loadServices} from '#hFil/esm-loading.mjs'
|
|
12
|
+
import AjvValidator from "#hVal/AjvValidator.mjs"
|
|
13
|
+
|
|
14
|
+
const BROKER_CONFIG = await getConfig('broker')
|
|
15
|
+
const NATS_CONFIG = await getConfig('nats')
|
|
16
|
+
const LOGGER_CONFIG = await getConfig('logger')
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Last git commit hash in short form
|
|
20
|
+
*/
|
|
21
|
+
const gitCommitHs = execSync("git rev-parse --short HEAD")
|
|
22
|
+
.toString()
|
|
23
|
+
.trim()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Moleculer srvice broker configurations
|
|
28
|
+
* @type {ServiceBroker}
|
|
29
|
+
*/
|
|
30
|
+
const broker = new ServiceBroker({
|
|
31
|
+
...{
|
|
32
|
+
...BROKER_CONFIG,
|
|
33
|
+
namespace: `${BROKER_CONFIG.namespace}-${gitCommitHs}`
|
|
34
|
+
},
|
|
35
|
+
...{
|
|
36
|
+
nodeID: nodeUid,
|
|
37
|
+
transporter: NATS_CONFIG,
|
|
38
|
+
validator: new AjvValidator(),
|
|
39
|
+
logger: LOGGER_CONFIG,
|
|
40
|
+
},
|
|
41
|
+
errorHandler: formatBrokerErrors,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Loads modules asynchronously
|
|
47
|
+
*/
|
|
48
|
+
await loadServices(broker)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Start broker with repl mode
|
|
53
|
+
*/
|
|
54
|
+
broker.start().then(() => broker.repl())
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Graceful shutdown handler
|
|
59
|
+
*/
|
|
60
|
+
const shutdown = async (signal) => {
|
|
61
|
+
broker.logger.info('❖ SBK', `Received ${signal}. Stopping broker...`)
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await broker.stop()
|
|
65
|
+
broker.logger.info('❖ SBK', "Broker stopped gracefully.")
|
|
66
|
+
process.exit(0)
|
|
67
|
+
} catch (err) {
|
|
68
|
+
broker.logger.error('❖ SBK', "Error during broker shutdown:", err)
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Listen for force stop signals
|
|
76
|
+
*/
|
|
77
|
+
process.on("SIGINT", shutdown) // Ctrl+C
|
|
78
|
+
process.on("SIGTERM", shutdown) // kill command
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Optional: handle uncaught errors
|
|
83
|
+
*/
|
|
84
|
+
process.on("uncaughtException", async (err) => {
|
|
85
|
+
broker.logger.error('❖ SBK', "Uncaught Exception:", err)
|
|
86
|
+
await shutdown("uncaughtException")
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
process.on("unhandledRejection", async (err) => {
|
|
90
|
+
broker.logger.error('❖ SBK', "Unhandled Rejection:", err)
|
|
91
|
+
await shutdown("unhandledRejection")
|
|
92
|
+
})
|
package/index.mjs
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*index.mjs*/
|
|
2
|
+
import path from "path"
|
|
3
|
+
import url from "url"
|
|
4
|
+
import fs from "fs"
|
|
5
|
+
|
|
6
|
+
import { ServiceBroker } from "moleculer"
|
|
7
|
+
import { execSync } from "child_process"
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
isReplEnabled,
|
|
11
|
+
isHotReloadEnabled,
|
|
12
|
+
serviceName,
|
|
13
|
+
nodeUid
|
|
14
|
+
} from '#hUti/node-argv.mjs'
|
|
15
|
+
import {formatBrokerErrors} from '#hUti/format-errors.mjs'
|
|
16
|
+
import {
|
|
17
|
+
getConfig,
|
|
18
|
+
loadServices
|
|
19
|
+
} from '#hFil/esm-loading.mjs'
|
|
20
|
+
import AjvValidator from "#hVal/AjvValidator.mjs"
|
|
21
|
+
|
|
22
|
+
const BROKER_CONFIG = await getConfig('broker')
|
|
23
|
+
const NATS_CONFIG = await getConfig('nats')
|
|
24
|
+
const LOGGER_CONFIG = await getConfig('logger')
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Last git commit hash in short form
|
|
28
|
+
*/
|
|
29
|
+
const gitCommitHs = execSync("git rev-parse --short HEAD")
|
|
30
|
+
.toString()
|
|
31
|
+
.trim()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Moleculer srvice broker configurations
|
|
36
|
+
* @type {ServiceBroker}
|
|
37
|
+
*/
|
|
38
|
+
const broker = new ServiceBroker({
|
|
39
|
+
...{
|
|
40
|
+
...BROKER_CONFIG,
|
|
41
|
+
...{
|
|
42
|
+
namespace: `${BROKER_CONFIG.namespace}-${gitCommitHs}`
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
...{
|
|
46
|
+
nodeID: nodeUid,
|
|
47
|
+
transporter: NATS_CONFIG,
|
|
48
|
+
validator: new AjvValidator(),
|
|
49
|
+
logger: LOGGER_CONFIG,
|
|
50
|
+
},
|
|
51
|
+
errorHandler: formatBrokerErrors,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// /**
|
|
56
|
+
// * Loads modules asynchronously
|
|
57
|
+
// */
|
|
58
|
+
// await loadServices(broker)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
// /**
|
|
62
|
+
// * Start broker with repl mode
|
|
63
|
+
// */
|
|
64
|
+
// if(isReplEnabled){
|
|
65
|
+
// broker.start().then(() => broker.repl())
|
|
66
|
+
// }
|
|
67
|
+
// else{
|
|
68
|
+
// broker.start()
|
|
69
|
+
// }
|
|
70
|
+
|
|
71
|
+
export const sbk = async (sBus, schema)=>{
|
|
72
|
+
broker.createService(schema)
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Start broker with repl mode
|
|
76
|
+
*/
|
|
77
|
+
if(isReplEnabled){
|
|
78
|
+
broker.start().then(() => broker.repl())
|
|
79
|
+
}
|
|
80
|
+
else{
|
|
81
|
+
broker.start()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Graceful shutdown handler
|
|
88
|
+
*/
|
|
89
|
+
const shutdown = async (signal) => {
|
|
90
|
+
broker.logger.info('❖ SBK', `Received ${signal}. Stopping broker...`)
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
await broker.stop()
|
|
94
|
+
broker.logger.info('❖ SBK', "Broker stopped gracefully.")
|
|
95
|
+
process.exit(0)
|
|
96
|
+
} catch (err) {
|
|
97
|
+
broker.logger.error('❖ SBK', "Error during broker shutdown:", err)
|
|
98
|
+
process.exit(1)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Listen for force stop signals
|
|
105
|
+
*/
|
|
106
|
+
process.on("SIGINT", shutdown) // Ctrl+C
|
|
107
|
+
process.on("SIGTERM", shutdown) // kill command
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Optional: handle uncaught errors
|
|
112
|
+
*/
|
|
113
|
+
process.on("uncaughtException", async (err) => {
|
|
114
|
+
broker.logger.error('❖ SBK', "Uncaught Exception:", err)
|
|
115
|
+
await shutdown("uncaughtException")
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
process.on("unhandledRejection", async (err) => {
|
|
119
|
+
broker.logger.error('❖ SBK', "Unhandled Rejection:", err)
|
|
120
|
+
await shutdown("unhandledRejection")
|
|
121
|
+
})
|
package/metrics.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*metrics.mjs*/
|
|
2
|
+
import { ServiceBroker } from "moleculer"
|
|
3
|
+
import { logLevel } from "kafkajs"
|
|
4
|
+
import os from "os"
|
|
5
|
+
|
|
6
|
+
import CONFIG from "./config/service.config.mjs"
|
|
7
|
+
import RedpandaTransporter from "./transporter/RedpandaTransporter.mjs"
|
|
8
|
+
import KAFKAJS_CONFIG from "./config/kafkajs.config.mjs"
|
|
9
|
+
|
|
10
|
+
const serviceName = "metrics"
|
|
11
|
+
|
|
12
|
+
const getNodeId = () => {
|
|
13
|
+
const hostName = os.hostname()
|
|
14
|
+
// return `${serviceName}.${hostName}.${process.pid}`
|
|
15
|
+
return `${serviceName}.${hostName}`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const broker = new ServiceBroker({
|
|
19
|
+
nodeID: getNodeId(),
|
|
20
|
+
transporter: new RedpandaTransporter(KAFKAJS_CONFIG),
|
|
21
|
+
metrics: true, // enable metrics
|
|
22
|
+
tracing: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
reporter: [
|
|
25
|
+
{
|
|
26
|
+
type: "Prometheus",
|
|
27
|
+
options: {
|
|
28
|
+
port: 8088,
|
|
29
|
+
path: "/metrics",
|
|
30
|
+
defaultLabels: (registry) => ({
|
|
31
|
+
nodeID: registry.broker.nodeID
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
// Enable tracing with console exporter (valid types: Console, Jaeger, Datadog, Zipkin)
|
|
38
|
+
tracing: {
|
|
39
|
+
enabled: true,
|
|
40
|
+
exporter: [
|
|
41
|
+
{
|
|
42
|
+
type: "Console"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
/*Start broker*/
|
|
49
|
+
broker.start()
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "samanbayaka",
|
|
3
|
+
"version": "0.0.2",
|
|
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
|
+
"#hVal/*": "./helper/validator/*",
|
|
20
|
+
"#sSys/*": "./services/system/*"
|
|
21
|
+
},
|
|
22
|
+
"main": "index.mjs",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
25
|
+
"start": "node index.mjs service_name"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"ajv": "^8.18.0",
|
|
29
|
+
"chokidar": "^5.0.0",
|
|
30
|
+
"compression": "^1.8.1",
|
|
31
|
+
"cookie-parser": "^1.4.7",
|
|
32
|
+
"helmet": "^8.1.0",
|
|
33
|
+
"kafkajs": "^2.2.4",
|
|
34
|
+
"moleculer": "^0.14.35",
|
|
35
|
+
"moleculer-auto-openapi": "^1.1.7",
|
|
36
|
+
"moleculer-repl": "^0.7.4",
|
|
37
|
+
"moleculer-web": "^0.10.8",
|
|
38
|
+
"msgpack5": "^6.0.2",
|
|
39
|
+
"nats": "^2.29.3",
|
|
40
|
+
"pnpm": "^10.32.1",
|
|
41
|
+
"swagger-stats": "^0.99.7"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/package.json.bk
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "samanbayaka-gateway",
|
|
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
|
+
"main": "index.mjs",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
19
|
+
"start": "node index.mjs service_name"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"chokidar": "^4.0.3",
|
|
23
|
+
"cookie-parser": "^1.4.7",
|
|
24
|
+
"helmet": "^8.1.0",
|
|
25
|
+
"kafkajs": "^2.2.4",
|
|
26
|
+
"moleculer": "^0.14.35",
|
|
27
|
+
"moleculer-auto-openapi": "^1.1.6",
|
|
28
|
+
"moleculer-repl": "^0.7.4",
|
|
29
|
+
"moleculer-web": "^0.10.8",
|
|
30
|
+
"swagger-stats": "^0.99.7"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 80 80" fill="none" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
2
|
+
<rect x="2" y="2" rx="25" width="76" height="76" fill="none" stroke="#b" stroke-width="2.2"/>
|
|
3
|
+
<g transform="rotate(45, 40, 40) scale(.8) translate(10, 10)">
|
|
4
|
+
<!--<rect x="2" y="2" width="12" height="12" rx="2" fill="#f2fabd" stroke-width="1" stroke="#f2fabd"/>-->
|
|
5
|
+
<rect x="18" y="2" width="12" height="12" rx="2" fill="#ecf9a5" stroke-width="1" stroke="#ecf9a5"/>
|
|
6
|
+
<rect x="34" y="2" width="12" height="12" rx="2" fill="#e7f792" stroke-width="1" stroke="#e7f792"/>
|
|
7
|
+
<rect x="50" y="2" width="12" height="12" rx="2" fill="#e3f786" stroke-width="1" stroke="#e3f786"/>
|
|
8
|
+
<!--<rect x="66" y="2" width="12" height="12" rx="2" fill="#cfe07a" stroke-width="1" stroke="#cfe07a"/>-->
|
|
9
|
+
<rect x="2" y="18" width="12" height="12" rx="2" fill="#ffedb6" stroke-width="1" stroke="#ffedb6"/>
|
|
10
|
+
<rect x="18" y="18" width="12" height="12" rx="2" fill="#ffe79b" stroke-width="1" stroke="#ffe79b"/>
|
|
11
|
+
<rect x="34" y="18" width="12" height="12" rx="2" fill="#ffe286" stroke-width="1" stroke="#ffe286"/>
|
|
12
|
+
<rect x="50" y="18" width="12" height="12" rx="2" fill="#ffdf78" stroke-width="1" stroke="#ffdf78"/>
|
|
13
|
+
<rect x="66" y="18" width="12" height="12" rx="2" fill="#ebcb6e" stroke-width="1" stroke="#ebcb6e"/>
|
|
14
|
+
<rect x="2" y="34" width="12" height="12" rx="2" fill="#ffd09d" stroke-width="1" stroke="#ffd09d"/>
|
|
15
|
+
<rect x="18" y="34" width="12" height="12" rx="2" fill="#ffbf79" stroke-width="1" stroke="#ffbf79"/>
|
|
16
|
+
<rect x="34" y="34" width="12" height="12" rx="2" fill="#ffb25e" stroke-width="1" stroke="#ffb25e"/>
|
|
17
|
+
<rect x="50" y="34" width="12" height="12" rx="2" fill="#ffaa4c" stroke-width="1" stroke="#ffaa4c"/>
|
|
18
|
+
<rect x="66" y="34" width="12" height="12" rx="2" fill="#ef9b46" stroke-width="1" stroke="#ef9b46"/>
|
|
19
|
+
<rect x="2" y="50" width="12" height="12" rx="2" fill="#ffac8b" stroke-width="1" stroke="#ffac8b"/>
|
|
20
|
+
<rect x="18" y="50" width="12" height="12" rx="2" fill="#ff8c63" stroke-width="1" stroke="#ff8c63"/>
|
|
21
|
+
<rect x="34" y="50" width="12" height="12" rx="2" fill="#ff7245" stroke-width="1" stroke="#ff7245"/>
|
|
22
|
+
<rect x="50" y="50" width="12" height="12" rx="2" fill="#ff6030" stroke-width="1" stroke="#ff6030"/>
|
|
23
|
+
<rect x="66" y="50" width="12" height="12" rx="2" fill="#ec582d" stroke-width="1" stroke="#ec582d"/>
|
|
24
|
+
<!--<rect x="2" y="66" width="12" height="12" rx="2" fill="#fb9495" stroke-width="1" stroke="#fb9495"/>-->
|
|
25
|
+
<rect x="18" y="66" width="12" height="12" rx="2" fill="#f36a71" stroke-width="1" stroke="#f36a71"/>
|
|
26
|
+
<rect x="34" y="66" width="12" height="12" rx="2" fill="#eb4558" stroke-width="1" stroke="#eb4558"/>
|
|
27
|
+
<rect x="50" y="66" width="12" height="12" rx="2" fill="#e41f47" stroke-width="1" stroke="#e41f47"/>
|
|
28
|
+
<!--<rect x="66" y="66" width="12" height="12" rx="2" fill="#d02142" stroke-width="1" stroke="#d02142"/>-->
|
|
29
|
+
</g>
|
|
30
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Dashboard with Vertical Tabs</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root {
|
|
8
|
+
--sidebar-width: 220px;
|
|
9
|
+
--bg: #0f1720;
|
|
10
|
+
--sidebar-bg: #111827;
|
|
11
|
+
--tab-hover: #1f2937;
|
|
12
|
+
--active: #2563eb;
|
|
13
|
+
--text: #e5e7eb;
|
|
14
|
+
--muted: #9ca3af;
|
|
15
|
+
font-family: sans-serif;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
height: 100vh;
|
|
21
|
+
display: grid;
|
|
22
|
+
grid-template-columns: var(--sidebar-width) 1fr;
|
|
23
|
+
background: var(--bg);
|
|
24
|
+
color: var(--text);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Sidebar */
|
|
28
|
+
.sidebar {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
background: var(--sidebar-bg);
|
|
32
|
+
padding: 12px;
|
|
33
|
+
gap: 8px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Sidebar brand */
|
|
37
|
+
.brand {
|
|
38
|
+
font-weight: bold;
|
|
39
|
+
font-size: 18px;
|
|
40
|
+
margin-bottom: 16px;
|
|
41
|
+
color: var(--active);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Tabs */
|
|
45
|
+
.tab {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 10px;
|
|
49
|
+
padding: 10px;
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
color: var(--muted);
|
|
53
|
+
transition: background 0.2s, color 0.2s;
|
|
54
|
+
}
|
|
55
|
+
.tab:hover { background: var(--tab-hover); color: var(--text); }
|
|
56
|
+
.tab.active { background: var(--active); color: white; }
|
|
57
|
+
|
|
58
|
+
/* Main content */
|
|
59
|
+
.main {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
padding: 20px;
|
|
63
|
+
overflow: auto;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
header {
|
|
67
|
+
font-size: 20px;
|
|
68
|
+
font-weight: bold;
|
|
69
|
+
margin-bottom: 20px;
|
|
70
|
+
}
|
|
71
|
+
section {
|
|
72
|
+
flex: 1;
|
|
73
|
+
background: #1f2937;
|
|
74
|
+
border-radius: 10px;
|
|
75
|
+
padding: 20px;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
78
|
+
</head>
|
|
79
|
+
<body>
|
|
80
|
+
<aside class="sidebar">
|
|
81
|
+
<div class="brand">🔥 Dashboard</div>
|
|
82
|
+
<div class="tab active" onclick="showPage('overview')">📊 Overview</div>
|
|
83
|
+
<div class="tab" onclick="showPage('reports')">📑 Reports</div>
|
|
84
|
+
<div class="tab" onclick="showPage('settings')">⚙️ Settings</div>
|
|
85
|
+
<div class="tab" onclick="showPage('profile')">👤 Profile</div>
|
|
86
|
+
</aside>
|
|
87
|
+
|
|
88
|
+
<main class="main">
|
|
89
|
+
<header id="pageTitle">Overview</header>
|
|
90
|
+
<section id="pageContent">
|
|
91
|
+
<p>Welcome to the dashboard. Select a tab on the left to switch content.</p>
|
|
92
|
+
</section>
|
|
93
|
+
</main>
|
|
94
|
+
|
|
95
|
+
<script>
|
|
96
|
+
const tabs = document.querySelectorAll('.tab');
|
|
97
|
+
const pageTitle = document.getElementById('pageTitle');
|
|
98
|
+
const pageContent = document.getElementById('pageContent');
|
|
99
|
+
|
|
100
|
+
function showPage(name) {
|
|
101
|
+
tabs.forEach(tab => tab.classList.remove('active'));
|
|
102
|
+
const clicked = [...tabs].find(t => t.textContent.includes(name[0].toUpperCase() + name.slice(1)));
|
|
103
|
+
if (clicked) clicked.classList.add('active');
|
|
104
|
+
|
|
105
|
+
pageTitle.textContent = name.charAt(0).toUpperCase() + name.slice(1);
|
|
106
|
+
pageContent.innerHTML = {
|
|
107
|
+
overview: "<p>Dashboard overview metrics go here.</p>",
|
|
108
|
+
reports: "<p>Reports section with charts and data tables.</p>",
|
|
109
|
+
settings: "<p>User and system settings here.</p>",
|
|
110
|
+
profile: "<p>Profile information and account settings.</p>"
|
|
111
|
+
}[name];
|
|
112
|
+
}
|
|
113
|
+
</script>
|
|
114
|
+
</body>
|
|
115
|
+
</html>
|