@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
@@ -3,8 +3,11 @@ import fs from 'fs';
3
3
  import yaml from 'js-yaml';
4
4
  import { profiles } from '../profiles/profiles.client.js';
5
5
  import { helpers } from '../helpers/helpers.js';
6
+ import { directories } from '../cli.js';
6
7
  import Handlebars from 'handlebars';
7
8
 
9
+
10
+
8
11
  /**
9
12
  * Gnar Engine Scaffolder
10
13
  */
@@ -32,10 +35,9 @@ export const scaffolder = {
32
35
  fs.mkdirSync(serviceDir, { recursive: true });
33
36
 
34
37
  // Get all files in the templates directory
35
- const templatesDir = path.join(import.meta.dirname, '../../templates/service');
36
38
  const templateFiles = scaffolder.getAllTemplateFiles({
37
- dir: templatesDir,
38
- baseDir: templatesDir
39
+ dir: directories.scaffolderTemplates,
40
+ baseDir: directories.scaffolderTemplates
39
41
  });
40
42
 
41
43
  // Register Handlebars helpers
@@ -136,6 +138,25 @@ export const scaffolder = {
136
138
 
137
139
  // Create the service directory
138
140
  fs.mkdirSync(serviceDir, { recursive: true });
141
+
142
+ // Add to deploy.yml
143
+ scaffolder.scaffoldServiceDeployYml({
144
+ deployPath: path.join(projectDir, 'deploy.localdev.yml'),
145
+ serviceName: serviceName,
146
+ database: null
147
+ });
148
+
149
+ // Scaffold secrets
150
+ scaffolder.scaffoldServiceSecrets({
151
+ secretsPath: path.join(projectDir, 'secrets.localdev.yml'),
152
+ serviceName: serviceName,
153
+ database: null
154
+ });
155
+
156
+ return {
157
+ message: `Front-end service "${serviceName}" created successfully at ${serviceDir}`,
158
+ servicePath: serviceDir
159
+ };
139
160
  },
140
161
 
141
162
  /**
@@ -284,13 +305,17 @@ export const scaffolder = {
284
305
  const parsedSecrets = yaml.load(rawSecrets);
285
306
 
286
307
  // generate random passwords
287
- Object.keys(parsedSecrets.services).forEach(serviceName => {
288
- Object.keys(parsedSecrets.services[serviceName]).forEach(key => {
289
- if (key.toLowerCase().includes('pass')) {
290
- parsedSecrets.services[serviceName][key] = helpers.generateRandomString(16);
291
- }
308
+ try {
309
+ Object.keys(parsedSecrets.services).forEach(serviceName => {
310
+ Object.keys(parsedSecrets.services[serviceName]).forEach(key => {
311
+ if (key.toLowerCase().includes('pass')) {
312
+ parsedSecrets.services[serviceName][key] = helpers.generateRandomString(16);
313
+ }
314
+ });
292
315
  });
293
- });
316
+ } catch (error) {
317
+ throw new Error('Error generating random passwords for project secrets: ' + error.message);
318
+ }
294
319
 
295
320
  // set random root api key
296
321
  const cliApiKey = helpers.generateRandomString(32);
@@ -328,15 +353,14 @@ export const scaffolder = {
328
353
  parsedSecrets.services[serviceName]['MYSQL_USER'] = serviceName + '_user';
329
354
  parsedSecrets.services[serviceName]['MYSQL_PASSWORD'] = helpers.generateRandomString(16);
330
355
  parsedSecrets.services[serviceName]['MYSQL_DATABASE'] = serviceName + '_db';
331
- parsedSecrets.services[serviceName]['MYSQL_HOST'] = serviceName + '-db';
332
- parsedSecrets.services[serviceName]['MYSQL_RANDOM_ROOT_PASSWORD'] = helpers.generateRandomString(16);
356
+ parsedSecrets.services[serviceName]['MYSQL_HOST'] = 'db-mysql';
333
357
  break;
334
358
  case 'mongodb':
335
359
  const mongoPassword = helpers.generateRandomString(16);
336
360
  const mongoRootPassword = helpers.generateRandomString(16);
337
361
  const mongoUser = serviceName + '_user';
338
362
  const mongoDatabase = serviceName + '_db';
339
- const mongoHost = serviceName + '-db';
363
+ const mongoHost = 'db-mongo';
340
364
  const mongoUrl = `mongodb://${mongoUser}:${mongoPassword}@${mongoHost}:27017/${mongoDatabase}`;
341
365
 
342
366
  parsedSecrets.services[serviceName]['MONGO_URL'] = mongoUrl;
@@ -347,8 +371,9 @@ export const scaffolder = {
347
371
  parsedSecrets.services[serviceName]['MONGO_HOST'] = mongoHost;
348
372
  break;
349
373
  default:
350
- throw new Error(`Unsupported database type in secret scaffolder: ${database}`);
351
- }
374
+ // no db
375
+ break;
376
+ }
352
377
 
353
378
  // save updated secrets file
354
379
  const newSecretsContent = yaml.dump(parsedSecrets);
@@ -411,7 +436,7 @@ export const scaffolder = {
411
436
  // add database service if required
412
437
  if (database) {
413
438
  serviceConfig.depends_on = [
414
- `${serviceName}-db`
439
+ `db-${database}`
415
440
  ]
416
441
  }
417
442
 
@@ -1,4 +1,4 @@
1
- import { message, http, logger, db } from '@gnar-engine/core';
1
+ import { message, http, logger, db, registerService, webSockets, test } from '@gnar-engine/core';
2
2
  import { config } from './config.js';
3
3
  import { messageHandlers } from './controllers/message.controller.js';
4
4
  import { httpController as {{lowerCase serviceName}}PlatformHttpController } from './controllers/http.controller.js';
@@ -26,6 +26,9 @@ export const initService = async () => {
26
26
  handlers: messageHandlers
27
27
  });
28
28
 
29
+ // Initialise websocket client & server
30
+ await webSockets.init(config.webSockets, config.serviceName);
31
+
29
32
  // Register http routes
30
33
  await http.registerRoutes({
31
34
  controllers: [
@@ -36,7 +39,15 @@ export const initService = async () => {
36
39
  // Start the HTTP server
37
40
  await http.start();
38
41
 
42
+ // Register service with control service
43
+ await registerService();
44
+
39
45
  logger.info('G n a r E n g i n e | {{capitaliseFirstLetter serviceName}} Service initialised successfully.');
46
+
47
+ // Tests
48
+ if (config.environment === 'test' && config.runTests) {
49
+ test.runCommandTests({config});
50
+ }
40
51
  }
41
52
 
42
53
  initService();
@@ -80,7 +80,7 @@ commands.register('{{serviceName}}Service.update{{pascalCase serviceName}}', asy
80
80
 
81
81
  return await {{serviceName}}.update({
82
82
  id: id,
83
- ...new{{pascalCase serviceName}}Data
83
+ updatedData: new{{pascalCase serviceName}}Data
84
84
  });
85
85
  });
86
86
 
@@ -25,7 +25,11 @@ export const config = {
25
25
  type: 'mongodb',
26
26
 
27
27
  // MongoDB
28
- connectionUrl: process.env.{{upperCase serviceName}}_MONGO_URL,
28
+ host: process.env.{{upperCase serviceName}}_MONGO_HOST,
29
+ database: process.env.{{upperCase serviceName}}_MONGO_DATABASE,
30
+ user: process.env.{{upperCase serviceName}}_MONGO_USER,
31
+ password: process.env.{{upperCase serviceName}}_MONGO_PASSWORD,
32
+ port: process.env.{{upperCase serviceName}}_MONGO_PORT || 27017,
29
33
  connectionArgs: {},
30
34
  },
31
35
 
@@ -5,6 +5,10 @@ export const config = {
5
5
  // service name
6
6
  serviceName: '{{serviceName}}Service',
7
7
 
8
+ // environment
9
+ environment: process.env.{{upperCase serviceName}}_NODE_ENV || 'dev',
10
+ runTests: process.env.{{upperCase serviceName}}_RUN_TESTS || false,
11
+
8
12
  // microservice | modular-monolith
9
13
  architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
10
14
 
@@ -1,126 +0,0 @@
1
- const request = require('supertest');
2
- const url = 'http://localhost';
3
-
4
- describe('User API', () => {
5
-
6
- let adminAuthToken;
7
- let customerAuthToken;
8
- let userId;
9
- let testCustomerEmail;
10
-
11
- beforeAll(async () => {
12
- // check we are not in production mode
13
- if (process.env.NODE_ENV === 'production') {
14
- throw new Error('Do not run tests in production mode!');
15
- }
16
-
17
- testCustomerEmail = 'customertest13@gnar.co.uk';
18
- });
19
-
20
- // Test authenticate with email and password
21
- it('POST /authenticate (as admin)', async () => {
22
- const response = await request(url).post('/authenticate')
23
- .set('Content-Type', 'application/json')
24
- .send({
25
- username: 'root@gnar.co.uk',
26
- password: 'gn4rlyR00tP0rt4lP4ss'
27
- });
28
- console.log(response.body);
29
- expect(response.status).toBe(200);
30
- expect(response.body).toHaveProperty('token');
31
-
32
- adminAuthToken = response.body.token;
33
- });
34
-
35
- // Create new customer user (success no auth required)
36
- it('POST /users (create new customer user no auth required)', async () => {
37
- const response = await request(url).post('/users')
38
- .set('Content-Type', 'application/json')
39
- .send({
40
- user:
41
- {
42
- email: testCustomerEmail,
43
- password: 'password1234'
44
- }
45
- })
46
- console.log(response.body);
47
- expect(response.status).toBe(200);
48
- expect(response.body.users[0].email).toBe(testCustomerEmail);
49
-
50
- // Save the new user id
51
- userId = response.body.users[0].id;
52
- console.log('New user id: ' + userId);
53
- });
54
-
55
- // Test authenticate with new user
56
- it('POST /authenticate (as new customer user)', async () => {
57
- const response = await request(url).post('/authenticate')
58
- .set('Content-Type', 'application/json')
59
- .send({
60
- username: testCustomerEmail,
61
- password: 'password1234'
62
- });
63
- console.log(response.body);
64
- expect(response.status).toBe(200);
65
- expect(response.body).toHaveProperty('token');
66
-
67
- customerAuthToken = response.body.token;
68
- });
69
-
70
- // Fetch single customer user (as customer)
71
- it('GET /users/{id} (as customer)', async () => {
72
- const response = await request(url).get('/users/' + userId)
73
- .set('Authorization', 'Bearer ' + customerAuthToken);
74
- console.log(response.body);
75
- expect(response.status).toBe(200);
76
- });
77
-
78
- // Fetch all users (not authorised as customer)
79
- it('GET /users (not authorised as customer)', async () => {
80
- const response = await request(url).get('/users')
81
- .set('Authorization', 'Bearer ' + customerAuthToken);
82
- console.log(response.body);
83
- expect(response.status).toBe(403);
84
- });
85
-
86
- // Fetch all users (success as admin)
87
- it('GET /users (as admin)', async () => {
88
- const response = await request(url).get('/users')
89
- .set('Authorization', 'Bearer ' + adminAuthToken);
90
- console.log(response.body);
91
- expect(response.status).toBe(200);
92
- });
93
-
94
- // Update customer user (as customer)
95
- it('POST /users/{id} (as customer)', async () => {
96
- const response = await request(url).post('/users/' + userId)
97
- .set('Authorization', 'Bearer ' + customerAuthToken)
98
- .set('Content-Type', 'application/json')
99
- .send({
100
- email: 'changedcustomeremail2@gnar.co.uk'
101
- });
102
- console.log(response.body);
103
- expect(response.status).toBe(200);
104
- expect(response.body.user.email).toBe('changedcustomeremail2@gnar.co.uk');
105
- });
106
-
107
- // Update customer user (check cannot elevate role to admin)
108
- it('POST /users/{id} (as customer)', async () => {
109
- const response = await request(url).post('/users/' + userId)
110
- .set('Authorization', 'Bearer ' + customerAuthToken)
111
- .set('Content-Type', 'application/json')
112
- .send({
113
- role: 'service_admin'
114
- });
115
- console.log(response.body);
116
- expect(response.status).toBe(403);
117
- });-
118
-
119
- // Delete customer user (as admin)
120
- it('DELETE /users/{id} (as admin)', async () => {
121
- const response = await request(url).delete('/users/' + userId)
122
- .set('Authorization', 'Bearer ' + adminAuthToken);
123
- console.log(response.body);
124
- expect(response.status).toBe(200);
125
- });
126
- });