@gnar-engine/cli 1.0.4 → 1.0.5

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 (119) hide show
  1. package/bootstrap/deploy.localdev.yml +30 -3
  2. package/bootstrap/secrets.localdev.yml +15 -4
  3. package/bootstrap/services/control/src/config.js +4 -0
  4. package/bootstrap/services/page/Dockerfile +23 -0
  5. package/bootstrap/services/page/package.json +16 -0
  6. package/bootstrap/services/page/src/app.js +50 -0
  7. package/bootstrap/services/page/src/commands/block.handler.js +94 -0
  8. package/bootstrap/services/page/src/commands/page.handler.js +167 -0
  9. package/bootstrap/services/page/src/config.js +62 -0
  10. package/bootstrap/services/page/src/controllers/block.http.controller.js +87 -0
  11. package/bootstrap/services/page/src/controllers/message.controller.js +51 -0
  12. package/bootstrap/services/page/src/controllers/page.http.controller.js +89 -0
  13. package/bootstrap/services/page/src/policies/block.policy.js +50 -0
  14. package/bootstrap/services/page/src/policies/page.policy.js +49 -0
  15. package/bootstrap/services/page/src/schema/page.schema.js +139 -0
  16. package/bootstrap/services/page/src/services/block.service.js +83 -0
  17. package/bootstrap/services/page/src/services/page.service.js +83 -0
  18. package/bootstrap/services/portal/Dockerfile +20 -0
  19. package/bootstrap/services/portal/README.md +73 -0
  20. package/bootstrap/services/portal/index.html +13 -0
  21. package/bootstrap/services/portal/nginx.conf +5 -0
  22. package/bootstrap/services/portal/package.json +33 -0
  23. package/bootstrap/services/portal/public/vite.svg +1 -0
  24. package/bootstrap/services/portal/react-router.config.js +7 -0
  25. package/bootstrap/services/portal/src/App.jsx +16 -0
  26. package/bootstrap/services/portal/src/assets/gnar-engine-white-logo.svg +9 -0
  27. package/bootstrap/services/portal/src/assets/icon-agent.svg +6 -0
  28. package/bootstrap/services/portal/src/assets/icon-cog.svg +4 -0
  29. package/bootstrap/services/portal/src/assets/icon-delete.svg +3 -0
  30. package/bootstrap/services/portal/src/assets/icon-home.svg +3 -0
  31. package/bootstrap/services/portal/src/assets/icon-padlock.svg +3 -0
  32. package/bootstrap/services/portal/src/assets/icon-page.svg +6 -0
  33. package/bootstrap/services/portal/src/assets/icon-reports.svg +3 -0
  34. package/bootstrap/services/portal/src/assets/icon-user.svg +3 -0
  35. package/bootstrap/services/portal/src/assets/icon-users.svg +3 -0
  36. package/bootstrap/services/portal/src/assets/login-green-rad-back-1.jpg +0 -0
  37. package/bootstrap/services/portal/src/assets/react.svg +1 -0
  38. package/bootstrap/services/portal/src/components/CrudList/CrudList.jsx +85 -0
  39. package/bootstrap/services/portal/src/components/CrudList/CrudList.less +59 -0
  40. package/bootstrap/services/portal/src/components/CustomSelect/CustomSelect.jsx +81 -0
  41. package/bootstrap/services/portal/src/components/CustomSelect/CustomSelect.less +0 -0
  42. package/bootstrap/services/portal/src/components/LoginForm/LoginForm.jsx +58 -0
  43. package/bootstrap/services/portal/src/components/PageBlockSwitch/PageBlockSwitch.jsx +129 -0
  44. package/bootstrap/services/portal/src/components/Sidebar/Sidebar.jsx +33 -0
  45. package/bootstrap/services/portal/src/components/Sidebar/Sidebar.less +37 -0
  46. package/bootstrap/services/portal/src/components/Topbar/Topbar.jsx +19 -0
  47. package/bootstrap/services/portal/src/components/Topbar/Topbar.less +22 -0
  48. package/bootstrap/services/portal/src/components/UserInfo/UserInfo.jsx +33 -0
  49. package/bootstrap/services/portal/src/components/UserInfo/UserInfo.less +21 -0
  50. package/bootstrap/services/portal/src/css/style.css +711 -0
  51. package/bootstrap/services/portal/src/data/pages.data.js +10 -0
  52. package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.jsx +65 -0
  53. package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.less +102 -0
  54. package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.jsx +115 -0
  55. package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.less +43 -0
  56. package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.jsx +124 -0
  57. package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.less +0 -0
  58. package/bootstrap/services/portal/src/elements/Repeater/Repeater.jsx +52 -0
  59. package/bootstrap/services/portal/src/elements/Repeater/Repeater.less +70 -0
  60. package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.jsx +18 -0
  61. package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.less +37 -0
  62. package/bootstrap/services/portal/src/elements/SaveButton/SaveButton.jsx +45 -0
  63. package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.jsx +63 -0
  64. package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.less +23 -0
  65. package/bootstrap/services/portal/src/elements/TextInput/TextInput.jsx +17 -0
  66. package/bootstrap/services/portal/src/layouts/Card/Card.jsx +15 -0
  67. package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.jsx +29 -0
  68. package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.less +49 -0
  69. package/bootstrap/services/portal/src/main.jsx +51 -0
  70. package/bootstrap/services/portal/src/pages/BlockSinglePage/BlockSinglePage.jsx +277 -0
  71. package/bootstrap/services/portal/src/pages/BlocksPage/BlocksPage.jsx +23 -0
  72. package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.jsx +11 -0
  73. package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.less +0 -0
  74. package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.jsx +21 -0
  75. package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.less +51 -0
  76. package/bootstrap/services/portal/src/pages/PageSinglePage/PageSinglePage.jsx +338 -0
  77. package/bootstrap/services/portal/src/pages/PagesPage/PagesPage.jsx +23 -0
  78. package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.jsx +9 -0
  79. package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.less +0 -0
  80. package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.jsx +25 -0
  81. package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.less +0 -0
  82. package/bootstrap/services/portal/src/services/block.js +28 -0
  83. package/bootstrap/services/portal/src/services/client.js +67 -0
  84. package/bootstrap/services/portal/src/services/gravatar.js +14 -0
  85. package/bootstrap/services/portal/src/services/page.js +28 -0
  86. package/bootstrap/services/portal/src/services/storage.js +62 -0
  87. package/bootstrap/services/portal/src/services/user.js +41 -0
  88. package/bootstrap/services/portal/src/slices/authSlice.js +101 -0
  89. package/bootstrap/services/portal/src/store/configureStore.js +10 -0
  90. package/bootstrap/services/portal/src/style/cards.less +57 -0
  91. package/bootstrap/services/portal/src/style/global.less +204 -0
  92. package/bootstrap/services/portal/src/style/icons.less +21 -0
  93. package/bootstrap/services/portal/src/style/inputs.less +52 -0
  94. package/bootstrap/services/portal/src/style/main.less +28 -0
  95. package/bootstrap/services/portal/src/utils/utils.js +9 -0
  96. package/bootstrap/services/portal/vite.config.js +12 -0
  97. package/bootstrap/services/user/src/app.js +6 -1
  98. package/bootstrap/services/user/src/commands/user.handler.js +0 -3
  99. package/bootstrap/services/user/src/config.js +5 -1
  100. package/bootstrap/services/user/src/policies/user.policy.js +3 -1
  101. package/bootstrap/services/user/src/tests/commands/user.test.js +22 -0
  102. package/install-from-clone.sh +30 -0
  103. package/package.json +1 -1
  104. package/src/cli.js +8 -0
  105. package/src/dev/commands.js +10 -2
  106. package/src/dev/dev.service.js +147 -60
  107. package/src/provisioner/Dockerfile +27 -0
  108. package/src/provisioner/package.json +19 -0
  109. package/src/provisioner/src/app.js +56 -0
  110. package/src/provisioner/src/services/mongodb.js +58 -0
  111. package/src/provisioner/src/services/mysql.js +51 -0
  112. package/src/provisioner/src/services/secrets.js +84 -0
  113. package/src/scaffolder/commands.js +1 -1
  114. package/src/scaffolder/scaffolder.handler.js +40 -15
  115. package/templates/service/src/app.js.hbs +12 -1
  116. package/templates/service/src/commands/{{serviceName}}.handler.js.hbs +1 -1
  117. package/templates/service/src/mongodb.config.js.hbs +5 -1
  118. package/templates/service/src/mysql.config.js.hbs +4 -0
  119. package/bootstrap/services/user/src/tests/user.test.js +0 -126
@@ -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,38 @@ 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
@@ -1,22 +1,33 @@
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:
12
+ AWS_SECRET_ACCESS_KEY:
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
28
+ page:
29
+ MONGO_HOST: db-mongo
30
+ MONGO_DATABASE: page_db
31
+ MONGO_USER: page_user
32
+ MONGO_PASSWORD: Q2atGxh21VqNIrZO
22
33
 
@@ -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
 
@@ -0,0 +1,23 @@
1
+ # Dockerfile for Page Service
2
+ FROM node:20-alpine
3
+
4
+ # Set the working directory
5
+ WORKDIR /usr/gnar_engine/app
6
+
7
+ # Define a global env var
8
+ ENV GLOBAL_SERVICE_BASE_DIR=/usr/gnar_engine/app/src/
9
+
10
+ # Copy package.json and package-lock.json
11
+ COPY ./services/page/package*.json ./
12
+
13
+ # Install nodemon
14
+ RUN npm install -g nodemon
15
+
16
+ # Install app dependencies
17
+ RUN npm install
18
+
19
+ # Expose the port the service will run on
20
+ EXPOSE 3000
21
+
22
+ # Start the application
23
+ CMD ["nodemon", "--watch", "./gnar_engine", "./gnar_engine/app.js"]
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "page-service",
3
+ "version": "1.0.0",
4
+ "description": "Page 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
+ }
16
+ }
@@ -0,0 +1,50 @@
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 pagePlatformHttpController } from './controllers/page.http.controller.js';
5
+ import { httpController as blockPlatformHttpController } from './controllers/block.http.controller.js';
6
+
7
+ /**
8
+ * Initialise service
9
+ */
10
+ export const initService = async () => {
11
+
12
+ // Run seeders
13
+ db.seeders.runSeeders({config});
14
+
15
+ // Import command handlers after the command bus is initialised
16
+ await import('./commands/page.handler.js');
17
+ await import('./commands/block.handler.js');
18
+
19
+ // Initialise and register message handlers
20
+ await message.init({
21
+ config: config.message,
22
+ handlers: messageHandlers
23
+ });
24
+
25
+ // Initialise websocket client & server
26
+ await webSockets.init(config.webSockets, config.serviceName);
27
+
28
+ // Register http routes
29
+ await http.registerRoutes({
30
+ controllers: [
31
+ pagePlatformHttpController,
32
+ blockPlatformHttpController
33
+ ]
34
+ });
35
+
36
+ // Start the HTTP server
37
+ await http.start();
38
+
39
+ // Register service with control service
40
+ await registerService();
41
+
42
+ logger.info('G n a r E n g i n e | Page Service initialised successfully.');
43
+
44
+ // Tests
45
+ if (config.environment === 'test' && config.runTests) {
46
+ test.runCommandTests({config});
47
+ }
48
+ }
49
+
50
+ initService();
@@ -0,0 +1,94 @@
1
+ import { commands, logger, error } from '@gnar-engine/core';
2
+ import { block } from '../services/block.service.js';
3
+ import { config } from '../config.js';
4
+ import { validateBlock } from '../schema/page.schema.js';
5
+
6
+
7
+ /**
8
+ * Get single block
9
+ */
10
+ commands.register('pageService.getSingleBlock', async ({id}) => {
11
+ if (id) {
12
+ return await block.getById({id: id});
13
+ } else {
14
+ throw new error.badRequest('Block id required');
15
+ }
16
+ });
17
+
18
+ /**
19
+ * Get many blocks
20
+ */
21
+ commands.register('pageService.getManyBlocks', async ({}) => {
22
+ return await block.getAll();
23
+ });
24
+
25
+ /**
26
+ * Create blocks
27
+ */
28
+ commands.register('pageService.createBlocks', async ({ blocks }) => {
29
+ const validationErrors = [];
30
+ let createdNewBlocks = [];
31
+
32
+ for (const newData of blocks) {
33
+ const { errors } = validateBlock(newData);
34
+ if (errors?.length) {
35
+ validationErrors.push(errors);
36
+ continue;
37
+ }
38
+
39
+ const created = await block.create(newData);
40
+ createdNewBlocks.push(created);
41
+ }
42
+
43
+ if (validationErrors.length) {
44
+ throw new error.badRequest(`Invalid block data: ${validationErrors}`);
45
+ }
46
+
47
+ return createdNewBlocks;
48
+ });
49
+
50
+ /**
51
+ * Update block
52
+ */
53
+ commands.register('pageService.updateBlock', async ({id, newBlockData}) => {
54
+
55
+ const validationErrors = [];
56
+
57
+ if (!id) {
58
+ throw new error.badRequest('Block ID required');
59
+ }
60
+
61
+ const obj = await block.getById({id: id});
62
+
63
+ if (!obj) {
64
+ throw new error.notFound('Block not found');
65
+ }
66
+
67
+ delete newBlockData.id;
68
+
69
+ const { errors } = validateBlock(newBlockData);
70
+
71
+ if (errors?.length) {
72
+ validationErrors.push(errors);
73
+ }
74
+
75
+ if (validationErrors.length) {
76
+ throw new error.badRequest(`Invalid block data: ${validationErrors}`);
77
+ }
78
+
79
+ return await block.update({
80
+ id: id,
81
+ updatedData: newBlockData
82
+ });
83
+ });
84
+
85
+ /**
86
+ * Delete block
87
+ */
88
+ commands.register('pageService.deleteBlock', async ({id}) => {
89
+ const obj = await block.getById({id: id});
90
+ if (!obj) {
91
+ throw new error.notFound('Block not found');
92
+ }
93
+ return await block.delete({id: id});
94
+ });
@@ -0,0 +1,167 @@
1
+ import { commands, logger, error, storage } from '@gnar-engine/core';
2
+ import { page } from '../services/page.service.js';
3
+ import { config } from '../config.js';
4
+ import { validatePage } from '../schema/page.schema.js';
5
+
6
+
7
+ /**
8
+ * Get single page
9
+ */
10
+ commands.register('pageService.getSinglePage', async ({id}) => {
11
+ if (id) {
12
+ return await page.getById({id: id});
13
+ } else {
14
+ throw new error.badRequest('Page email or id required');
15
+ }
16
+ });
17
+
18
+ /**
19
+ * Get many pages
20
+ */
21
+ commands.register('pageService.getManyPages', async ({}) => {
22
+ return await page.getAll();
23
+ });
24
+
25
+ /**
26
+ * Create pages
27
+ */
28
+ commands.register('pageService.createPages', async ({ pages, requestUser }) => {
29
+ const validationErrors = [];
30
+ let createdNewPages = [];
31
+
32
+ for (const newData of pages) {
33
+ const { errors } = validatePage(newData);
34
+ if (errors?.length) {
35
+ validationErrors.push(errors);
36
+ continue;
37
+ }
38
+
39
+ newData = await commands.execute('processUploadsInData', { data: newData, requestUser });
40
+
41
+ const created = await page.create(newData);
42
+ createdNewPages.push(created);
43
+ }
44
+
45
+ if (validationErrors.length) {
46
+ throw new error.badRequest(`Invalid page data: ${validationErrors}`);
47
+ }
48
+
49
+ return createdNewPages;
50
+ });
51
+
52
+ /**
53
+ * Update page
54
+ */
55
+ commands.register('pageService.updatePage', async ({id, newPageData, requestUser}) => {
56
+
57
+ const validationErrors = [];
58
+
59
+ if (!id) {
60
+ throw new error.badRequest('Page ID required');
61
+
62
+ }
63
+
64
+ const obj = await page.getById({id: id});
65
+
66
+ if (!obj) {
67
+ throw new error.notFound('Page not found');
68
+
69
+ }
70
+
71
+ delete newPageData.id;
72
+
73
+ const { errors } = validatePage(newPageData);
74
+
75
+ if (errors?.length) {
76
+ validationErrors.push(errors);
77
+ }
78
+
79
+ if (validationErrors.length) {
80
+ throw new error.badRequest(`Invalid page data: ${validationErrors}`);
81
+ }
82
+
83
+ newPageData = await commands.execute('processUploadsInData', { data: newPageData, requestUser });
84
+
85
+ return await page.update({
86
+ id: id,
87
+ updatedData: newPageData
88
+ });
89
+ });
90
+
91
+ /**
92
+ * Delete page
93
+ */
94
+ commands.register('pageService.deletePage', async ({id}) => {
95
+ const obj = await page.getById({id: id});
96
+ if (!obj) {
97
+ throw new error.notFound('Page not found');
98
+ }
99
+ return await page.delete({id: id});
100
+ });
101
+
102
+
103
+ /**
104
+ * Find file and image uploads and store them
105
+ *
106
+ * @param {Object} data - The data object to search for files/images
107
+ * @param {Function} uploadFn - Function to handle the actual upload process
108
+ * @returns {Object} - The updated data object with stored file/image references
109
+ */
110
+ commands.register('pageService.processUploadsInData', async ({ data, requestUser }) => {
111
+
112
+ const uploadFilesRecursive = async (data) => {
113
+ if (Array.isArray(data)) {
114
+ return Promise.all(data.map(item => uploadFilesRecursive(item)));
115
+ } else if (data && typeof data === 'object') {
116
+ const result = { ...data };
117
+
118
+ for (const [key, value] of Object.entries(data)) {
119
+ if (key === 'file' && typeof value === 'string') {
120
+
121
+ logger.info('Processing file upload in page data');
122
+
123
+ // Filename
124
+ const fileName = result.fileName || `upload_${Date.now()}`;
125
+
126
+ // Mime type
127
+ let mimeType = result.mimeType;
128
+ let base64Data = value;
129
+
130
+ const matches = value.match(/^data:(.+);base64,(.+)$/);
131
+ if (matches) {
132
+ mimeType = mimeType || matches[1];
133
+ base64Data = matches[2];
134
+ }
135
+
136
+ if (!mimeType) mimeType = 'application/octet-stream';
137
+
138
+ // Upload
139
+ const url = await storage.upload({
140
+ file: Buffer.from(base64Data, 'base64'),
141
+ key: 'public/page-content/' + fileName,
142
+ contentType: mimeType,
143
+ metadata: {
144
+ uploadedAt: new Date().toISOString(),
145
+ uploadedBy: requestUser ? requestUser.id : 'unknown'
146
+ }
147
+ });
148
+
149
+ // Add url and remove upload keys
150
+ result.url = url;
151
+ delete result.file;
152
+ if (result.fileName) delete result.fileName;
153
+ if (result.mimeType) delete result.mimeType;
154
+
155
+ } else {
156
+ result[key] = await uploadFilesRecursive(value);
157
+ }
158
+ }
159
+
160
+ return result;
161
+ }
162
+
163
+ return data;
164
+ };
165
+
166
+ return await uploadFilesRecursive(data);
167
+ });
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Gnar Engine Service Config
3
+ */
4
+ export const config = {
5
+ // service name
6
+ serviceName: 'pageService',
7
+
8
+ // microservice | modular-monolith
9
+ architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
10
+
11
+ // web server
12
+ http: {
13
+ allowedOrigins: [],
14
+ allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
15
+ allowedHeaders: ['Content-Type', 'Authorization'],
16
+ rateLimiting: {
17
+ max: 5,
18
+ timeWindow: '1 minute',
19
+ }
20
+ },
21
+
22
+ // database
23
+ db: {
24
+ // type: mongodb | mysql
25
+ type: 'mongodb',
26
+
27
+ // MongoDB
28
+ host: process.env.PAGE_MONGO_HOST,
29
+ database: process.env.PAGE_MONGO_DATABASE,
30
+ user: process.env.PAGE_MONGO_USER,
31
+ password: process.env.PAGE_MONGO_PASSWORD,
32
+ port: process.env.PAGE_MONGO_PORT || 27017,
33
+ connectionArgs: {},
34
+ },
35
+
36
+ // storage
37
+ storage: {
38
+ // driver: s3
39
+ driver: 's3',
40
+ uploadsUrl: process.env.UPLOADS_URL,
41
+
42
+ // s3
43
+ bucket: process.env.S3_BUCKET,
44
+ region: process.env.AWS_REGION,
45
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
46
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
47
+ },
48
+
49
+ // message broker
50
+ message: {
51
+ url: process.env.RABBITMQ_URL,
52
+ queueName: 'pageServiceQueue',
53
+ prefetch: 20
54
+ },
55
+
56
+ webSockets: {
57
+ reconnectInterval: 5000,
58
+ maxInitialConnectionAttempts: 5
59
+ },
60
+
61
+ hashNameSpace: '',
62
+ }
@@ -0,0 +1,87 @@
1
+ import { commands } from '@gnar-engine/core';
2
+ import { authorise } from '../policies/block.policy.js';
3
+
4
+ /**
5
+ * HTTP controller
6
+ */
7
+ export const httpController = {
8
+
9
+ /**
10
+ * Get single block
11
+ */
12
+ getSingle: {
13
+ method: 'GET',
14
+ url: '/blocks/: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('getSingleBlock', params);
21
+ reply.code(200).send({ block: result });
22
+ }
23
+ },
24
+
25
+ /**
26
+ * Get multiple blocks
27
+ */
28
+ getMany: {
29
+ method: 'GET',
30
+ url: '/blocks/',
31
+ preHandler: async (request, reply) => authorise.getMany(request, reply),
32
+ handler: async (request, reply) => {
33
+ const params = {};
34
+ const results = await commands.execute('getManyBlocks', params);
35
+ reply.code(200).send({ blocks: results });
36
+ }
37
+ },
38
+
39
+ /**
40
+ * Create new block
41
+ */
42
+ create: {
43
+ method: 'POST',
44
+ url: '/blocks/',
45
+ preHandler: async (request, reply) => authorise.create(request, reply),
46
+ handler: async (request, reply) => {
47
+ const params = {
48
+ blocks: [request.body.block]
49
+ };
50
+ const results = await commands.execute('createBlocks', params);
51
+ reply.code(200).send({ blocks: results });
52
+ },
53
+ },
54
+
55
+ /**
56
+ * Update block
57
+ */
58
+ update: {
59
+ method: 'POST',
60
+ url: '/blocks/:id',
61
+ preHandler: async (request, reply) => authorise.update(request, reply),
62
+ handler: async (request, reply) => {
63
+ const params = {
64
+ id: request.params.id,
65
+ newBlockData: request.body.block
66
+ };
67
+ const result = await commands.execute('updateBlock', params);
68
+ reply.code(200).send({ page: result });
69
+ },
70
+ },
71
+
72
+ /**
73
+ * Delete block
74
+ */
75
+ delete: {
76
+ method: 'DELETE',
77
+ url: '/blocks/: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('deleteBlock', params);
84
+ reply.code(200).send({ message: 'Block deleted' });
85
+ },
86
+ },
87
+ }
@@ -0,0 +1,51 @@
1
+ import { commands } from '@gnar-engine/core';
2
+
3
+ export const messageHandlers = {
4
+
5
+ getPage: async (payload) => {
6
+ let result;
7
+ if (payload.data?.id) {
8
+ result = await commands.execute('getSinglePage', {
9
+ id: payload.data.id
10
+ });
11
+ } else if (payload.data?.email) {
12
+ result = await commands.execute('getSinglePage', {
13
+ email: payload.data.email
14
+ });
15
+ } else {
16
+ throw new Error('No page ID or email provided');
17
+ }
18
+ if (!result) {
19
+ throw new Error('Page not found');
20
+ }
21
+ return { page: result };
22
+ },
23
+
24
+ getManyPages: async (payload) => {
25
+ const results = await commands.execute('getManyPages', {});
26
+ return { pages: results };
27
+ },
28
+
29
+ createPage: async (payload) => {
30
+ const results = await commands.execute('createPages', {
31
+ pages: [payload.data.page]
32
+ });
33
+ return { pages: results };
34
+ },
35
+
36
+ updatePage: async (payload) => {
37
+ const result = await commands.execute('updatePage', {
38
+ id: payload.data.id,
39
+ newPageData: payload.data
40
+ });
41
+ return { page: result };
42
+ },
43
+
44
+ deletePage: async (payload) => {
45
+ await commands.execute('deletePage', {
46
+ id: payload.data.id
47
+ });
48
+ return { message: 'Page deleted' };
49
+ },
50
+
51
+ };