@gnar-engine/cli 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/assets/gnar-engine-logo-white.svg +9 -0
  2. package/bootstrap/deploy.localdev.yml +51 -0
  3. package/bootstrap/secrets.localdev.yml +27 -0
  4. package/bootstrap/services/agent/Dockerfile +23 -0
  5. package/bootstrap/services/agent/notes.md +28 -0
  6. package/bootstrap/services/agent/package.json +16 -0
  7. package/bootstrap/services/agent/src/app.js +52 -0
  8. package/bootstrap/services/agent/src/commands/agent.handler.js +104 -0
  9. package/bootstrap/services/agent/src/config.js +52 -0
  10. package/bootstrap/services/agent/src/controllers/http.controller.js +44 -0
  11. package/bootstrap/services/agent/src/controllers/message.controller.js +51 -0
  12. package/bootstrap/services/agent/src/db/migrations/01-init.js +50 -0
  13. package/bootstrap/services/agent/src/db/migrations/02-agent-service-init.js +36 -0
  14. package/bootstrap/services/agent/src/policies/agent.policy.js +13 -0
  15. package/bootstrap/services/agent/src/schema/Agent.schema.js +17 -0
  16. package/bootstrap/services/agent/src/services/agent.service.js +259 -0
  17. package/bootstrap/services/agent/src/services/chatgpt.service.js +46 -0
  18. package/bootstrap/services/agent/src/services/manifest.service.js +21 -0
  19. package/bootstrap/services/control/Dockerfile +23 -0
  20. package/bootstrap/services/control/Dockerfile.prod +37 -0
  21. package/bootstrap/services/control/README.md +25 -0
  22. package/bootstrap/services/control/package.json +16 -0
  23. package/bootstrap/services/control/src/app.js +45 -0
  24. package/bootstrap/services/control/src/commands/control.handler.js +231 -0
  25. package/bootstrap/services/control/src/commands/service.handler.js +81 -0
  26. package/bootstrap/services/control/src/commands/task.handler.js +247 -0
  27. package/bootstrap/services/control/src/config.js +55 -0
  28. package/bootstrap/services/control/src/controllers/http.controller.js +228 -0
  29. package/bootstrap/services/control/src/controllers/message.controller.js +40 -0
  30. package/bootstrap/services/control/src/db/migrations/01-init.js +50 -0
  31. package/bootstrap/services/control/src/db/migrations/02-control-service-init.js +60 -0
  32. package/bootstrap/services/control/src/db/migrations/03-alter-tasks.js +29 -0
  33. package/bootstrap/services/control/src/policies/task.policy.js +53 -0
  34. package/bootstrap/services/control/src/schema/control.schema.js +42 -0
  35. package/bootstrap/services/control/src/services/registry.service.js +83 -0
  36. package/bootstrap/services/control/src/services/reset.service.js +28 -0
  37. package/bootstrap/services/control/src/services/task.service.js +153 -0
  38. package/bootstrap/services/control/src/tests/control.test.js +50 -0
  39. package/bootstrap/services/notification/Dockerfile +23 -0
  40. package/bootstrap/services/notification/Dockerfile.prod +37 -0
  41. package/bootstrap/services/notification/README.md +3 -0
  42. package/bootstrap/services/notification/package.json +34 -0
  43. package/bootstrap/services/notification/src/app.js +51 -0
  44. package/bootstrap/services/notification/src/commands/command-bus.js +20 -0
  45. package/bootstrap/services/notification/src/commands/handlers/control.handler.js +18 -0
  46. package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +157 -0
  47. package/bootstrap/services/notification/src/config.js +15 -0
  48. package/bootstrap/services/notification/src/controllers/message.controller.js +82 -0
  49. package/bootstrap/services/notification/src/services/logger.service.js +16 -0
  50. package/bootstrap/services/notification/src/services/ses.service.js +23 -0
  51. package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +136 -0
  52. package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +87 -0
  53. package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +132 -0
  54. package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +77 -0
  55. package/bootstrap/services/notification/src/tests/notification.test.js +0 -0
  56. package/bootstrap/services/portal/Dockerfile +23 -0
  57. package/bootstrap/services/portal/Dockerfile.remote +40 -0
  58. package/bootstrap/services/portal/README.md +22 -0
  59. package/bootstrap/services/portal/nginx.conf +12 -0
  60. package/bootstrap/services/portal/package.json +59 -0
  61. package/bootstrap/services/portal/public/favicon.ico +0 -0
  62. package/bootstrap/services/portal/public/gnar-white.png +0 -0
  63. package/bootstrap/services/portal/public/gnarengine-logo-black.png +0 -0
  64. package/bootstrap/services/portal/public/index.html +43 -0
  65. package/bootstrap/services/portal/public/logo192.png +0 -0
  66. package/bootstrap/services/portal/public/logo512.png +0 -0
  67. package/bootstrap/services/portal/public/manifest.json +25 -0
  68. package/bootstrap/services/portal/public/robots.txt +3 -0
  69. package/bootstrap/services/portal/src/App.js +56 -0
  70. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black.svg +1 -0
  71. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black_Green.svg +1 -0
  72. package/bootstrap/services/portal/src/assets/Logo_Anchord_White_Green.svg +1 -0
  73. package/bootstrap/services/portal/src/assets/activity.svg +3 -0
  74. package/bootstrap/services/portal/src/assets/arrow.svg +3 -0
  75. package/bootstrap/services/portal/src/assets/bin-white.svg +3 -0
  76. package/bootstrap/services/portal/src/assets/bin.svg +3 -0
  77. package/bootstrap/services/portal/src/assets/check.svg +3 -0
  78. package/bootstrap/services/portal/src/assets/chevron.svg +3 -0
  79. package/bootstrap/services/portal/src/assets/contact.svg +3 -0
  80. package/bootstrap/services/portal/src/assets/dots-vertical.svg +5 -0
  81. package/bootstrap/services/portal/src/assets/eye-off.svg +3 -0
  82. package/bootstrap/services/portal/src/assets/eye.svg +4 -0
  83. package/bootstrap/services/portal/src/assets/gnar-engine-black.svg +47 -0
  84. package/bootstrap/services/portal/src/assets/gnar-engine-white.svg +47 -0
  85. package/bootstrap/services/portal/src/assets/gnar_engine.svg +3 -0
  86. package/bootstrap/services/portal/src/assets/gnarengine-logo-black.png +0 -0
  87. package/bootstrap/services/portal/src/assets/home.svg +3 -0
  88. package/bootstrap/services/portal/src/assets/link.svg +3 -0
  89. package/bootstrap/services/portal/src/assets/lock.svg +3 -0
  90. package/bootstrap/services/portal/src/assets/package.svg +4 -0
  91. package/bootstrap/services/portal/src/assets/raffle.svg +3 -0
  92. package/bootstrap/services/portal/src/assets/settings.svg +4 -0
  93. package/bootstrap/services/portal/src/assets/shopping-bag.svg +3 -0
  94. package/bootstrap/services/portal/src/assets/user-black.svg +3 -0
  95. package/bootstrap/services/portal/src/assets/user.svg +3 -0
  96. package/bootstrap/services/portal/src/assets/users.svg +3 -0
  97. package/bootstrap/services/portal/src/assets/wallet.svg +3 -0
  98. package/bootstrap/services/portal/src/css/style.css +1007 -0
  99. package/bootstrap/services/portal/src/data/data.js +70 -0
  100. package/bootstrap/services/portal/src/features/attributeFormRow/AttributeFormRow.jsx +32 -0
  101. package/bootstrap/services/portal/src/features/billingShipping/BillingShipping.jsx +160 -0
  102. package/bootstrap/services/portal/src/features/crud/crudEdit.less +230 -0
  103. package/bootstrap/services/portal/src/features/crud/crudList.less +134 -0
  104. package/bootstrap/services/portal/src/features/crud/crudPage.less +31 -0
  105. package/bootstrap/services/portal/src/features/crudContact/CrudContactList.jsx +108 -0
  106. package/bootstrap/services/portal/src/features/crudContact/CrudContactSingle.jsx +243 -0
  107. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderList.jsx +109 -0
  108. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderSingle.jsx +315 -0
  109. package/bootstrap/services/portal/src/features/crudProducts/CrudProductList.jsx +104 -0
  110. package/bootstrap/services/portal/src/features/crudProducts/CrudProductSingle.jsx +388 -0
  111. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesList.jsx +104 -0
  112. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesSingle.jsx +208 -0
  113. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionList.jsx +110 -0
  114. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionSingle.jsx +261 -0
  115. package/bootstrap/services/portal/src/features/crudUser/CrudUserList.jsx +107 -0
  116. package/bootstrap/services/portal/src/features/crudUser/CrudUserSingle.jsx +402 -0
  117. package/bootstrap/services/portal/src/features/inventoryFormRow/InventoryFormRow.jsx +30 -0
  118. package/bootstrap/services/portal/src/features/lineItems/LineItems.jsx +113 -0
  119. package/bootstrap/services/portal/src/features/loginForm/LoginForm.jsx +56 -0
  120. package/bootstrap/services/portal/src/features/loginForm/loginForm.less +56 -0
  121. package/bootstrap/services/portal/src/features/notes/Notes.jsx +18 -0
  122. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetForm.jsx +96 -0
  123. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetRequestForm.jsx +74 -0
  124. package/bootstrap/services/portal/src/features/priceFormRow/PriceFormRow.jsx +102 -0
  125. package/bootstrap/services/portal/src/features/priceFormRow/priceFormRow.less +24 -0
  126. package/bootstrap/services/portal/src/features/raffleEntriesList/RaffleEntriesList.jsx +99 -0
  127. package/bootstrap/services/portal/src/features/raffleProductFormRow/RaffleProductFormRow.jsx +46 -0
  128. package/bootstrap/services/portal/src/features/sidebar/Sidebar.jsx +64 -0
  129. package/bootstrap/services/portal/src/features/sidebar/sidebar.less +49 -0
  130. package/bootstrap/services/portal/src/features/skus/Skus.jsx +109 -0
  131. package/bootstrap/services/portal/src/features/subscriptionSchedule/SubscriptionSchedule.jsx +44 -0
  132. package/bootstrap/services/portal/src/features/taxonomyFormRow/TaxonomyFormRow.jsx +32 -0
  133. package/bootstrap/services/portal/src/features/user/User.jsx +54 -0
  134. package/bootstrap/services/portal/src/features/user/user.less +57 -0
  135. package/bootstrap/services/portal/src/includes/utilities.js +259 -0
  136. package/bootstrap/services/portal/src/index.js +14 -0
  137. package/bootstrap/services/portal/src/layouts/CrudLayout.jsx +50 -0
  138. package/bootstrap/services/portal/src/layouts/LoginLayout.jsx +17 -0
  139. package/bootstrap/services/portal/src/layouts/PortalLayout.jsx +48 -0
  140. package/bootstrap/services/portal/src/layouts/loginLayout.less +33 -0
  141. package/bootstrap/services/portal/src/layouts/portalLayout.less +67 -0
  142. package/bootstrap/services/portal/src/pages/contacts/Contacts.jsx +199 -0
  143. package/bootstrap/services/portal/src/pages/dashboard/Dashboard.jsx +17 -0
  144. package/bootstrap/services/portal/src/pages/integrations/Integrations.jsx +10 -0
  145. package/bootstrap/services/portal/src/pages/login/Login.jsx +15 -0
  146. package/bootstrap/services/portal/src/pages/login/login.less +10 -0
  147. package/bootstrap/services/portal/src/pages/orders/Orders.jsx +199 -0
  148. package/bootstrap/services/portal/src/pages/passwordReset/PasswordResetPage.jsx +15 -0
  149. package/bootstrap/services/portal/src/pages/passwordResetRequest/PasswordResetRequestPage.jsx +15 -0
  150. package/bootstrap/services/portal/src/pages/payments/Payments.jsx +10 -0
  151. package/bootstrap/services/portal/src/pages/portal/Portal.jsx +43 -0
  152. package/bootstrap/services/portal/src/pages/products/Products.jsx +212 -0
  153. package/bootstrap/services/portal/src/pages/raffleEntries/RaffleEntries.jsx +124 -0
  154. package/bootstrap/services/portal/src/pages/raffles/Raffles.jsx +186 -0
  155. package/bootstrap/services/portal/src/pages/reports/Reports.jsx +10 -0
  156. package/bootstrap/services/portal/src/pages/settings/Settings.jsx +10 -0
  157. package/bootstrap/services/portal/src/pages/subscriptions/Subscriptions.jsx +199 -0
  158. package/bootstrap/services/portal/src/pages/users/Users.jsx +193 -0
  159. package/bootstrap/services/portal/src/pages/users/users.less +25 -0
  160. package/bootstrap/services/portal/src/slices/authSlice.js +71 -0
  161. package/bootstrap/services/portal/src/store/configureStore.js +12 -0
  162. package/bootstrap/services/portal/src/styles/global.less +159 -0
  163. package/bootstrap/services/portal/src/styles/inputs.less +157 -0
  164. package/bootstrap/services/portal/src/styles/main.less +26 -0
  165. package/bootstrap/services/portal/src/ui/collapsible/Collapsible.jsx +97 -0
  166. package/bootstrap/services/portal/src/ui/collapsible/collapsible.less +23 -0
  167. package/bootstrap/services/portal/src/ui/customCheckbox/CustomCheckbox.jsx +17 -0
  168. package/bootstrap/services/portal/src/ui/customCheckbox/customCheckbox.less +42 -0
  169. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelect.jsx +63 -0
  170. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelectPeriod.jsx +63 -0
  171. package/bootstrap/services/portal/src/ui/customSelect/CustomSelect.jsx +63 -0
  172. package/bootstrap/services/portal/src/ui/customSelect/customSelect.less +92 -0
  173. package/bootstrap/services/portal/src/ui/goBack/GoBack.jsx +19 -0
  174. package/bootstrap/services/portal/src/ui/loader/Loader.jsx +12 -0
  175. package/bootstrap/services/portal/src/ui/pagination/Pagination.jsx +23 -0
  176. package/bootstrap/services/portal/src/ui/repeater/Repeater.jsx +29 -0
  177. package/bootstrap/services/portal/src/ui/saveButton/SaveButton.jsx +69 -0
  178. package/bootstrap/services/portal/src/ui/saveButton/saveButton.less +0 -0
  179. package/bootstrap/services/rabbit-mq/Dockerfile.prod +9 -0
  180. package/bootstrap/services/user/Dockerfile +23 -0
  181. package/bootstrap/services/user/Dockerfile.prod +37 -0
  182. package/bootstrap/services/user/README.md +8 -0
  183. package/bootstrap/services/user/package.json +16 -0
  184. package/bootstrap/services/user/src/app.js +45 -0
  185. package/bootstrap/services/user/src/commands/session.handler.js +23 -0
  186. package/bootstrap/services/user/src/commands/user.handler.js +286 -0
  187. package/bootstrap/services/user/src/config.js +73 -0
  188. package/bootstrap/services/user/src/controllers/http.controller.js +156 -0
  189. package/bootstrap/services/user/src/controllers/message.controller.js +51 -0
  190. package/bootstrap/services/user/src/db/migrations/01-init.js +50 -0
  191. package/bootstrap/services/user/src/db/migrations/02-user-service-init.js +63 -0
  192. package/bootstrap/services/user/src/db/migrations/03-unauth-sessions.js +43 -0
  193. package/bootstrap/services/user/src/db/seeders/development/01-root-user.js +29 -0
  194. package/bootstrap/services/user/src/db/seeders/development/02-portal-admin-user.js +27 -0
  195. package/bootstrap/services/user/src/db/seeders/production/01-root-user.js +29 -0
  196. package/bootstrap/services/user/src/policies/user.policy.js +81 -0
  197. package/bootstrap/services/user/src/schema/user.schema.js +69 -0
  198. package/bootstrap/services/user/src/services/authentication.service.js +127 -0
  199. package/bootstrap/services/user/src/services/session.service.js +58 -0
  200. package/bootstrap/services/user/src/services/user.service.js +130 -0
  201. package/bootstrap/services/user/src/tests/user.test.js +126 -0
  202. package/package.json +3 -7
  203. package/src/agent/agent.client.js +28 -0
  204. package/src/agent/commands.js +48 -0
  205. package/src/cli.js +30 -0
  206. package/src/config.js +8 -0
  207. package/src/control/commands.js +156 -0
  208. package/src/control/control.client.js +127 -0
  209. package/src/dev/commands.js +71 -0
  210. package/src/dev/dev.service.js +320 -0
  211. package/src/engine/infra.js +142 -0
  212. package/src/helpers/helpers.js +63 -0
  213. package/src/profiles/command.js +170 -0
  214. package/src/profiles/profiles.client.js +101 -0
  215. package/src/scaffolder/commands.js +123 -0
  216. package/src/scaffolder/scaffolder.handler.js +252 -0
  217. package/src/services/client.js +174 -0
  218. package/templates/service/Dockerfile.hbs +20 -0
  219. package/templates/service/app.js.hbs +38 -0
  220. package/templates/service/commands/{{serviceName}}.handler.js.hbs +97 -0
  221. package/templates/service/config.js.hbs +48 -0
  222. package/templates/service/controllers/http.controller.js.hbs +87 -0
  223. package/templates/service/controllers/message.controller.js.hbs +51 -0
  224. package/templates/service/db/migrations/01-init.js.hbs +50 -0
  225. package/templates/service/db/migrations/02-{{lowerCase serviceName}}-service-init.js.hbs +23 -0
  226. package/templates/service/package.json.hbs +18 -0
  227. package/templates/service/policies/{{serviceName}}.policy.js.hbs +49 -0
  228. package/templates/service/schema/{{serviceName}}.schema.js.hbs +14 -0
  229. package/templates/service/services/{{serviceName}}.service.js.hbs +32 -0
  230. package/dist/cli.js +0 -18
@@ -0,0 +1,231 @@
1
+ import { commands, logger, message, db } from '@gnar-engine/core';
2
+ import { registry } from "../services/registry.service.js";
3
+ import { task } from "../services/task.service.js";
4
+ import { reset } from "../services/reset.service.js";
5
+
6
+
7
+ /**
8
+ * Run migrations
9
+ *
10
+ * @param {Object} params
11
+ * @param {string} params.service The service name
12
+ * @param {string} params.migration The migration name
13
+ * @returns {Promise<void>}
14
+ */
15
+ commands.register('controlService.runMigrations', async ({service, migration}) => {
16
+ let response;
17
+
18
+ // run single migration
19
+ if (service && migration) {
20
+ logger.info(`Running migration: ${migration} for service: ${service}`);
21
+
22
+ const payload = {
23
+ method: 'runMigrations',
24
+ data: {
25
+ migration: migration
26
+ }
27
+ };
28
+
29
+ // send message to service queue
30
+ try {
31
+ response = await message.sendAndForget(service.name, payload);
32
+ } catch (error) {
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ // run all migrations for service
38
+ else if (service) {
39
+ logger.info(`Running migrations for service: ${service}`);
40
+
41
+ if (service == 'controlService') {
42
+ try {
43
+ await migrations.runMigrations();
44
+ return;
45
+ } catch (error) {
46
+ throw error;
47
+ }
48
+ }
49
+
50
+ const payload = {
51
+ method: 'runMigrations',
52
+ };
53
+
54
+ // send message to service queue
55
+ try {
56
+ response = await message.sendAndForget(service.name, payload);
57
+ } catch (error) {
58
+ throw error;
59
+ }
60
+ }
61
+
62
+ // run all migrations
63
+ else {
64
+ // get registered services
65
+ const services = await registry.getServices();
66
+
67
+ logger.info(`Running migrations for services: ${JSON.stringify(services)}`);
68
+
69
+ // run migrations for each service
70
+ const migrationPromises = services.map(async (service) => {
71
+ const payload = {
72
+ method: 'runMigrations'
73
+ };
74
+
75
+ try {
76
+ return await message.sendAndForget(service.name, payload);
77
+ } catch (error) {
78
+ throw error;
79
+ }
80
+ });
81
+
82
+ const responses = await Promise.all(migrationPromises);
83
+ }
84
+
85
+ return response;
86
+ })
87
+
88
+ /**
89
+ * Run seeders
90
+ *
91
+ * @param {Object} params
92
+ * @param {string} params.service The service name
93
+ * @returns {Promise<void>}
94
+ */
95
+ commands.register('controlService.runSeeders', async ({service}) => {
96
+ let response;
97
+
98
+ // run seeders for service
99
+ if (service) {
100
+ logger.info(`Running seeders for service: ${service}`);
101
+
102
+ const payload = {
103
+ method: 'runSeeders',
104
+ };
105
+
106
+ // send message to service queue
107
+ try {
108
+ response = await message.sendAndForget(service.name, payload);
109
+ } catch (error) {
110
+ throw error
111
+ }
112
+ }
113
+
114
+ // run seeders for all services
115
+ else {
116
+ // get registered services
117
+ const services = await registry.getServices();
118
+
119
+ logger.info(`Running seeders for services: ${JSON.stringify(services)}`);
120
+
121
+ // run seeders for each service
122
+ for (const service of services) {
123
+ const payload = {
124
+ method: 'runSeeders',
125
+ };
126
+
127
+ // send message to service queue
128
+ try {
129
+ response = await message.sendAndForget(service.name, payload);
130
+ } catch (error) {
131
+ throw error
132
+ }
133
+ }
134
+ }
135
+
136
+ return response;
137
+ })
138
+
139
+ /**
140
+ * Run full database reset (centrally)
141
+ */
142
+ commands.register('controlService.runReset', async () => {
143
+
144
+ if (process.env.NODE_ENV !== 'development') {
145
+ throw new Error("Reset is only allowed in development environment");
146
+ }
147
+
148
+ // delete all mongodb collections
149
+ try {
150
+ await reset.dropMongoCollections();
151
+ } catch (error) {
152
+ logger.error("Error resetting MongoDB: " + error);
153
+ }
154
+
155
+ // delete all mysql databases
156
+ try {
157
+ await reset.dropMysqlDatabases();
158
+ } catch (error) {
159
+ logger.error("Error resetting MYSQL: " + error);
160
+ }
161
+ })
162
+
163
+ /**
164
+ * Run health check
165
+ * ----------------
166
+ *
167
+ * Checks services are available and subscribed to RabbitMQ
168
+ */
169
+ commands.register('controlService.runHealthcheck', async () => {
170
+
171
+ const services = [
172
+ 'userService',
173
+ 'contactService',
174
+ 'productService',
175
+ 'cartService',
176
+ 'orderService',
177
+ 'subscriptionService',
178
+ 'checkoutService'
179
+ ]
180
+
181
+ const results = await Promise.all(
182
+ services.map(async (service) => {
183
+ try {
184
+ const result = await message.sendAwaitResponse(service, {
185
+ method: 'healthCheck'
186
+ });
187
+ return { service, result };
188
+ } catch (error) {
189
+ return {
190
+ service,
191
+ error: `Error checking health of ${service}: ${error}`
192
+ };
193
+ }
194
+ })
195
+ );
196
+
197
+ const errors = results
198
+ .filter(r => r.error)
199
+ .map(r => r.error);
200
+
201
+ // Check for failed tasks
202
+ const failedTasks = await task.getTasksByStatus({
203
+ status: 'failed'
204
+ });
205
+
206
+ if (failedTasks.length > 0) {
207
+ errors.push(`${failedTasks.length} failed tasks`);
208
+ }
209
+
210
+ // Aggregate errors
211
+ if (errors.length > 0) {
212
+ throw new error.failedHealthCheck(JSON.stringify(errors));
213
+ }
214
+
215
+ logger.info('Health check passed');
216
+ return;
217
+ })
218
+
219
+ /**
220
+ * Internal health check (kills process if it fails)
221
+ */
222
+ commands.register('controlService.internalHealthCheck', async () => {
223
+
224
+ // ensure db connection
225
+ try {
226
+ await db.checkConnection();
227
+ } catch (err) {
228
+ logger.error('[Internal health check] Failed - Exiting. Error connecting to MYSQL: ' + err);
229
+ process.exit(1);
230
+ }
231
+ })
@@ -0,0 +1,81 @@
1
+ import { commands, logger } from '@gnar-engine/core';
2
+ import { registry } from '../services/registry.service.js';
3
+ import { validateService } from '../schema/control.schema.js';
4
+
5
+
6
+ /**
7
+ * Register a service in the registry
8
+ *
9
+ * @param {Object} params
10
+ * @param {Object} params.serivce The service data
11
+ * @returns {Promise<Object>} The service data
12
+ */
13
+ commands.register('controlService.registerService', async ({service}) => {
14
+
15
+ // validate the service
16
+ const errors = validateService(service);
17
+
18
+ if (errors) {
19
+ throw new Error(JSON.stringify(errors));
20
+ }
21
+
22
+ // register the service
23
+ let serviceId;
24
+ try {
25
+ const existingServices = await registry.getServices();
26
+
27
+ if (existingServices.find(s => s.name === service.name)) {
28
+ await registry.updateService(service);
29
+ } else {
30
+ serviceId = await registry.registerService(service);
31
+ }
32
+
33
+ logger.info(`Service registered: ${service.name}`);
34
+ } catch (error) {
35
+ throw error;
36
+ }
37
+
38
+ // return the service data
39
+ return {
40
+ id: serviceId,
41
+ ...service
42
+ };
43
+ })
44
+
45
+ /**
46
+ * Get all registered services
47
+ *
48
+ * @param {Object} params
49
+ * @param {boolean} params.includeManifests Whether to include service manifests
50
+ * @returns {Promise<Array>} List of services
51
+ */
52
+ commands.register('controlService.getServices', async ({includeManifests}) => {
53
+ let services;
54
+ try {
55
+ if (includeManifests) {
56
+ services = await registry.getServicesWithManifests();
57
+ } else {
58
+ services = await registry.getServices();
59
+ }
60
+ } catch (error) {
61
+ throw error;
62
+ }
63
+
64
+ return services;
65
+ })
66
+
67
+ /**
68
+ * Get all manifests
69
+ *
70
+ * @returns {Promise<Array>} List of manifests by service
71
+ */
72
+ commands.register('controlService.getManifests', async() => {
73
+ let manifests;
74
+ try {
75
+ manifests = await registry.getManifests();
76
+ } catch (error) {
77
+ throw error;
78
+ }
79
+
80
+ return manifests;
81
+ })
@@ -0,0 +1,247 @@
1
+ import { message, commands, logger } from '@gnar-engine/core';
2
+ import { task } from '../services/task.service.js';
3
+ import { validateTask } from '../schema/control.schema.js';
4
+
5
+
6
+ /**
7
+ * Handle task batch
8
+ *
9
+ * @param {string} status - Task status
10
+ */
11
+ commands.register('controlService.handleTaskBatch', async (status = null) => {
12
+
13
+ if (!status) {
14
+ status = 'scheduled';
15
+ }
16
+
17
+ logger.info(`Running Task batch... Status: ${status}`);
18
+
19
+ const tasks = await task.getTaskBatch({
20
+ status: status
21
+ });
22
+
23
+ logger.info('Found tasks: ' + JSON.stringify(tasks));
24
+
25
+ const errors = [];
26
+
27
+ const taskPromises = tasks.map(taskObj => (
28
+ (async () => {
29
+ let success = false;
30
+
31
+ try {
32
+ logger.info(taskObj);
33
+ await handleTask(taskObj);
34
+ await task.updateTaskStatus({ id: taskObj.id, status: 'completed' });
35
+
36
+ success = true;
37
+ } catch (error) {
38
+ logger.error(`Error handling task ${taskObj.id}: ${error}`);
39
+ errors.push({
40
+ task: taskObj,
41
+ error: error.message
42
+ });
43
+ await task.updateTaskStatus({ id: taskObj.id, status: 'failed' });
44
+ }
45
+
46
+ try {
47
+ if (success) {
48
+ if (taskObj.rescheduleCentrallyOnSuccess) {
49
+ await centrallyRescheduleTask({ task: taskObj });
50
+ }
51
+ } else {
52
+ if (taskObj.rescheduleCentrallyOnFailure) {
53
+ await centrallyRescheduleTask({ task: taskObj });
54
+ }
55
+ }
56
+ } catch (error) {
57
+ logger.error(`Error rescheduling task ${taskObj.id}: ${error}`);
58
+ errors.push({
59
+ task: taskObj,
60
+ error: error.message
61
+ });
62
+ }
63
+ })()
64
+ ));
65
+
66
+ await Promise.allSettled(taskPromises);
67
+
68
+ if (errors.length > 0) {
69
+ logger.info(JSON.stringify(errors));
70
+ return errors;
71
+ }
72
+ })
73
+
74
+ /**
75
+ * Handle task
76
+ *
77
+ * @param {Object} taskObj
78
+ * @returns {Promise<Object>} The task data
79
+ */
80
+ commands.register('controlService.handleTask', async (taskObj) => {
81
+ try {
82
+ logger.info(`Running Task: ${taskObj.name} - ${taskObj.id}`);
83
+
84
+ // Add the task id to the payload
85
+ taskObj.payload.taskId = taskObj.id;
86
+
87
+ // Execute task
88
+ try {
89
+ const response = await message.sendAwaitResponse(taskObj.handlerServiceName, {
90
+ method: taskObj.handlerName,
91
+ data: taskObj.payload
92
+ });
93
+
94
+ // if there are errors (these are for runtime errors - should not be used for retrying)
95
+ if (response.error || response.status === 'failed') {
96
+ logger.error(`Error executing task ${taskObj.name}: ${response.error}. Failing task...`);
97
+ throw new Error(`Error executing task ${taskObj.name}: ${response.error}`);
98
+ }
99
+ } catch (error) {
100
+ throw new Error(error);
101
+ }
102
+ } catch (error) {
103
+ await task.updateTaskStatus({id: taskObj.id, status: 'failed'});
104
+ throw new Error(`Error handling task: ${taskObj.name} - ${error}`);
105
+ }
106
+ })
107
+
108
+ /**
109
+ * Centrally reschedule task
110
+ *
111
+ * @param {Object} params
112
+ * @param {Object} params.task The task data
113
+ * @returns {Promise<Object>} The task data
114
+ */
115
+ commands.register('controlService.centrallyRescheduleTask', async ({task: taskObj}) => {
116
+ try {
117
+ let newScheduled;
118
+ let oldScheduledDate = new Date(taskObj.scheduled);
119
+
120
+ switch (taskObj.recurringInterval) {
121
+ case 'hourly':
122
+ newScheduled = new Date(oldScheduledDate.getTime() + taskObj.recurringIntervalCount * 60 * 60 * 1000);
123
+ break;
124
+ case 'daily':
125
+ newScheduled = new Date(oldScheduledDate.getTime() + taskObj.recurringIntervalCount * 24 * 60 * 60 * 1000);
126
+ break;
127
+ case 'weekly':
128
+ newScheduled = new Date(oldScheduledDate.getTime() + taskObj.recurringIntervalCount * 7 * 24 * 60 * 60 * 1000);
129
+ break;
130
+ case 'monthly': {
131
+ let scheduledDate = new Date(oldScheduledDate);
132
+ scheduledDate.setMonth(scheduledDate.getMonth() + taskObj.recurringIntervalCount);
133
+ newScheduled = scheduledDate;
134
+ break;
135
+ }
136
+ case 'yearly': {
137
+ let scheduledDate = new Date(oldScheduledDate);
138
+ scheduledDate.setFullYear(scheduledDate.getFullYear() + taskObj.recurringIntervalCount);
139
+ newScheduled = scheduledDate;
140
+ break;
141
+ }
142
+
143
+ default:
144
+ throw new Error('Invalid scheduling interval: ' + taskObj.recurringInterval);
145
+ }
146
+
147
+ // reschedule the task
148
+ delete taskObj.id;
149
+
150
+ await scheduleTask({
151
+ task: {
152
+ ...taskObj,
153
+ scheduled: newScheduled,
154
+ status: 'scheduled'
155
+ }
156
+ });
157
+
158
+ } catch (error) {
159
+ logger.error('Error rescheduling task centrally: ' + error);
160
+ throw new Error(`Error rescheduling task: ${error}`);
161
+ }
162
+ })
163
+
164
+ /**
165
+ * Schedule task
166
+ *
167
+ * @param {Object} params
168
+ * @param {Object} params.task The task data
169
+ * @returns {Promise<Object>} The task data
170
+ */
171
+ commands.register('controlService.scheduleTask', async ({task: taskObj}) => {
172
+ try {
173
+ // prep task object
174
+ taskObj.scheduled = new Date(taskObj.scheduled).toISOString().slice(0, 19).replace('T', ' ');
175
+ taskObj.rescheduleCentrallyOnSuccess = !!taskObj.rescheduleCentrallyOnSuccess;
176
+ taskObj.rescheduleCentrallyOnFailure = !!taskObj.rescheduleCentrallyOnFailure;
177
+ taskObj.recurringIntervalCount = taskObj.recurringIntervalCount || 1;
178
+
179
+ // validate the task
180
+ const errors = validateTask(taskObj);
181
+
182
+ if (errors) {
183
+ throw new Error(JSON.stringify(errors));
184
+ }
185
+
186
+ // check indempotency
187
+ if (taskObj.idempotencyKey) {
188
+ const isIndempotent = await task.checkIndempotent({
189
+ idempotencyKey: taskObj.idempotencyKey
190
+ });
191
+
192
+ if (!isIndempotent) {
193
+ throw new Error('Task task is already scheduled: ' + taskObj.idempotencyKey);
194
+ }
195
+ }
196
+
197
+ // schedule the task
198
+ const taskId = await task.scheduleTask(taskObj);
199
+
200
+ logger.info('Scheduled task: ' + JSON.stringify(taskObj));
201
+
202
+ // return the task data
203
+ return {
204
+ id: taskId,
205
+ ...taskObj
206
+ }
207
+ } catch (error) {
208
+ logger.error('Error scheduling task: ' + error);
209
+ throw new Error(`Error scheduling task: ${error}`);
210
+ }
211
+ })
212
+
213
+ /**
214
+ * Get tasks by status
215
+ *
216
+ * @param {string} status - Task status
217
+ */
218
+ commands.register('controlService.getTasksByStatus', async (status) => {
219
+ try {
220
+ if (!status) {
221
+ status = 'scheduled';
222
+ }
223
+
224
+ const tasks = await task.getTasksByStatus({
225
+ status: status
226
+ });
227
+
228
+ return tasks;
229
+ } catch (error) {
230
+ logger.error('Error getting tasks by status in handler: ' + error);
231
+ throw new Error(`Error getting tasks by status in handler: ${error}`);
232
+ }
233
+ })
234
+
235
+ /**
236
+ * Delete failed tasks
237
+ */
238
+ commands.register('controlService.deleteFailedTasks', async () => {
239
+ try {
240
+ const deleteFailedTasks = await task.deleteFailedTasks();
241
+ logger.info('Deleted failed tasks: ' + deleteFailedTasks);
242
+ return deleteFailedTasks;
243
+ } catch (error) {
244
+ logger.error('Error deleting failed tasks: ' + error);
245
+ throw new Error(`Error deleting failed tasks: ${error}`);
246
+ }
247
+ })
@@ -0,0 +1,55 @@
1
+
2
+ /**
3
+ * Gnar Engine Service Config
4
+ */
5
+ export const config = {
6
+ // service name
7
+ serviceName: 'controlService',
8
+
9
+ // microservice | modular-monolith
10
+ architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
11
+
12
+ // web server
13
+ http: {
14
+ allowedOrigins: [],
15
+ allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
16
+ allowedHeaders: ['Content-Type', 'Authorization'],
17
+ rateLimiting: {
18
+ max: 5,
19
+ timeWindow: '1 minute',
20
+ }
21
+ },
22
+
23
+ // database
24
+ db: {
25
+ // type: mongodb | mysql
26
+ type: 'mysql',
27
+
28
+ // MongoDB
29
+ connectionUrl: process.env.CONTROL_MONGO_URL,
30
+ connectionArgs: {},
31
+
32
+ // MySQL
33
+ host: process.env.CONTROL_MYSQL_HOST,
34
+ user: process.env.CONTROL_MYSQL_USER,
35
+ password: process.env.CONTROL_MYSQL_PASSWORD,
36
+ database: process.env.CONTROL_MYSQL_DATABASE,
37
+ // port: process.env.CONTROL_MYSQL_PORT,
38
+ connectionLimit: 10,
39
+ queueLimit: 20,
40
+ maxRetries: 5
41
+ },
42
+
43
+ // message broker
44
+ message: {
45
+ url: process.env.RABBITMQ_URL,
46
+ queueName: 'controlServiceQueue',
47
+ prefetch: 20,
48
+ },
49
+
50
+ // web socket client & server
51
+ webSockets: {
52
+ reconnectInterval: 5000,
53
+ maxInitialConnectionAttempts: 5
54
+ }
55
+ }