@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.
Files changed (153) hide show
  1. package/bootstrap/deploy.localdev.yml +44 -3
  2. package/bootstrap/secrets.localdev.yml +20 -5
  3. package/bootstrap/services/control/src/config.js +4 -0
  4. package/bootstrap/services/notification/Dockerfile +2 -2
  5. package/bootstrap/services/notification/package.json +14 -32
  6. package/bootstrap/services/notification/src/app.js +50 -48
  7. package/bootstrap/services/notification/src/commands/notification.handler.js +96 -0
  8. package/bootstrap/services/notification/src/config.js +55 -12
  9. package/bootstrap/services/notification/src/controllers/http.controller.js +87 -0
  10. package/bootstrap/services/notification/src/controllers/message.controller.js +39 -70
  11. package/bootstrap/services/notification/src/db/migrations/01-init.js +50 -0
  12. package/bootstrap/services/notification/src/db/migrations/02-notification-service-init.js +23 -0
  13. package/bootstrap/services/notification/src/policies/notification.policy.js +49 -0
  14. package/bootstrap/services/notification/src/schema/notification.schema.js +17 -0
  15. package/bootstrap/services/notification/src/services/notification.service.js +32 -0
  16. package/bootstrap/services/page/Dockerfile +23 -0
  17. package/bootstrap/services/page/package.json +16 -0
  18. package/bootstrap/services/page/src/app.js +50 -0
  19. package/bootstrap/services/page/src/commands/block.handler.js +94 -0
  20. package/bootstrap/services/page/src/commands/page.handler.js +167 -0
  21. package/bootstrap/services/page/src/config.js +62 -0
  22. package/bootstrap/services/page/src/controllers/block.http.controller.js +87 -0
  23. package/bootstrap/services/page/src/controllers/message.controller.js +51 -0
  24. package/bootstrap/services/page/src/controllers/page.http.controller.js +89 -0
  25. package/bootstrap/services/page/src/policies/block.policy.js +50 -0
  26. package/bootstrap/services/page/src/policies/page.policy.js +49 -0
  27. package/bootstrap/services/page/src/schema/page.schema.js +139 -0
  28. package/bootstrap/services/page/src/services/block.service.js +83 -0
  29. package/bootstrap/services/page/src/services/page.service.js +83 -0
  30. package/bootstrap/services/portal/Dockerfile +20 -0
  31. package/bootstrap/services/portal/README.md +73 -0
  32. package/bootstrap/services/portal/index.html +13 -0
  33. package/bootstrap/services/portal/nginx.conf +5 -0
  34. package/bootstrap/services/portal/package.json +33 -0
  35. package/bootstrap/services/portal/public/vite.svg +1 -0
  36. package/bootstrap/services/portal/react-router.config.js +7 -0
  37. package/bootstrap/services/portal/src/App.jsx +16 -0
  38. package/bootstrap/services/portal/src/assets/gnar-engine-white-logo.svg +9 -0
  39. package/bootstrap/services/portal/src/assets/icon-agent.svg +6 -0
  40. package/bootstrap/services/portal/src/assets/icon-cog.svg +4 -0
  41. package/bootstrap/services/portal/src/assets/icon-delete.svg +3 -0
  42. package/bootstrap/services/portal/src/assets/icon-home.svg +3 -0
  43. package/bootstrap/services/portal/src/assets/icon-padlock.svg +3 -0
  44. package/bootstrap/services/portal/src/assets/icon-page.svg +6 -0
  45. package/bootstrap/services/portal/src/assets/icon-reports.svg +3 -0
  46. package/bootstrap/services/portal/src/assets/icon-user.svg +3 -0
  47. package/bootstrap/services/portal/src/assets/icon-users.svg +3 -0
  48. package/bootstrap/services/portal/src/assets/login-green-rad-back-1.jpg +0 -0
  49. package/bootstrap/services/portal/src/assets/react.svg +1 -0
  50. package/bootstrap/services/portal/src/components/CrudList/CrudList.jsx +85 -0
  51. package/bootstrap/services/portal/src/components/CrudList/CrudList.less +59 -0
  52. package/bootstrap/services/portal/src/components/CustomSelect/CustomSelect.jsx +81 -0
  53. package/bootstrap/services/portal/src/components/LoginForm/LoginForm.jsx +58 -0
  54. package/bootstrap/services/portal/src/components/PageBlockSwitch/PageBlockSwitch.jsx +129 -0
  55. package/bootstrap/services/portal/src/components/Sidebar/Sidebar.jsx +33 -0
  56. package/bootstrap/services/portal/src/components/Sidebar/Sidebar.less +37 -0
  57. package/bootstrap/services/portal/src/components/Topbar/Topbar.jsx +19 -0
  58. package/bootstrap/services/portal/src/components/Topbar/Topbar.less +22 -0
  59. package/bootstrap/services/portal/src/components/UserInfo/UserInfo.jsx +33 -0
  60. package/bootstrap/services/portal/src/components/UserInfo/UserInfo.less +21 -0
  61. package/bootstrap/services/portal/src/css/style.css +711 -0
  62. package/bootstrap/services/portal/src/data/pages.data.js +10 -0
  63. package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.jsx +65 -0
  64. package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.less +102 -0
  65. package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.jsx +115 -0
  66. package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.less +43 -0
  67. package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.jsx +124 -0
  68. package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.less +0 -0
  69. package/bootstrap/services/portal/src/elements/Repeater/Repeater.jsx +52 -0
  70. package/bootstrap/services/portal/src/elements/Repeater/Repeater.less +70 -0
  71. package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.jsx +18 -0
  72. package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.less +37 -0
  73. package/bootstrap/services/portal/src/elements/SaveButton/SaveButton.jsx +45 -0
  74. package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.jsx +63 -0
  75. package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.less +23 -0
  76. package/bootstrap/services/portal/src/elements/TextInput/TextInput.jsx +17 -0
  77. package/bootstrap/services/portal/src/layouts/Card/Card.jsx +15 -0
  78. package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.jsx +29 -0
  79. package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.less +49 -0
  80. package/bootstrap/services/portal/src/main.jsx +51 -0
  81. package/bootstrap/services/portal/src/pages/BlockSinglePage/BlockSinglePage.jsx +277 -0
  82. package/bootstrap/services/portal/src/pages/BlocksPage/BlocksPage.jsx +23 -0
  83. package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.jsx +11 -0
  84. package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.less +0 -0
  85. package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.jsx +21 -0
  86. package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.less +51 -0
  87. package/bootstrap/services/portal/src/pages/PageSinglePage/PageSinglePage.jsx +338 -0
  88. package/bootstrap/services/portal/src/pages/PagesPage/PagesPage.jsx +23 -0
  89. package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.jsx +9 -0
  90. package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.less +0 -0
  91. package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.jsx +25 -0
  92. package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.less +0 -0
  93. package/bootstrap/services/portal/src/services/block.js +28 -0
  94. package/bootstrap/services/portal/src/services/client.js +70 -0
  95. package/bootstrap/services/portal/src/services/gravatar.js +14 -0
  96. package/bootstrap/services/portal/src/services/page.js +28 -0
  97. package/bootstrap/services/portal/src/services/storage.js +62 -0
  98. package/bootstrap/services/portal/src/services/user.js +41 -0
  99. package/bootstrap/services/portal/src/slices/authSlice.js +101 -0
  100. package/bootstrap/services/portal/src/store/configureStore.js +10 -0
  101. package/bootstrap/services/portal/src/style/cards.less +57 -0
  102. package/bootstrap/services/portal/src/style/global.less +204 -0
  103. package/bootstrap/services/portal/src/style/icons.less +21 -0
  104. package/bootstrap/services/portal/src/style/inputs.less +52 -0
  105. package/bootstrap/services/portal/src/style/main.less +28 -0
  106. package/bootstrap/services/portal/src/utils/utils.js +9 -0
  107. package/bootstrap/services/portal/vite.config.js +12 -0
  108. package/bootstrap/services/user/src/app.js +6 -1
  109. package/bootstrap/services/user/src/commands/user.handler.js +35 -21
  110. package/bootstrap/services/user/src/config.js +5 -1
  111. package/bootstrap/services/user/src/policies/user.policy.js +3 -1
  112. package/bootstrap/services/user/src/tests/commands/user.test.js +31 -0
  113. package/install-from-clone.sh +30 -0
  114. package/package.json +1 -1
  115. package/src/cli.js +2 -0
  116. package/src/config.js +8 -0
  117. package/src/dev/commands.js +11 -3
  118. package/src/dev/dev.service.js +164 -64
  119. package/src/helpers/helpers.js +24 -0
  120. package/src/profiles/command.js +41 -0
  121. package/src/profiles/profiles.client.js +23 -0
  122. package/src/provisioner/Dockerfile +27 -0
  123. package/src/provisioner/package.json +19 -0
  124. package/src/provisioner/src/app.js +56 -0
  125. package/src/provisioner/src/services/mongodb.js +58 -0
  126. package/src/provisioner/src/services/mysql.js +51 -0
  127. package/src/provisioner/src/services/secrets.js +84 -0
  128. package/src/scaffolder/commands.js +58 -2
  129. package/src/scaffolder/scaffolder.handler.js +164 -72
  130. package/templates/entity/src/commands/{{entityName}}.handler.js.hbs +94 -0
  131. package/templates/entity/src/controllers/{{entityName}}.http.controller.js.hbs +87 -0
  132. package/templates/entity/src/mysql.db/migrations/03-{{entityName}}-entity-init.js.hbs +23 -0
  133. package/templates/entity/src/policies/{{entityName}}.policy.js.hbs +49 -0
  134. package/templates/entity/src/schema/{{entityName}}.schema.js.hbs +17 -0
  135. package/templates/entity/src/services/mongodb.{{entityName}}.service.js.hbs +70 -0
  136. package/templates/entity/src/services/mysql.{{entityName}}.service.js.hbs +27 -0
  137. package/templates/service/src/app.js.hbs +12 -1
  138. package/templates/service/src/commands/{{serviceName}}.handler.js.hbs +1 -1
  139. package/templates/service/src/mongodb.config.js.hbs +5 -1
  140. package/templates/service/src/mysql.config.js.hbs +4 -0
  141. package/bootstrap/services/notification/Dockerfile.prod +0 -37
  142. package/bootstrap/services/notification/README.md +0 -3
  143. package/bootstrap/services/notification/src/commands/command-bus.js +0 -20
  144. package/bootstrap/services/notification/src/commands/handlers/control.handler.js +0 -18
  145. package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +0 -157
  146. package/bootstrap/services/notification/src/services/logger.service.js +0 -16
  147. package/bootstrap/services/notification/src/services/ses.service.js +0 -23
  148. package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +0 -136
  149. package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +0 -87
  150. package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +0 -132
  151. package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +0 -77
  152. package/bootstrap/services/user/src/tests/user.test.js +0 -126
  153. /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
- - control-db
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
- - user-db
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: control-db
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: user-db
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 4000
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
- "name": "gnar_engine_notification",
3
- "version": "1.0.0",
4
- "description": "Gnar Engine - Notification Service",
5
- "type": "module",
6
- "scripts": {
7
- "start": "node ./src/app.js",
8
- "start:dev": "nodemon --watch ./src ./src/app.js",
9
- "test": "NODE_OPTIONS='--experimental-vm-modules' jest --watchAll --verbose"
10
- },
11
- "author": "Gnar Software Ltd.",
12
- "license": "ISC",
13
- "dependencies": {
14
- "@aws-sdk/client-ses": "^3.806.0",
15
- "ajv": "^8.17.1",
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 dotenv from 'dotenv';
2
- import { logger } from './services/logger.service.js';
3
- import { commandBus } from './commands/command-bus.js';
4
- import { messageController } from './controllers/message.controller.js';
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
- * @function startServer
23
- * @description Initializes and starts the Fastify server.
7
+ * Initialise service
24
8
  */
25
- const startService = async () => {
26
-
27
- // Register commands
28
- commandBus.register('internalHealthCheck', internalHealthCheck);
29
- commandBus.register('sendNotification', sendNotification);
30
-
31
- // Init controllers and error handlers
32
- messageController.init();
33
-
34
- // Register with control service
35
- try {
36
- await messageAwaitResponse('controlService', {
37
- method: 'registerService',
38
- data: {
39
- service: {
40
- name: 'notificationService'
41
- }
42
- }
43
- });
44
- } catch (error) {
45
- logger.info('No response from the control service when registering as a service');
46
- }
47
-
48
- };
49
-
50
- // Entry point
51
- startService();
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
- * Email sending service
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 emailSendingService = 'SES';
4
+ export const config = {
5
+ // service name
6
+ serviceName: 'notificationService',
11
7
 
12
- /**
13
- * Email header logo url
14
- */
15
- export const emailHeaderLogoUrl = 'https://admin.gnarengine.com/public/gnarengine-logo-black.png';
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 { commandBus } from '../commands/command-bus.js';
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
- // Configuration
6
- const queueName = 'notificationServiceQueue';
7
- const prefetch = 3;
3
+ export const messageHandlers = {
8
4
 
9
- export const messageController = {
10
- handleMessage: async function (msg, channel) {
11
- if (!msg) return;
12
-
13
- const payload = JSON.parse(msg.content.toString());
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
- switch (payload.method) {
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
- // Handler for the health check method
49
- async handleHealthCheck(msg, channel) {
50
- try {
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
- // Handler for unknown methods
62
- async handleMethodNotFound(msg, channel) {
63
- try {
64
- channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ error: 'Method not found' })), {
65
- correlationId: msg.properties.correlationId,
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
- // Initialize RabbitMQ and consumers
75
- init: async function () {
76
- await initializeRabbitMQ(
77
- queueName,
78
- prefetch,
79
- this.handleMessage.bind(this)
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
+ };