@gnar-engine/cli 1.0.0 → 1.0.1
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,153 @@
|
|
|
1
|
+
import { logger, utils, db } from '@gnar-engine/core';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Task Service
|
|
6
|
+
*/
|
|
7
|
+
export const task = {
|
|
8
|
+
|
|
9
|
+
// schedule a task
|
|
10
|
+
scheduleTask: async ({name, payload, scheduled, recurringInterval, recurringIntervalCount, rescheduleCentrallyOnSuccess = false, rescheduleCentrallyOnFailure = false, handlerServiceName, handlerName, idempotencyKey = ''}) => {
|
|
11
|
+
try {
|
|
12
|
+
const [result] = await db.execute(
|
|
13
|
+
'INSERT INTO `tasks` (`id`, `name`, `payload`, `status`, `scheduled`, `recurring_interval`, `recurring_interval_count`, `reschedule_centrally_on_success`, `reschedule_centrally_on_failure`, `handler_service_name`, `handler_name`, `idempotency_key`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
|
14
|
+
[utils.uuid(), name, JSON.stringify(payload), 'scheduled', scheduled, recurringInterval, recurringIntervalCount, rescheduleCentrallyOnSuccess, rescheduleCentrallyOnFailure, handlerServiceName, handlerName, idempotencyKey]
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return result.insertId;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
logger.error("Error scheduling task:" + error);
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// update task status
|
|
25
|
+
updateTaskStatus: async ({id, status}) => {
|
|
26
|
+
try {
|
|
27
|
+
const [result] = await db.execute(
|
|
28
|
+
'UPDATE `tasks` SET `status` = ? WHERE `id` = ?',
|
|
29
|
+
[status, id]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return result.affectedRows;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.error("Error updating task status:" + error);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// cancel tasks by associated resource id
|
|
40
|
+
cancelTasksByAssociatedResourceId: async ({associatedResourceId}) => {
|
|
41
|
+
try {
|
|
42
|
+
const [result] = await db.execute(
|
|
43
|
+
'UPDATE `tasks` SET `status` = ? WHERE `associated_resource_id` = ?',
|
|
44
|
+
['cancelled', associatedResourceId]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return result.affectedRows;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
logger.error("Error cancelling tasks by associated resource id:" + error);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// get task batch
|
|
55
|
+
getTaskBatch: async ({status}) => {
|
|
56
|
+
try {
|
|
57
|
+
const [result] = await db.execute(
|
|
58
|
+
'SELECT * FROM `tasks` WHERE `status` = ? AND `scheduled` <= NOW()',
|
|
59
|
+
[status]
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// map from snake case to camel case
|
|
63
|
+
const tasks = result.map(task => {
|
|
64
|
+
return {
|
|
65
|
+
id: task.id,
|
|
66
|
+
name: task.name,
|
|
67
|
+
payload: JSON.parse(task.payload),
|
|
68
|
+
status: task.status,
|
|
69
|
+
scheduled: task.scheduled,
|
|
70
|
+
recurringInterval: task.recurring_interval,
|
|
71
|
+
recurringIntervalCount: task.recurring_interval_count,
|
|
72
|
+
rescheduleCentrallyOnSuccess: task.reschedule_centrally_on_success,
|
|
73
|
+
rescheduleCentrallyOnFailure: task.reschedule_centrally_on_failure,
|
|
74
|
+
handlerServiceName: task.handler_service_name,
|
|
75
|
+
handlerName: task.handler_name,
|
|
76
|
+
idempotencyKey: task.idempotency_key
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return tasks;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
logger.error("Error getting task batch:" + error);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// get tasks by status
|
|
88
|
+
getTasksByStatus: async ({status}) => {
|
|
89
|
+
try {
|
|
90
|
+
const [result] = await db.execute(
|
|
91
|
+
'SELECT * FROM `tasks` WHERE `status` = ?',
|
|
92
|
+
[status]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// map from snake case to camel case
|
|
96
|
+
const tasks = result.map(task => {
|
|
97
|
+
return {
|
|
98
|
+
id: task.id,
|
|
99
|
+
name: task.name,
|
|
100
|
+
payload: JSON.parse(task.payload),
|
|
101
|
+
status: task.status,
|
|
102
|
+
scheduled: task.scheduled,
|
|
103
|
+
recurringInterval: task.recurring_interval,
|
|
104
|
+
recurringIntervalCount: task.recurring_interval_count,
|
|
105
|
+
rescheduleCentrallyOnSuccess: task.reschedule_centrally_on_success,
|
|
106
|
+
rescheduleCentrallyOnFailure: task.reschedule_centrally_on_failure,
|
|
107
|
+
handlerServiceName: task.handler_service_name,
|
|
108
|
+
handlerName: task.handler_name,
|
|
109
|
+
idempotencyKey: task.idempotency_key
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return tasks;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logger.error("Error getting tasks by status in service:" + error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// ensure task is the only scheduled with this indempotency key
|
|
121
|
+
checkIndempotent: async ({idempotencyKey}) => {
|
|
122
|
+
try {
|
|
123
|
+
if (!idempotencyKey) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const [result] = await db.execute(
|
|
128
|
+
'SELECT * FROM `tasks` WHERE `idempotency_key` = ? AND `status` = ?',
|
|
129
|
+
[idempotencyKey, 'scheduled']
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return result.length === 0;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
logger.error("Error checking indempotency:" + error);
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
// delete failed tasks
|
|
140
|
+
deleteFailedTasks: async () => {
|
|
141
|
+
try {
|
|
142
|
+
const [result] = await db.execute(
|
|
143
|
+
'DELETE FROM `tasks` WHERE `status` = ?',
|
|
144
|
+
['failed']
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return result.affectedRows;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
logger.error("Error deleting failed tasks:" + error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import request from 'supertest';
|
|
2
|
+
const url = 'http://localhost:4000';
|
|
3
|
+
const authUrl = 'http://localhost:4001';
|
|
4
|
+
|
|
5
|
+
describe('Products API CRUD', () => {
|
|
6
|
+
|
|
7
|
+
let authToken;
|
|
8
|
+
let productId;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
// check we are not in production mode
|
|
12
|
+
if (process.env.NODE_ENV === 'production') {
|
|
13
|
+
throw new Error('Do not run tests in production mode!');
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// authenticate with email and password
|
|
18
|
+
it('POST /authenticate', async () => {
|
|
19
|
+
const response = await request(authUrl).post('/authenticate')
|
|
20
|
+
.set('Content-Type', 'application/json')
|
|
21
|
+
.send({
|
|
22
|
+
username: 'adam@gnar.co.uk',
|
|
23
|
+
password: 'password'
|
|
24
|
+
});
|
|
25
|
+
console.log(response.body);
|
|
26
|
+
expect(response.status).toBe(200);
|
|
27
|
+
expect(response.body).toHaveProperty('token');
|
|
28
|
+
|
|
29
|
+
authToken = response.body.token;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// schedule a task
|
|
33
|
+
it('should return 200 OK - POST /tasks/schedule', async () => {
|
|
34
|
+
const res = await request(url).post('/tasks/schedule')
|
|
35
|
+
.set('Content-Type', 'application/json')
|
|
36
|
+
.set('Authorization', 'Bearer ' + authToken)
|
|
37
|
+
.send({
|
|
38
|
+
name: 'test-task',
|
|
39
|
+
payload: { test: 'data' },
|
|
40
|
+
scheduled: new Date().addMonths(1),
|
|
41
|
+
recurringInterval: 'monthly',
|
|
42
|
+
recurringIntervalCount: 1,
|
|
43
|
+
handlerServiceName: 'userService',
|
|
44
|
+
handlerName: 'testHandler'
|
|
45
|
+
});
|
|
46
|
+
console.log(res.body);
|
|
47
|
+
expect(res.statusCode).toEqual(200);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Dockerfile for Notification 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/notification/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 4000
|
|
21
|
+
|
|
22
|
+
# Start the application
|
|
23
|
+
CMD ["nodemon", "--watch", "./gnar_engine", "./gnar_engine/app.js"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Stage 1: Builder
|
|
2
|
+
FROM node:20-alpine AS builder
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# Copy app source
|
|
7
|
+
COPY ./services/notification/src ./src
|
|
8
|
+
COPY ./Lib ./Lib
|
|
9
|
+
|
|
10
|
+
# Copy environment variables (do this later in build only if needed)
|
|
11
|
+
COPY ./.env.production .env
|
|
12
|
+
|
|
13
|
+
# Copy package files and install deps
|
|
14
|
+
COPY ./services/notification/package*.json ./
|
|
15
|
+
RUN npm install --omit=dev
|
|
16
|
+
|
|
17
|
+
# Stage 2: Runtime
|
|
18
|
+
FROM node:20-alpine
|
|
19
|
+
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
|
|
22
|
+
# Copy built app from builder stage
|
|
23
|
+
COPY --from=builder /app /app
|
|
24
|
+
|
|
25
|
+
# Install system deps
|
|
26
|
+
RUN apk add --no-cache ca-certificates wget
|
|
27
|
+
|
|
28
|
+
# Install AWS DocumentDB CA certificates
|
|
29
|
+
RUN mkdir -p /usr/local/share/ca-certificates && \
|
|
30
|
+
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -O /usr/local/share/ca-certificates/aws-docdb.pem && \
|
|
31
|
+
update-ca-certificates
|
|
32
|
+
|
|
33
|
+
# Expose port
|
|
34
|
+
EXPOSE 4000
|
|
35
|
+
|
|
36
|
+
# Start app
|
|
37
|
+
CMD ["npm", "run", "start"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gnar_engine_notification",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Gnar Engine - Notification Service",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node ./src/app.js",
|
|
8
|
+
"start:dev": "nodemon --watch ./src ./src/app.js",
|
|
9
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules' jest --watchAll --verbose"
|
|
10
|
+
},
|
|
11
|
+
"author": "Gnar Software Ltd.",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@aws-sdk/client-ses": "^3.806.0",
|
|
15
|
+
"ajv": "^8.17.1",
|
|
16
|
+
"ajv-formats": "^3.0.1",
|
|
17
|
+
"dotenv": "^16.4.7",
|
|
18
|
+
"nodemailer": "^7.0.3",
|
|
19
|
+
"path": "^0.12.7",
|
|
20
|
+
"pino": "^9.6.0",
|
|
21
|
+
"pino-pretty": "^13.0.0",
|
|
22
|
+
"uuid": "^11.1.0",
|
|
23
|
+
"handlebars": "^4.7.8"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@babel/preset-env": "^7.22.20",
|
|
27
|
+
"jest": "^29.7.0",
|
|
28
|
+
"nodemon": "^3.1.9",
|
|
29
|
+
"supertest": "^6.3.3"
|
|
30
|
+
},
|
|
31
|
+
"workspaces": [
|
|
32
|
+
"./Lib/*"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import { logger } from './services/logger.service.js';
|
|
3
|
+
import { commandBus } from './commands/command-bus.js';
|
|
4
|
+
import { messageController } from './controllers/message.controller.js';
|
|
5
|
+
import { messageAwaitResponse } from '@gnar-engine/message-client';
|
|
6
|
+
import { internalHealthCheck } from './commands/handlers/control.handler.js';
|
|
7
|
+
import { sendNotification } from './commands/handlers/notification.handler.js';
|
|
8
|
+
|
|
9
|
+
dotenv.config({ path: '.env' });
|
|
10
|
+
|
|
11
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
12
|
+
console.error('🚨 Unhandled Rejection at:', promise, '\nReason:', reason);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
process.on('uncaughtException', (err) => {
|
|
17
|
+
console.error('🚨 Uncaught Exception:', err);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @function startServer
|
|
23
|
+
* @description Initializes and starts the Fastify server.
|
|
24
|
+
*/
|
|
25
|
+
const startService = async () => {
|
|
26
|
+
|
|
27
|
+
// Register commands
|
|
28
|
+
commandBus.register('internalHealthCheck', internalHealthCheck);
|
|
29
|
+
commandBus.register('sendNotification', sendNotification);
|
|
30
|
+
|
|
31
|
+
// Init controllers and error handlers
|
|
32
|
+
messageController.init();
|
|
33
|
+
|
|
34
|
+
// Register with control service
|
|
35
|
+
try {
|
|
36
|
+
await messageAwaitResponse('controlService', {
|
|
37
|
+
method: 'registerService',
|
|
38
|
+
data: {
|
|
39
|
+
service: {
|
|
40
|
+
name: 'notificationService'
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
} catch (error) {
|
|
45
|
+
logger.info('No response from the control service when registering as a service');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Entry point
|
|
51
|
+
startService();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command bus
|
|
3
|
+
*/
|
|
4
|
+
export const commandBus = {
|
|
5
|
+
handlers: new Map(),
|
|
6
|
+
|
|
7
|
+
register(commandName, handlerFunction) {
|
|
8
|
+
this.handlers.set(commandName, handlerFunction);
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
async execute(commandName, ...args) {
|
|
12
|
+
const handlerFunction = this.handlers.get(commandName);
|
|
13
|
+
if (!handlerFunction) {
|
|
14
|
+
console.log('handlers', this.handlers);
|
|
15
|
+
throw new Error(`Command ${commandName} not registered`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return await handlerFunction(...args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Run seeders
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {string} params.seeder Name of single seeder to run (optional)
|
|
7
|
+
*/
|
|
8
|
+
export const runSeeders = async ({seeder}) => {
|
|
9
|
+
// checkout service has no db
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal health check (kills process if it fails)
|
|
14
|
+
*/
|
|
15
|
+
export const internalHealthCheck = async () => {
|
|
16
|
+
|
|
17
|
+
// Nothing to check for checkout service
|
|
18
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { logger } from '../../services/logger.service.js';
|
|
2
|
+
import { emailSendingService, emailHeaderLogoUrl } from './../../config.js';
|
|
3
|
+
import { helpers } from '@gnar-engine/helpers';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import handlebars from 'handlebars';
|
|
6
|
+
import nodemailer from 'nodemailer';
|
|
7
|
+
import { getSesClient } from '../../services/ses.service.js';
|
|
8
|
+
import { SendEmailCommand } from '@aws-sdk/client-ses';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Send a notification
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} params
|
|
15
|
+
* @param {string} params.templateName - Name of the template file (without extension)
|
|
16
|
+
* @param {string} params.to - Recipient email address
|
|
17
|
+
* @param {Object} params.params - Parameters to be passed to the template
|
|
18
|
+
* @param {string} params.subject - Subject of the email
|
|
19
|
+
*/
|
|
20
|
+
export const sendNotification = async ({ templateName, to, params, subject }) => {
|
|
21
|
+
let source;
|
|
22
|
+
let template;
|
|
23
|
+
|
|
24
|
+
// get the requested template
|
|
25
|
+
try {
|
|
26
|
+
const workingDir = process.cwd();
|
|
27
|
+
const path = workingDir + '/src/templates/' + templateName + '.hbs';
|
|
28
|
+
source = fs.readFileSync(path, 'utf8');
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error('Error reading template file: ' + error.message);
|
|
31
|
+
throw new Error('Template not found');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// compile the template
|
|
35
|
+
try {
|
|
36
|
+
template = handlebars.compile(source);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.error('Error compiling template: ' + error.message);
|
|
39
|
+
throw new Error('Template compilation failed');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// append other params
|
|
43
|
+
params = prepareParams(params, templateName);
|
|
44
|
+
|
|
45
|
+
// prepare the template
|
|
46
|
+
const html = template(params);
|
|
47
|
+
|
|
48
|
+
// send the email
|
|
49
|
+
switch (emailSendingService) {
|
|
50
|
+
case 'SMTP':
|
|
51
|
+
await sendSmtpEmail({ to, subject, html });
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case 'SES':
|
|
55
|
+
await sendSesEmail({ to, subject, html });
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case 'Direct':
|
|
59
|
+
logger.error('Email sending service not implemented: ' + emailSendingService);
|
|
60
|
+
throw new Error('Email sending service not implemented');
|
|
61
|
+
|
|
62
|
+
default:
|
|
63
|
+
logger.error('Invalid email sending service: ' + emailSendingService);
|
|
64
|
+
throw new Error('Invalid email sending service');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Send SMTP email
|
|
70
|
+
*
|
|
71
|
+
* @param {Object} params
|
|
72
|
+
* @param {string} params.to - Recipient email address
|
|
73
|
+
* @param {string} params.subject - Email subject
|
|
74
|
+
* @param {string} params.html - HTML content of the email
|
|
75
|
+
*/
|
|
76
|
+
export const sendSmtpEmail = async ({ to, subject, html }) => {
|
|
77
|
+
try {
|
|
78
|
+
const transporter = nodemailer.createTransport({
|
|
79
|
+
host: process.env.SMTP_HOST,
|
|
80
|
+
port: parseInt(process.env.SMTP_PORT || '465'),
|
|
81
|
+
secure: true,
|
|
82
|
+
auth: {
|
|
83
|
+
user: process.env.SMTP_USER,
|
|
84
|
+
pass: process.env.SMTP_PASS
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const mailOptions = {
|
|
89
|
+
from: `"Your App Name" <${process.env.SMTP_USER}>`,
|
|
90
|
+
to,
|
|
91
|
+
subject,
|
|
92
|
+
html
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
await transporter.sendMail(mailOptions);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logger.error('SMTP email send error: ' + error.message);
|
|
98
|
+
throw new Error('SMTP email failed to send');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Send SES email
|
|
104
|
+
*
|
|
105
|
+
* @param {Object} params
|
|
106
|
+
* @param {string} params.to - Recipient email address
|
|
107
|
+
* @param {string} params.subject - Email subject
|
|
108
|
+
* @param {string} params.html - HTML content of the email
|
|
109
|
+
*/
|
|
110
|
+
export const sendSesEmail = async ({ to, subject, html }) => {
|
|
111
|
+
try {
|
|
112
|
+
const sesClient = getSesClient();
|
|
113
|
+
|
|
114
|
+
const command = new SendEmailCommand({
|
|
115
|
+
Source: process.env.NOTIFICATION_SES_SOURCE_EMAIL,
|
|
116
|
+
Destination: {
|
|
117
|
+
ToAddresses: [to]
|
|
118
|
+
},
|
|
119
|
+
Message: {
|
|
120
|
+
Subject: {
|
|
121
|
+
Data: subject,
|
|
122
|
+
Charset: 'UTF-8'
|
|
123
|
+
},
|
|
124
|
+
Body: {
|
|
125
|
+
Html: {
|
|
126
|
+
Data: html,
|
|
127
|
+
Charset: 'UTF-8'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
await sesClient.send(command);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
logger.error('Error sending email with SES: ' + error.message);
|
|
136
|
+
throw new Error('SES email failed to send');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Prepare parameters for the template
|
|
142
|
+
*
|
|
143
|
+
* @param {Object} params - Parameters to be passed to the template
|
|
144
|
+
* @param {string} templateName - Name of the template file (without extension)
|
|
145
|
+
* @returns {Object} - Prepared parameters
|
|
146
|
+
*/
|
|
147
|
+
const prepareParams = (params, templateName) => {
|
|
148
|
+
|
|
149
|
+
// add shop logo
|
|
150
|
+
params.logoUrl = emailHeaderLogoUrl;
|
|
151
|
+
|
|
152
|
+
if (params.order?.currency) {
|
|
153
|
+
params.currencySymbol = helpers.ecommerce.getCurrencySymbol(params.order.currency);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return params
|
|
157
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Email sending service
|
|
4
|
+
*
|
|
5
|
+
* - Values:
|
|
6
|
+
* - SES (not implemented yet)
|
|
7
|
+
* - SMTP
|
|
8
|
+
* - Direct (not implemented yet)
|
|
9
|
+
*/
|
|
10
|
+
export const emailSendingService = 'SES';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Email header logo url
|
|
14
|
+
*/
|
|
15
|
+
export const emailHeaderLogoUrl = 'https://admin.gnarengine.com/public/gnarengine-logo-black.png';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { commandBus } from '../commands/command-bus.js';
|
|
2
|
+
import { logger } from '../services/logger.service.js';
|
|
3
|
+
import { initializeRabbitMQ } from '@gnar-engine/message-client';
|
|
4
|
+
|
|
5
|
+
// Configuration
|
|
6
|
+
const queueName = 'notificationServiceQueue';
|
|
7
|
+
const prefetch = 3;
|
|
8
|
+
|
|
9
|
+
export const messageController = {
|
|
10
|
+
handleMessage: async function (msg, channel) {
|
|
11
|
+
if (!msg) return;
|
|
12
|
+
|
|
13
|
+
const payload = JSON.parse(msg.content.toString());
|
|
14
|
+
|
|
15
|
+
if (!payload.method) {
|
|
16
|
+
return channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ error: 'Method not found' })), {
|
|
17
|
+
correlationId: msg.properties.correlationId,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
switch (payload.method) {
|
|
22
|
+
case 'sendNotification':
|
|
23
|
+
try {
|
|
24
|
+
const { templateName, to, params, subject } = payload.data;
|
|
25
|
+
await commandBus.execute('sendNotification', { templateName, to, params, subject });
|
|
26
|
+
channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ status: 'ok' })), {
|
|
27
|
+
correlationId: msg.properties.correlationId,
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error("Error sending notification: " + error);
|
|
31
|
+
channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ error: 'Failed to send notification' })), {
|
|
32
|
+
correlationId: msg.properties.correlationId,
|
|
33
|
+
});
|
|
34
|
+
} finally {
|
|
35
|
+
channel.ack(msg);
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case 'healthCheck':
|
|
40
|
+
await this.handleHealthCheck(msg, channel);
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
default:
|
|
44
|
+
await this.handleMethodNotFound(msg, channel);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// Handler for the health check method
|
|
49
|
+
async handleHealthCheck(msg, channel) {
|
|
50
|
+
try {
|
|
51
|
+
channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ status: 'ok' })), {
|
|
52
|
+
correlationId: msg.properties.correlationId,
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
logger.error("Error running health check:", error);
|
|
56
|
+
} finally {
|
|
57
|
+
channel.ack(msg);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Handler for unknown methods
|
|
62
|
+
async handleMethodNotFound(msg, channel) {
|
|
63
|
+
try {
|
|
64
|
+
channel.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify({ error: 'Method not found' })), {
|
|
65
|
+
correlationId: msg.properties.correlationId,
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
logger.error("Error handling method not found:", error);
|
|
69
|
+
} finally {
|
|
70
|
+
channel.ack(msg);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Initialize RabbitMQ and consumers
|
|
75
|
+
init: async function () {
|
|
76
|
+
await initializeRabbitMQ(
|
|
77
|
+
queueName,
|
|
78
|
+
prefetch,
|
|
79
|
+
this.handleMessage.bind(this)
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
6
|
+
|
|
7
|
+
// Create a logger instance
|
|
8
|
+
export const logger = pino({
|
|
9
|
+
level: process.env.LOG_MODE || 'info',
|
|
10
|
+
transport: {
|
|
11
|
+
target: 'pino-pretty', // Pretty print logs for the console
|
|
12
|
+
options: {
|
|
13
|
+
colorize: true, // Colorize the logs in the console
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
});
|