@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.
- package/assets/gnar-engine-logo-white.svg +9 -0
- package/bootstrap/deploy.localdev.yml +51 -0
- package/bootstrap/secrets.localdev.yml +27 -0
- package/bootstrap/services/agent/Dockerfile +23 -0
- package/bootstrap/services/agent/notes.md +28 -0
- package/bootstrap/services/agent/package.json +16 -0
- package/bootstrap/services/agent/src/app.js +52 -0
- package/bootstrap/services/agent/src/commands/agent.handler.js +104 -0
- package/bootstrap/services/agent/src/config.js +52 -0
- package/bootstrap/services/agent/src/controllers/http.controller.js +44 -0
- package/bootstrap/services/agent/src/controllers/message.controller.js +51 -0
- package/bootstrap/services/agent/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/agent/src/db/migrations/02-agent-service-init.js +36 -0
- package/bootstrap/services/agent/src/policies/agent.policy.js +13 -0
- package/bootstrap/services/agent/src/schema/Agent.schema.js +17 -0
- package/bootstrap/services/agent/src/services/agent.service.js +259 -0
- package/bootstrap/services/agent/src/services/chatgpt.service.js +46 -0
- package/bootstrap/services/agent/src/services/manifest.service.js +21 -0
- package/bootstrap/services/control/Dockerfile +23 -0
- package/bootstrap/services/control/Dockerfile.prod +37 -0
- package/bootstrap/services/control/README.md +25 -0
- package/bootstrap/services/control/package.json +16 -0
- package/bootstrap/services/control/src/app.js +45 -0
- package/bootstrap/services/control/src/commands/control.handler.js +231 -0
- package/bootstrap/services/control/src/commands/service.handler.js +81 -0
- package/bootstrap/services/control/src/commands/task.handler.js +247 -0
- package/bootstrap/services/control/src/config.js +55 -0
- package/bootstrap/services/control/src/controllers/http.controller.js +228 -0
- package/bootstrap/services/control/src/controllers/message.controller.js +40 -0
- package/bootstrap/services/control/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/control/src/db/migrations/02-control-service-init.js +60 -0
- package/bootstrap/services/control/src/db/migrations/03-alter-tasks.js +29 -0
- package/bootstrap/services/control/src/policies/task.policy.js +53 -0
- package/bootstrap/services/control/src/schema/control.schema.js +42 -0
- package/bootstrap/services/control/src/services/registry.service.js +83 -0
- package/bootstrap/services/control/src/services/reset.service.js +28 -0
- package/bootstrap/services/control/src/services/task.service.js +153 -0
- package/bootstrap/services/control/src/tests/control.test.js +50 -0
- package/bootstrap/services/notification/Dockerfile +23 -0
- package/bootstrap/services/notification/Dockerfile.prod +37 -0
- package/bootstrap/services/notification/README.md +3 -0
- package/bootstrap/services/notification/package.json +34 -0
- package/bootstrap/services/notification/src/app.js +51 -0
- package/bootstrap/services/notification/src/commands/command-bus.js +20 -0
- package/bootstrap/services/notification/src/commands/handlers/control.handler.js +18 -0
- package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +157 -0
- package/bootstrap/services/notification/src/config.js +15 -0
- package/bootstrap/services/notification/src/controllers/message.controller.js +82 -0
- package/bootstrap/services/notification/src/services/logger.service.js +16 -0
- package/bootstrap/services/notification/src/services/ses.service.js +23 -0
- package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +136 -0
- package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +87 -0
- package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +132 -0
- package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +77 -0
- package/bootstrap/services/notification/src/tests/notification.test.js +0 -0
- package/bootstrap/services/portal/Dockerfile +23 -0
- package/bootstrap/services/portal/Dockerfile.remote +40 -0
- package/bootstrap/services/portal/README.md +22 -0
- package/bootstrap/services/portal/nginx.conf +12 -0
- package/bootstrap/services/portal/package.json +59 -0
- package/bootstrap/services/portal/public/favicon.ico +0 -0
- package/bootstrap/services/portal/public/gnar-white.png +0 -0
- package/bootstrap/services/portal/public/gnarengine-logo-black.png +0 -0
- package/bootstrap/services/portal/public/index.html +43 -0
- package/bootstrap/services/portal/public/logo192.png +0 -0
- package/bootstrap/services/portal/public/logo512.png +0 -0
- package/bootstrap/services/portal/public/manifest.json +25 -0
- package/bootstrap/services/portal/public/robots.txt +3 -0
- package/bootstrap/services/portal/src/App.js +56 -0
- package/bootstrap/services/portal/src/assets/Logo_Anchord_Black.svg +1 -0
- package/bootstrap/services/portal/src/assets/Logo_Anchord_Black_Green.svg +1 -0
- package/bootstrap/services/portal/src/assets/Logo_Anchord_White_Green.svg +1 -0
- package/bootstrap/services/portal/src/assets/activity.svg +3 -0
- package/bootstrap/services/portal/src/assets/arrow.svg +3 -0
- package/bootstrap/services/portal/src/assets/bin-white.svg +3 -0
- package/bootstrap/services/portal/src/assets/bin.svg +3 -0
- package/bootstrap/services/portal/src/assets/check.svg +3 -0
- package/bootstrap/services/portal/src/assets/chevron.svg +3 -0
- package/bootstrap/services/portal/src/assets/contact.svg +3 -0
- package/bootstrap/services/portal/src/assets/dots-vertical.svg +5 -0
- package/bootstrap/services/portal/src/assets/eye-off.svg +3 -0
- package/bootstrap/services/portal/src/assets/eye.svg +4 -0
- package/bootstrap/services/portal/src/assets/gnar-engine-black.svg +47 -0
- package/bootstrap/services/portal/src/assets/gnar-engine-white.svg +47 -0
- package/bootstrap/services/portal/src/assets/gnar_engine.svg +3 -0
- package/bootstrap/services/portal/src/assets/gnarengine-logo-black.png +0 -0
- package/bootstrap/services/portal/src/assets/home.svg +3 -0
- package/bootstrap/services/portal/src/assets/link.svg +3 -0
- package/bootstrap/services/portal/src/assets/lock.svg +3 -0
- package/bootstrap/services/portal/src/assets/package.svg +4 -0
- package/bootstrap/services/portal/src/assets/raffle.svg +3 -0
- package/bootstrap/services/portal/src/assets/settings.svg +4 -0
- package/bootstrap/services/portal/src/assets/shopping-bag.svg +3 -0
- package/bootstrap/services/portal/src/assets/user-black.svg +3 -0
- package/bootstrap/services/portal/src/assets/user.svg +3 -0
- package/bootstrap/services/portal/src/assets/users.svg +3 -0
- package/bootstrap/services/portal/src/assets/wallet.svg +3 -0
- package/bootstrap/services/portal/src/css/style.css +1007 -0
- package/bootstrap/services/portal/src/data/data.js +70 -0
- package/bootstrap/services/portal/src/features/attributeFormRow/AttributeFormRow.jsx +32 -0
- package/bootstrap/services/portal/src/features/billingShipping/BillingShipping.jsx +160 -0
- package/bootstrap/services/portal/src/features/crud/crudEdit.less +230 -0
- package/bootstrap/services/portal/src/features/crud/crudList.less +134 -0
- package/bootstrap/services/portal/src/features/crud/crudPage.less +31 -0
- package/bootstrap/services/portal/src/features/crudContact/CrudContactList.jsx +108 -0
- package/bootstrap/services/portal/src/features/crudContact/CrudContactSingle.jsx +243 -0
- package/bootstrap/services/portal/src/features/crudOrder/CrudOrderList.jsx +109 -0
- package/bootstrap/services/portal/src/features/crudOrder/CrudOrderSingle.jsx +315 -0
- package/bootstrap/services/portal/src/features/crudProducts/CrudProductList.jsx +104 -0
- package/bootstrap/services/portal/src/features/crudProducts/CrudProductSingle.jsx +388 -0
- package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesList.jsx +104 -0
- package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesSingle.jsx +208 -0
- package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionList.jsx +110 -0
- package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionSingle.jsx +261 -0
- package/bootstrap/services/portal/src/features/crudUser/CrudUserList.jsx +107 -0
- package/bootstrap/services/portal/src/features/crudUser/CrudUserSingle.jsx +402 -0
- package/bootstrap/services/portal/src/features/inventoryFormRow/InventoryFormRow.jsx +30 -0
- package/bootstrap/services/portal/src/features/lineItems/LineItems.jsx +113 -0
- package/bootstrap/services/portal/src/features/loginForm/LoginForm.jsx +56 -0
- package/bootstrap/services/portal/src/features/loginForm/loginForm.less +56 -0
- package/bootstrap/services/portal/src/features/notes/Notes.jsx +18 -0
- package/bootstrap/services/portal/src/features/passwordReset/PasswordResetForm.jsx +96 -0
- package/bootstrap/services/portal/src/features/passwordReset/PasswordResetRequestForm.jsx +74 -0
- package/bootstrap/services/portal/src/features/priceFormRow/PriceFormRow.jsx +102 -0
- package/bootstrap/services/portal/src/features/priceFormRow/priceFormRow.less +24 -0
- package/bootstrap/services/portal/src/features/raffleEntriesList/RaffleEntriesList.jsx +99 -0
- package/bootstrap/services/portal/src/features/raffleProductFormRow/RaffleProductFormRow.jsx +46 -0
- package/bootstrap/services/portal/src/features/sidebar/Sidebar.jsx +64 -0
- package/bootstrap/services/portal/src/features/sidebar/sidebar.less +49 -0
- package/bootstrap/services/portal/src/features/skus/Skus.jsx +109 -0
- package/bootstrap/services/portal/src/features/subscriptionSchedule/SubscriptionSchedule.jsx +44 -0
- package/bootstrap/services/portal/src/features/taxonomyFormRow/TaxonomyFormRow.jsx +32 -0
- package/bootstrap/services/portal/src/features/user/User.jsx +54 -0
- package/bootstrap/services/portal/src/features/user/user.less +57 -0
- package/bootstrap/services/portal/src/includes/utilities.js +259 -0
- package/bootstrap/services/portal/src/index.js +14 -0
- package/bootstrap/services/portal/src/layouts/CrudLayout.jsx +50 -0
- package/bootstrap/services/portal/src/layouts/LoginLayout.jsx +17 -0
- package/bootstrap/services/portal/src/layouts/PortalLayout.jsx +48 -0
- package/bootstrap/services/portal/src/layouts/loginLayout.less +33 -0
- package/bootstrap/services/portal/src/layouts/portalLayout.less +67 -0
- package/bootstrap/services/portal/src/pages/contacts/Contacts.jsx +199 -0
- package/bootstrap/services/portal/src/pages/dashboard/Dashboard.jsx +17 -0
- package/bootstrap/services/portal/src/pages/integrations/Integrations.jsx +10 -0
- package/bootstrap/services/portal/src/pages/login/Login.jsx +15 -0
- package/bootstrap/services/portal/src/pages/login/login.less +10 -0
- package/bootstrap/services/portal/src/pages/orders/Orders.jsx +199 -0
- package/bootstrap/services/portal/src/pages/passwordReset/PasswordResetPage.jsx +15 -0
- package/bootstrap/services/portal/src/pages/passwordResetRequest/PasswordResetRequestPage.jsx +15 -0
- package/bootstrap/services/portal/src/pages/payments/Payments.jsx +10 -0
- package/bootstrap/services/portal/src/pages/portal/Portal.jsx +43 -0
- package/bootstrap/services/portal/src/pages/products/Products.jsx +212 -0
- package/bootstrap/services/portal/src/pages/raffleEntries/RaffleEntries.jsx +124 -0
- package/bootstrap/services/portal/src/pages/raffles/Raffles.jsx +186 -0
- package/bootstrap/services/portal/src/pages/reports/Reports.jsx +10 -0
- package/bootstrap/services/portal/src/pages/settings/Settings.jsx +10 -0
- package/bootstrap/services/portal/src/pages/subscriptions/Subscriptions.jsx +199 -0
- package/bootstrap/services/portal/src/pages/users/Users.jsx +193 -0
- package/bootstrap/services/portal/src/pages/users/users.less +25 -0
- package/bootstrap/services/portal/src/slices/authSlice.js +71 -0
- package/bootstrap/services/portal/src/store/configureStore.js +12 -0
- package/bootstrap/services/portal/src/styles/global.less +159 -0
- package/bootstrap/services/portal/src/styles/inputs.less +157 -0
- package/bootstrap/services/portal/src/styles/main.less +26 -0
- package/bootstrap/services/portal/src/ui/collapsible/Collapsible.jsx +97 -0
- package/bootstrap/services/portal/src/ui/collapsible/collapsible.less +23 -0
- package/bootstrap/services/portal/src/ui/customCheckbox/CustomCheckbox.jsx +17 -0
- package/bootstrap/services/portal/src/ui/customCheckbox/customCheckbox.less +42 -0
- package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelect.jsx +63 -0
- package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelectPeriod.jsx +63 -0
- package/bootstrap/services/portal/src/ui/customSelect/CustomSelect.jsx +63 -0
- package/bootstrap/services/portal/src/ui/customSelect/customSelect.less +92 -0
- package/bootstrap/services/portal/src/ui/goBack/GoBack.jsx +19 -0
- package/bootstrap/services/portal/src/ui/loader/Loader.jsx +12 -0
- package/bootstrap/services/portal/src/ui/pagination/Pagination.jsx +23 -0
- package/bootstrap/services/portal/src/ui/repeater/Repeater.jsx +29 -0
- package/bootstrap/services/portal/src/ui/saveButton/SaveButton.jsx +69 -0
- package/bootstrap/services/portal/src/ui/saveButton/saveButton.less +0 -0
- package/bootstrap/services/rabbit-mq/Dockerfile.prod +9 -0
- package/bootstrap/services/user/Dockerfile +23 -0
- package/bootstrap/services/user/Dockerfile.prod +37 -0
- package/bootstrap/services/user/README.md +8 -0
- package/bootstrap/services/user/package.json +16 -0
- package/bootstrap/services/user/src/app.js +45 -0
- package/bootstrap/services/user/src/commands/session.handler.js +23 -0
- package/bootstrap/services/user/src/commands/user.handler.js +286 -0
- package/bootstrap/services/user/src/config.js +73 -0
- package/bootstrap/services/user/src/controllers/http.controller.js +156 -0
- package/bootstrap/services/user/src/controllers/message.controller.js +51 -0
- package/bootstrap/services/user/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/user/src/db/migrations/02-user-service-init.js +63 -0
- package/bootstrap/services/user/src/db/migrations/03-unauth-sessions.js +43 -0
- package/bootstrap/services/user/src/db/seeders/development/01-root-user.js +29 -0
- package/bootstrap/services/user/src/db/seeders/development/02-portal-admin-user.js +27 -0
- package/bootstrap/services/user/src/db/seeders/production/01-root-user.js +29 -0
- package/bootstrap/services/user/src/policies/user.policy.js +81 -0
- package/bootstrap/services/user/src/schema/user.schema.js +69 -0
- package/bootstrap/services/user/src/services/authentication.service.js +127 -0
- package/bootstrap/services/user/src/services/session.service.js +58 -0
- package/bootstrap/services/user/src/services/user.service.js +130 -0
- package/bootstrap/services/user/src/tests/user.test.js +126 -0
- package/package.json +3 -7
- package/src/agent/agent.client.js +28 -0
- package/src/agent/commands.js +48 -0
- package/src/cli.js +30 -0
- package/src/config.js +8 -0
- package/src/control/commands.js +156 -0
- package/src/control/control.client.js +127 -0
- package/src/dev/commands.js +71 -0
- package/src/dev/dev.service.js +320 -0
- package/src/engine/infra.js +142 -0
- package/src/helpers/helpers.js +63 -0
- package/src/profiles/command.js +170 -0
- package/src/profiles/profiles.client.js +101 -0
- package/src/scaffolder/commands.js +123 -0
- package/src/scaffolder/scaffolder.handler.js +252 -0
- package/src/services/client.js +174 -0
- package/templates/service/Dockerfile.hbs +20 -0
- package/templates/service/app.js.hbs +38 -0
- package/templates/service/commands/{{serviceName}}.handler.js.hbs +97 -0
- package/templates/service/config.js.hbs +48 -0
- package/templates/service/controllers/http.controller.js.hbs +87 -0
- package/templates/service/controllers/message.controller.js.hbs +51 -0
- package/templates/service/db/migrations/01-init.js.hbs +50 -0
- package/templates/service/db/migrations/02-{{lowerCase serviceName}}-service-init.js.hbs +23 -0
- package/templates/service/package.json.hbs +18 -0
- package/templates/service/policies/{{serviceName}}.policy.js.hbs +49 -0
- package/templates/service/schema/{{serviceName}}.schema.js.hbs +14 -0
- package/templates/service/services/{{serviceName}}.service.js.hbs +32 -0
- 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
|
+
}
|