@gnar-engine/cli 1.0.4 → 1.0.6
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/bootstrap/deploy.localdev.yml +44 -3
- package/bootstrap/secrets.localdev.yml +20 -5
- package/bootstrap/services/control/src/config.js +4 -0
- package/bootstrap/services/notification/Dockerfile +2 -2
- package/bootstrap/services/notification/package.json +14 -32
- package/bootstrap/services/notification/src/app.js +50 -48
- package/bootstrap/services/notification/src/commands/notification.handler.js +96 -0
- package/bootstrap/services/notification/src/config.js +55 -12
- package/bootstrap/services/notification/src/controllers/http.controller.js +87 -0
- package/bootstrap/services/notification/src/controllers/message.controller.js +39 -70
- package/bootstrap/services/notification/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/notification/src/db/migrations/02-notification-service-init.js +23 -0
- package/bootstrap/services/notification/src/policies/notification.policy.js +49 -0
- package/bootstrap/services/notification/src/schema/notification.schema.js +17 -0
- package/bootstrap/services/notification/src/services/notification.service.js +32 -0
- package/bootstrap/services/page/Dockerfile +23 -0
- package/bootstrap/services/page/package.json +16 -0
- package/bootstrap/services/page/src/app.js +50 -0
- package/bootstrap/services/page/src/commands/block.handler.js +94 -0
- package/bootstrap/services/page/src/commands/page.handler.js +167 -0
- package/bootstrap/services/page/src/config.js +62 -0
- package/bootstrap/services/page/src/controllers/block.http.controller.js +87 -0
- package/bootstrap/services/page/src/controllers/message.controller.js +51 -0
- package/bootstrap/services/page/src/controllers/page.http.controller.js +89 -0
- package/bootstrap/services/page/src/policies/block.policy.js +50 -0
- package/bootstrap/services/page/src/policies/page.policy.js +49 -0
- package/bootstrap/services/page/src/schema/page.schema.js +139 -0
- package/bootstrap/services/page/src/services/block.service.js +83 -0
- package/bootstrap/services/page/src/services/page.service.js +83 -0
- package/bootstrap/services/portal/Dockerfile +20 -0
- package/bootstrap/services/portal/README.md +73 -0
- package/bootstrap/services/portal/index.html +13 -0
- package/bootstrap/services/portal/nginx.conf +5 -0
- package/bootstrap/services/portal/package.json +33 -0
- package/bootstrap/services/portal/public/vite.svg +1 -0
- package/bootstrap/services/portal/react-router.config.js +7 -0
- package/bootstrap/services/portal/src/App.jsx +16 -0
- package/bootstrap/services/portal/src/assets/gnar-engine-white-logo.svg +9 -0
- package/bootstrap/services/portal/src/assets/icon-agent.svg +6 -0
- package/bootstrap/services/portal/src/assets/icon-cog.svg +4 -0
- package/bootstrap/services/portal/src/assets/icon-delete.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-home.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-padlock.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-page.svg +6 -0
- package/bootstrap/services/portal/src/assets/icon-reports.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-user.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-users.svg +3 -0
- package/bootstrap/services/portal/src/assets/login-green-rad-back-1.jpg +0 -0
- package/bootstrap/services/portal/src/assets/react.svg +1 -0
- package/bootstrap/services/portal/src/components/CrudList/CrudList.jsx +85 -0
- package/bootstrap/services/portal/src/components/CrudList/CrudList.less +59 -0
- package/bootstrap/services/portal/src/components/CustomSelect/CustomSelect.jsx +81 -0
- package/bootstrap/services/portal/src/components/LoginForm/LoginForm.jsx +58 -0
- package/bootstrap/services/portal/src/components/PageBlockSwitch/PageBlockSwitch.jsx +129 -0
- package/bootstrap/services/portal/src/components/Sidebar/Sidebar.jsx +33 -0
- package/bootstrap/services/portal/src/components/Sidebar/Sidebar.less +37 -0
- package/bootstrap/services/portal/src/components/Topbar/Topbar.jsx +19 -0
- package/bootstrap/services/portal/src/components/Topbar/Topbar.less +22 -0
- package/bootstrap/services/portal/src/components/UserInfo/UserInfo.jsx +33 -0
- package/bootstrap/services/portal/src/components/UserInfo/UserInfo.less +21 -0
- package/bootstrap/services/portal/src/css/style.css +711 -0
- package/bootstrap/services/portal/src/data/pages.data.js +10 -0
- package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.jsx +65 -0
- package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.less +102 -0
- package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.jsx +115 -0
- package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.less +43 -0
- package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.jsx +124 -0
- package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.less +0 -0
- package/bootstrap/services/portal/src/elements/Repeater/Repeater.jsx +52 -0
- package/bootstrap/services/portal/src/elements/Repeater/Repeater.less +70 -0
- package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.jsx +18 -0
- package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.less +37 -0
- package/bootstrap/services/portal/src/elements/SaveButton/SaveButton.jsx +45 -0
- package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.jsx +63 -0
- package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.less +23 -0
- package/bootstrap/services/portal/src/elements/TextInput/TextInput.jsx +17 -0
- package/bootstrap/services/portal/src/layouts/Card/Card.jsx +15 -0
- package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.jsx +29 -0
- package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.less +49 -0
- package/bootstrap/services/portal/src/main.jsx +51 -0
- package/bootstrap/services/portal/src/pages/BlockSinglePage/BlockSinglePage.jsx +277 -0
- package/bootstrap/services/portal/src/pages/BlocksPage/BlocksPage.jsx +23 -0
- package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.jsx +11 -0
- package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.less +0 -0
- package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.jsx +21 -0
- package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.less +51 -0
- package/bootstrap/services/portal/src/pages/PageSinglePage/PageSinglePage.jsx +338 -0
- package/bootstrap/services/portal/src/pages/PagesPage/PagesPage.jsx +23 -0
- package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.jsx +9 -0
- package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.less +0 -0
- package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.jsx +25 -0
- package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.less +0 -0
- package/bootstrap/services/portal/src/services/block.js +28 -0
- package/bootstrap/services/portal/src/services/client.js +70 -0
- package/bootstrap/services/portal/src/services/gravatar.js +14 -0
- package/bootstrap/services/portal/src/services/page.js +28 -0
- package/bootstrap/services/portal/src/services/storage.js +62 -0
- package/bootstrap/services/portal/src/services/user.js +41 -0
- package/bootstrap/services/portal/src/slices/authSlice.js +101 -0
- package/bootstrap/services/portal/src/store/configureStore.js +10 -0
- package/bootstrap/services/portal/src/style/cards.less +57 -0
- package/bootstrap/services/portal/src/style/global.less +204 -0
- package/bootstrap/services/portal/src/style/icons.less +21 -0
- package/bootstrap/services/portal/src/style/inputs.less +52 -0
- package/bootstrap/services/portal/src/style/main.less +28 -0
- package/bootstrap/services/portal/src/utils/utils.js +9 -0
- package/bootstrap/services/portal/vite.config.js +12 -0
- package/bootstrap/services/user/src/app.js +6 -1
- package/bootstrap/services/user/src/commands/user.handler.js +35 -21
- package/bootstrap/services/user/src/config.js +5 -1
- package/bootstrap/services/user/src/policies/user.policy.js +3 -1
- package/bootstrap/services/user/src/tests/commands/user.test.js +31 -0
- package/install-from-clone.sh +30 -0
- package/package.json +1 -1
- package/src/cli.js +2 -0
- package/src/config.js +8 -0
- package/src/dev/commands.js +11 -3
- package/src/dev/dev.service.js +164 -64
- package/src/helpers/helpers.js +24 -0
- package/src/profiles/command.js +41 -0
- package/src/profiles/profiles.client.js +23 -0
- package/src/provisioner/Dockerfile +27 -0
- package/src/provisioner/package.json +19 -0
- package/src/provisioner/src/app.js +56 -0
- package/src/provisioner/src/services/mongodb.js +58 -0
- package/src/provisioner/src/services/mysql.js +51 -0
- package/src/provisioner/src/services/secrets.js +84 -0
- package/src/scaffolder/commands.js +58 -2
- package/src/scaffolder/scaffolder.handler.js +164 -72
- package/templates/entity/src/commands/{{entityName}}.handler.js.hbs +94 -0
- package/templates/entity/src/controllers/{{entityName}}.http.controller.js.hbs +87 -0
- package/templates/entity/src/mysql.db/migrations/03-{{entityName}}-entity-init.js.hbs +23 -0
- package/templates/entity/src/policies/{{entityName}}.policy.js.hbs +49 -0
- package/templates/entity/src/schema/{{entityName}}.schema.js.hbs +17 -0
- package/templates/entity/src/services/mongodb.{{entityName}}.service.js.hbs +70 -0
- package/templates/entity/src/services/mysql.{{entityName}}.service.js.hbs +27 -0
- package/templates/service/src/app.js.hbs +12 -1
- package/templates/service/src/commands/{{serviceName}}.handler.js.hbs +1 -1
- package/templates/service/src/mongodb.config.js.hbs +5 -1
- package/templates/service/src/mysql.config.js.hbs +4 -0
- package/bootstrap/services/notification/Dockerfile.prod +0 -37
- package/bootstrap/services/notification/README.md +0 -3
- package/bootstrap/services/notification/src/commands/command-bus.js +0 -20
- package/bootstrap/services/notification/src/commands/handlers/control.handler.js +0 -18
- package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +0 -157
- package/bootstrap/services/notification/src/services/logger.service.js +0 -16
- package/bootstrap/services/notification/src/services/ses.service.js +0 -23
- package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +0 -136
- package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +0 -87
- package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +0 -132
- package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +0 -77
- package/bootstrap/services/user/src/tests/user.test.js +0 -126
- /package/bootstrap/services/{notification/src/tests/notification.test.js → portal/src/components/CustomSelect/CustomSelect.less} +0 -0
|
@@ -13,7 +13,7 @@ config:
|
|
|
13
13
|
min_tasks: 1
|
|
14
14
|
max_tasks: 1
|
|
15
15
|
depends_on:
|
|
16
|
-
-
|
|
16
|
+
- db-mysql
|
|
17
17
|
command:
|
|
18
18
|
- npm
|
|
19
19
|
- run
|
|
@@ -28,11 +28,52 @@ config:
|
|
|
28
28
|
min_tasks: 1
|
|
29
29
|
max_tasks: 1
|
|
30
30
|
depends_on:
|
|
31
|
-
-
|
|
31
|
+
- db-mysql
|
|
32
32
|
command:
|
|
33
33
|
- npm
|
|
34
34
|
- run
|
|
35
35
|
- start:dev
|
|
36
36
|
ports:
|
|
37
37
|
- '4002:3000'
|
|
38
|
-
|
|
38
|
+
- name: portal
|
|
39
|
+
listener_rules:
|
|
40
|
+
paths:
|
|
41
|
+
- /portal
|
|
42
|
+
min_tasks: 1
|
|
43
|
+
max_tasks: 1
|
|
44
|
+
command:
|
|
45
|
+
- npm
|
|
46
|
+
- run
|
|
47
|
+
- dev
|
|
48
|
+
- '-- --host'
|
|
49
|
+
ports:
|
|
50
|
+
- '4003:5173'
|
|
51
|
+
- name: page
|
|
52
|
+
listener_rules:
|
|
53
|
+
paths:
|
|
54
|
+
- /pages
|
|
55
|
+
- /blocks
|
|
56
|
+
min_tasks: 1
|
|
57
|
+
max_tasks: 1
|
|
58
|
+
command:
|
|
59
|
+
- npm
|
|
60
|
+
- run
|
|
61
|
+
- start:dev
|
|
62
|
+
ports:
|
|
63
|
+
- '4004:3000'
|
|
64
|
+
depends_on:
|
|
65
|
+
- db-mongo
|
|
66
|
+
- name: notification
|
|
67
|
+
listener_rules:
|
|
68
|
+
paths:
|
|
69
|
+
- /notification
|
|
70
|
+
min_tasks: 1
|
|
71
|
+
max_tasks: 1
|
|
72
|
+
command:
|
|
73
|
+
- npm
|
|
74
|
+
- run
|
|
75
|
+
- start:dev
|
|
76
|
+
ports:
|
|
77
|
+
- '4005:3000'
|
|
78
|
+
depends_on:
|
|
79
|
+
- db-mysql
|
|
@@ -1,22 +1,37 @@
|
|
|
1
|
+
provision:
|
|
2
|
+
MYSQL_ROOT_PASSWORD: GExtMVydbpIihS5f
|
|
3
|
+
MONGO_ROOT_PASSWORD: E7lVrilnp07gIxcX
|
|
1
4
|
global:
|
|
2
5
|
RABBITMQ_URL: amqp://gnar_engine_rabbit_user:gn4rlyRaB81Tw4sh@rabbitmq:5672
|
|
3
6
|
RABBITMQ_USER: gnar_engine_rabbit_user
|
|
4
7
|
RABBITMQ_PASS: gn4rlyRaB81Tw4sh
|
|
8
|
+
UPLOADS_URL: https://gnar-engine-test.s3.eu-west-2.amazonaws.com
|
|
9
|
+
S3_BUCKET: gnar-engine-test
|
|
10
|
+
AWS_REGION: eu-west-2
|
|
11
|
+
AWS_ACCESS_KEY_ID: null
|
|
12
|
+
AWS_SECRET_ACCESS_KEY: null
|
|
5
13
|
services:
|
|
6
14
|
control:
|
|
7
|
-
MYSQL_HOST:
|
|
15
|
+
MYSQL_HOST: db-mysql
|
|
8
16
|
MYSQL_DATABASE: ge_control_db
|
|
9
17
|
MYSQL_USER: ge_control_db_user
|
|
10
18
|
MYSQL_PASSWORD: bL6I1ABJFDPO3JAR
|
|
11
|
-
MYSQL_RANDOM_ROOT_PASSWORD: GExtMVydbpIihS5f
|
|
12
19
|
user:
|
|
13
|
-
MYSQL_HOST:
|
|
20
|
+
MYSQL_HOST: db-mysql
|
|
14
21
|
MYSQL_DATABASE: ge_user_db
|
|
15
22
|
MYSQL_USER: ge_user_db_user
|
|
16
23
|
MYSQL_PASSWORD: 7cm0crGDTaJy9Sa2
|
|
17
|
-
MYSQL_RANDOM_ROOT_PASSWORD: APJW3b7teaRHTvcv
|
|
18
24
|
ROOT_ADMIN_EMAIL: adam@gnar.co.uk
|
|
19
25
|
ROOT_ADMIN_USERNAME: engineadmin
|
|
20
26
|
ROOT_ADMIN_PASSWORD: MOF4TWiFh4TXOHcL
|
|
21
27
|
ROOT_ADMIN_API_KEY: JPpmhQN0bs8f5x2jN5ypCU6Ww6VETrW2
|
|
22
|
-
|
|
28
|
+
page:
|
|
29
|
+
MONGO_HOST: db-mongo
|
|
30
|
+
MONGO_DATABASE: page_db
|
|
31
|
+
MONGO_USER: page_user
|
|
32
|
+
MONGO_PASSWORD: Q2atGxh21VqNIrZO
|
|
33
|
+
notification:
|
|
34
|
+
MYSQL_USER: notification_user
|
|
35
|
+
MYSQL_PASSWORD: hmubUB5FBVf4V7dF
|
|
36
|
+
MYSQL_DATABASE: notification_db
|
|
37
|
+
MYSQL_HOST: db-mysql
|
|
@@ -6,6 +6,10 @@ export const config = {
|
|
|
6
6
|
// service name
|
|
7
7
|
serviceName: 'controlService',
|
|
8
8
|
|
|
9
|
+
// environment
|
|
10
|
+
environment: process.env.CONTROL_NODE_ENV || 'dev',
|
|
11
|
+
runTests: process.env.CONTROL_RUN_TESTS || false,
|
|
12
|
+
|
|
9
13
|
// microservice | modular-monolith
|
|
10
14
|
architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
|
|
11
15
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Dockerfile for Notification Service
|
|
2
|
-
FROM node:20-alpine
|
|
2
|
+
FROM node:20-alpine
|
|
3
3
|
|
|
4
4
|
# Set the working directory
|
|
5
5
|
WORKDIR /usr/gnar_engine/app
|
|
@@ -17,7 +17,7 @@ RUN npm install -g nodemon
|
|
|
17
17
|
RUN npm install
|
|
18
18
|
|
|
19
19
|
# Expose the port the service will run on
|
|
20
|
-
EXPOSE
|
|
20
|
+
EXPOSE 3000
|
|
21
21
|
|
|
22
22
|
# Start the application
|
|
23
23
|
CMD ["nodemon", "--watch", "./gnar_engine", "./gnar_engine/app.js"]
|
|
@@ -1,34 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"ajv-formats": "^3.0.1",
|
|
17
|
-
"dotenv": "^16.4.7",
|
|
18
|
-
"nodemailer": "^7.0.3",
|
|
19
|
-
"path": "^0.12.7",
|
|
20
|
-
"pino": "^9.6.0",
|
|
21
|
-
"pino-pretty": "^13.0.0",
|
|
22
|
-
"uuid": "^11.1.0",
|
|
23
|
-
"handlebars": "^4.7.8"
|
|
24
|
-
},
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"@babel/preset-env": "^7.22.20",
|
|
27
|
-
"jest": "^29.7.0",
|
|
28
|
-
"nodemon": "^3.1.9",
|
|
29
|
-
"supertest": "^6.3.3"
|
|
30
|
-
},
|
|
31
|
-
"workspaces": [
|
|
32
|
-
"./Lib/*"
|
|
33
|
-
]
|
|
2
|
+
"name": "notification-service",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Notification microservice for Gnar Engine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node ./src/app.js",
|
|
8
|
+
"start:dev": "nodemon --watch ./src ./src/app.js",
|
|
9
|
+
"test": "jest --watchAll --verbose"
|
|
10
|
+
},
|
|
11
|
+
"author": "Gnar Software Ltd.",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@gnar-engine/core": "^1.0.1"
|
|
15
|
+
}
|
|
34
16
|
}
|
|
@@ -1,51 +1,53 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { messageAwaitResponse } from '@gnar-engine/message-client';
|
|
6
|
-
import { internalHealthCheck } from './commands/handlers/control.handler.js';
|
|
7
|
-
import { sendNotification } from './commands/handlers/notification.handler.js';
|
|
8
|
-
|
|
9
|
-
dotenv.config({ path: '.env' });
|
|
10
|
-
|
|
11
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
12
|
-
console.error('🚨 Unhandled Rejection at:', promise, '\nReason:', reason);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
process.on('uncaughtException', (err) => {
|
|
17
|
-
console.error('🚨 Uncaught Exception:', err);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
});
|
|
1
|
+
import { message, http, logger, db, registerService, webSockets, test } from '@gnar-engine/core';
|
|
2
|
+
import { config } from './config.js';
|
|
3
|
+
import { messageHandlers } from './controllers/message.controller.js';
|
|
4
|
+
import { httpController as notificationPlatformHttpController } from './controllers/http.controller.js';
|
|
20
5
|
|
|
21
6
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @description Initializes and starts the Fastify server.
|
|
7
|
+
* Initialise service
|
|
24
8
|
*/
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
9
|
+
export const initService = async () => {
|
|
10
|
+
|
|
11
|
+
// Run migrations
|
|
12
|
+
if (config.db.type == 'mysql') {
|
|
13
|
+
db.migrations.runMigrations({config});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Run seeders
|
|
17
|
+
db.seeders.runSeeders({config});
|
|
18
|
+
|
|
19
|
+
// Import command handlers after the command bus is initialised
|
|
20
|
+
await import('./commands/notification.handler.js');
|
|
21
|
+
// Add more handlers as needed
|
|
22
|
+
|
|
23
|
+
// Initialise and register message handlers
|
|
24
|
+
await message.init({
|
|
25
|
+
config: config.message,
|
|
26
|
+
handlers: messageHandlers
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Initialise websocket client & server
|
|
30
|
+
await webSockets.init(config.webSockets, config.serviceName);
|
|
31
|
+
|
|
32
|
+
// Register http routes
|
|
33
|
+
await http.registerRoutes({
|
|
34
|
+
controllers: [
|
|
35
|
+
notificationPlatformHttpController,
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Start the HTTP server
|
|
40
|
+
await http.start();
|
|
41
|
+
|
|
42
|
+
// Register service with control service
|
|
43
|
+
await registerService();
|
|
44
|
+
|
|
45
|
+
logger.info('G n a r E n g i n e | Notification Service initialised successfully.');
|
|
46
|
+
|
|
47
|
+
// Tests
|
|
48
|
+
if (config.environment === 'test' && config.runTests) {
|
|
49
|
+
test.runCommandTests({config});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
initService();
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { commands, logger, error } from '@gnar-engine/core';
|
|
2
|
+
import { notification } from '../services/notification.service.js';
|
|
3
|
+
import { config } from '../config.js';
|
|
4
|
+
import { validateNotification } from '../schema/notification.schema.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get single notification
|
|
9
|
+
*/
|
|
10
|
+
commands.register('notificationService.getSingleNotification', async ({id}) => {
|
|
11
|
+
if (id) {
|
|
12
|
+
return await notification.getById({id: id});
|
|
13
|
+
} else {
|
|
14
|
+
throw new error.badRequest('Notification email or id required');
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get many notifications
|
|
20
|
+
*/
|
|
21
|
+
commands.register('notificationService.getManyNotifications', async ({}) => {
|
|
22
|
+
return await notification.getAll();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create notifications
|
|
27
|
+
*/
|
|
28
|
+
commands.register('notificationService.createNotifications', async ({ notifications }) => {
|
|
29
|
+
const validationErrors = [];
|
|
30
|
+
let createdNewNotifications = [];
|
|
31
|
+
|
|
32
|
+
for (const newData of notifications) {
|
|
33
|
+
const { errors } = validateNotification(newData);
|
|
34
|
+
if (errors?.length) {
|
|
35
|
+
validationErrors.push(errors);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const created = await notification.create(newData);
|
|
40
|
+
createdNewNotifications.push(created);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (validationErrors.length) {
|
|
44
|
+
throw new error.badRequest(`Invalid notification data: ${validationErrors}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return createdNewNotifications;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Update notification
|
|
52
|
+
*/
|
|
53
|
+
commands.register('notificationService.updateNotification', async ({id, newNotificationData}) => {
|
|
54
|
+
|
|
55
|
+
const validationErrors = [];
|
|
56
|
+
|
|
57
|
+
if (!id) {
|
|
58
|
+
throw new error.badRequest('Notification ID required');
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const obj = await notification.getById({id: id});
|
|
63
|
+
|
|
64
|
+
if (!obj) {
|
|
65
|
+
throw new error.notFound('Notification not found');
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
delete newNotificationData.id;
|
|
70
|
+
|
|
71
|
+
const { errors } = validateNotificationUpdate(newNotificationData);
|
|
72
|
+
|
|
73
|
+
if (errors?.length) {
|
|
74
|
+
validationErrors.push(errors);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (validationErrors.length) {
|
|
78
|
+
throw new error.badRequest(`Invalid notification data: ${validationErrors}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return await notification.update({
|
|
82
|
+
id: id,
|
|
83
|
+
updatedData: newNotificationData
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Delete notification
|
|
89
|
+
*/
|
|
90
|
+
commands.register('notificationService.deleteNotification', async ({id}) => {
|
|
91
|
+
const obj = await notification.getById({id: id});
|
|
92
|
+
if (!obj) {
|
|
93
|
+
throw new error.notFound('Notification not found');
|
|
94
|
+
}
|
|
95
|
+
return await notification.delete({id: id});
|
|
96
|
+
});
|
|
@@ -1,15 +1,58 @@
|
|
|
1
|
-
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* - Values:
|
|
6
|
-
* - SES (not implemented yet)
|
|
7
|
-
* - SMTP
|
|
8
|
-
* - Direct (not implemented yet)
|
|
2
|
+
* Gnar Engine Service Config
|
|
9
3
|
*/
|
|
10
|
-
export const
|
|
4
|
+
export const config = {
|
|
5
|
+
// service name
|
|
6
|
+
serviceName: 'notificationService',
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
// environment
|
|
9
|
+
environment: process.env.NOTIFICATION_NODE_ENV || 'dev',
|
|
10
|
+
runTests: process.env.NOTIFICATION_RUN_TESTS || false,
|
|
11
|
+
|
|
12
|
+
// microservice | modular-monolith
|
|
13
|
+
architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
|
|
14
|
+
|
|
15
|
+
// web server
|
|
16
|
+
http: {
|
|
17
|
+
allowedOrigins: [],
|
|
18
|
+
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
19
|
+
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
20
|
+
rateLimiting: {
|
|
21
|
+
max: 5,
|
|
22
|
+
timeWindow: '1 minute',
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// database
|
|
27
|
+
db: {
|
|
28
|
+
// type: mongodb | mysql
|
|
29
|
+
type: 'mysql',
|
|
30
|
+
|
|
31
|
+
// MongoDB
|
|
32
|
+
connectionUrl: process.env.NOTIFICATION_MONGO_URL,
|
|
33
|
+
connectionArgs: {},
|
|
34
|
+
|
|
35
|
+
// MySQL
|
|
36
|
+
host: process.env.NOTIFICATION_MYSQL_HOST,
|
|
37
|
+
user: process.env.NOTIFICATION_MYSQL_USER,
|
|
38
|
+
password: process.env.NOTIFICATION_MYSQL_PASSWORD,
|
|
39
|
+
database: process.env.NOTIFICATION_MYSQL_DATABASE,
|
|
40
|
+
connectionLimit: 10,
|
|
41
|
+
queueLimit: 20,
|
|
42
|
+
maxRetries: 5
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// message broker
|
|
46
|
+
message: {
|
|
47
|
+
url: process.env.RABBITMQ_URL,
|
|
48
|
+
queueName: 'notificationServiceQueue',
|
|
49
|
+
prefetch: 20
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
webSockets: {
|
|
53
|
+
reconnectInterval: 5000,
|
|
54
|
+
maxInitialConnectionAttempts: 5
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
hashNameSpace: '',
|
|
58
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { commands } from '@gnar-engine/core';
|
|
2
|
+
import { authorise } from '../policies/notification.policy.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HTTP controller
|
|
6
|
+
*/
|
|
7
|
+
export const httpController = {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get single notification
|
|
11
|
+
*/
|
|
12
|
+
getSingle: {
|
|
13
|
+
method: 'GET',
|
|
14
|
+
url: '/notifications/:id',
|
|
15
|
+
preHandler: async (request, reply) => authorise.getSingle(request, reply),
|
|
16
|
+
handler: async (request, reply) => {
|
|
17
|
+
const params = {
|
|
18
|
+
id: request.params.id
|
|
19
|
+
};
|
|
20
|
+
const result = await commands.execute('getSingleNotification', params);
|
|
21
|
+
reply.code(200).send({ notification: result });
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get multiple notifications
|
|
27
|
+
*/
|
|
28
|
+
getMany: {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
url: '/notifications/',
|
|
31
|
+
preHandler: async (request, reply) => authorise.getMany(request, reply),
|
|
32
|
+
handler: async (request, reply) => {
|
|
33
|
+
const params = {};
|
|
34
|
+
const results = await commands.execute('getManyNotifications', params);
|
|
35
|
+
reply.code(200).send({ notifications: results });
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create new notification
|
|
41
|
+
*/
|
|
42
|
+
create: {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
url: '/notifications/',
|
|
45
|
+
preHandler: async (request, reply) => authorise.create(request, reply),
|
|
46
|
+
handler: async (request, reply) => {
|
|
47
|
+
const params = {
|
|
48
|
+
notifications: [request.body.notification]
|
|
49
|
+
};
|
|
50
|
+
const results = await commands.execute('createNotifications', params);
|
|
51
|
+
reply.code(200).send({ notifications: results });
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Update notification
|
|
57
|
+
*/
|
|
58
|
+
update: {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
url: '/notifications/:id',
|
|
61
|
+
preHandler: async (request, reply) => authorise.update(request, reply),
|
|
62
|
+
handler: async (request, reply) => {
|
|
63
|
+
const params = {
|
|
64
|
+
id: request.params.id,
|
|
65
|
+
newNotificationData: request.body
|
|
66
|
+
};
|
|
67
|
+
const result = await commands.execute('updateNotification', params);
|
|
68
|
+
reply.code(200).send({ notification: result });
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Delete notification
|
|
74
|
+
*/
|
|
75
|
+
delete: {
|
|
76
|
+
method: 'DELETE',
|
|
77
|
+
url: '/notifications/:id',
|
|
78
|
+
preHandler: async (request, reply) => authorise.delete(request, reply),
|
|
79
|
+
handler: async (request, reply) => {
|
|
80
|
+
const params = {
|
|
81
|
+
id: request.params.id
|
|
82
|
+
};
|
|
83
|
+
await commands.execute('deleteNotification', params);
|
|
84
|
+
reply.code(200).send({ message: 'Notification deleted' });
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
@@ -1,82 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { logger } from '../services/logger.service.js';
|
|
3
|
-
import { initializeRabbitMQ } from '@gnar-engine/message-client';
|
|
1
|
+
import { commands } from '@gnar-engine/core';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
const queueName = 'notificationServiceQueue';
|
|
7
|
-
const prefetch = 3;
|
|
3
|
+
export const messageHandlers = {
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (!payload.method) {
|
|
16
|
-
return channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ error: 'Method not found' })), {
|
|
17
|
-
correlationId: msg.properties.correlationId,
|
|
5
|
+
getNotification: async (payload) => {
|
|
6
|
+
let result;
|
|
7
|
+
if (payload.data?.id) {
|
|
8
|
+
result = await commands.execute('getSingleNotification', {
|
|
9
|
+
id: payload.data.id
|
|
18
10
|
});
|
|
11
|
+
} else if (payload.data?.email) {
|
|
12
|
+
result = await commands.execute('getSingleNotification', {
|
|
13
|
+
email: payload.data.email
|
|
14
|
+
});
|
|
15
|
+
} else {
|
|
16
|
+
throw new Error('No notification ID or email provided');
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
case 'sendNotification':
|
|
23
|
-
try {
|
|
24
|
-
const { templateName, to, params, subject } = payload.data;
|
|
25
|
-
await commandBus.execute('sendNotification', { templateName, to, params, subject });
|
|
26
|
-
channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ status: 'ok' })), {
|
|
27
|
-
correlationId: msg.properties.correlationId,
|
|
28
|
-
});
|
|
29
|
-
} catch (error) {
|
|
30
|
-
logger.error("Error sending notification: " + error);
|
|
31
|
-
channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ error: 'Failed to send notification' })), {
|
|
32
|
-
correlationId: msg.properties.correlationId,
|
|
33
|
-
});
|
|
34
|
-
} finally {
|
|
35
|
-
channel.ack(msg);
|
|
36
|
-
}
|
|
37
|
-
break;
|
|
38
|
-
|
|
39
|
-
case 'healthCheck':
|
|
40
|
-
await this.handleHealthCheck(msg, channel);
|
|
41
|
-
break;
|
|
42
|
-
|
|
43
|
-
default:
|
|
44
|
-
await this.handleMethodNotFound(msg, channel);
|
|
18
|
+
if (!result) {
|
|
19
|
+
throw new Error('Notification not found');
|
|
45
20
|
}
|
|
21
|
+
return { notification: result };
|
|
46
22
|
},
|
|
47
23
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ status: 'ok' })), {
|
|
52
|
-
correlationId: msg.properties.correlationId,
|
|
53
|
-
});
|
|
54
|
-
} catch (error) {
|
|
55
|
-
logger.error("Error running health check:", error);
|
|
56
|
-
} finally {
|
|
57
|
-
channel.ack(msg);
|
|
58
|
-
}
|
|
24
|
+
getManyNotifications: async (payload) => {
|
|
25
|
+
const results = await commands.execute('getManyNotifications', {});
|
|
26
|
+
return { notifications: results };
|
|
59
27
|
},
|
|
60
28
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
67
|
-
} catch (error) {
|
|
68
|
-
logger.error("Error handling method not found:", error);
|
|
69
|
-
} finally {
|
|
70
|
-
channel.ack(msg);
|
|
71
|
-
}
|
|
29
|
+
createNotification: async (payload) => {
|
|
30
|
+
const results = await commands.execute('createNotifications', {
|
|
31
|
+
notifications: [payload.data.notification]
|
|
32
|
+
});
|
|
33
|
+
return { notifications: results };
|
|
72
34
|
},
|
|
73
35
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
);
|
|
36
|
+
updateNotification: async (payload) => {
|
|
37
|
+
const result = await commands.execute('updateNotification', {
|
|
38
|
+
id: payload.data.id,
|
|
39
|
+
newNotificationData: payload.data
|
|
40
|
+
});
|
|
41
|
+
return { notification: result };
|
|
81
42
|
},
|
|
82
|
-
|
|
43
|
+
|
|
44
|
+
deleteNotification: async (payload) => {
|
|
45
|
+
await commands.execute('deleteNotification', {
|
|
46
|
+
id: payload.data.id
|
|
47
|
+
});
|
|
48
|
+
return { message: 'Notification deleted' };
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
};
|