@solidstarters/solid-core 1.2.145 → 1.2.148
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/dist/config/common.config.d.ts +10 -0
- package/dist/config/common.config.d.ts.map +1 -1
- package/dist/config/common.config.js +6 -1
- package/dist/config/common.config.js.map +1 -1
- package/dist/decorators/mail-provider.decorator.d.ts +3 -0
- package/dist/decorators/mail-provider.decorator.d.ts.map +1 -0
- package/dist/decorators/mail-provider.decorator.js +11 -0
- package/dist/decorators/mail-provider.decorator.js.map +1 -0
- package/dist/factories/mail.factory.d.ts +14 -0
- package/dist/factories/mail.factory.d.ts.map +1 -0
- package/dist/factories/mail.factory.js +50 -0
- package/dist/factories/mail.factory.js.map +1 -0
- package/dist/helpers/solid-registry.d.ts +3 -0
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js +7 -0
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +5 -5
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/jobs/database/api-email-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/api-email-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/api-email-subscriber-database.service.js +6 -3
- package/dist/jobs/database/api-email-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.d.ts +3 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.d.ts.map +1 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.js +6 -3
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.js.map +1 -1
- package/dist/jobs/database/generate-code-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/generate-code-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/generate-code-subscriber-database.service.js +6 -3
- package/dist/jobs/database/generate-code-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/otp-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/otp-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/otp-subscriber-database.service.js +5 -2
- package/dist/jobs/database/otp-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/sms-subscriber-database.service.d.ts +4 -2
- package/dist/jobs/database/sms-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/sms-subscriber-database.service.js +7 -4
- package/dist/jobs/database/sms-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/{email-publisher-database.service.d.ts → smtp-email-publisher-database.service.d.ts} +2 -2
- package/dist/jobs/database/smtp-email-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/{email-publisher-database.service.js → smtp-email-publisher-database.service.js} +8 -8
- package/dist/jobs/database/smtp-email-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/{email-queue-options-database.d.ts → smtp-email-queue-options-database.d.ts} +1 -1
- package/dist/jobs/database/smtp-email-queue-options-database.d.ts.map +1 -0
- package/dist/jobs/database/{email-queue-options-database.js → smtp-email-queue-options-database.js} +1 -1
- package/dist/jobs/database/smtp-email-queue-options-database.js.map +1 -0
- package/dist/jobs/database/{email-subscriber-database.service.d.ts → smtp-email-subscriber-database.service.d.ts} +6 -4
- package/dist/jobs/database/smtp-email-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/{email-subscriber-database.service.js → smtp-email-subscriber-database.service.js} +14 -11
- package/dist/jobs/database/smtp-email-subscriber-database.service.js.map +1 -0
- package/dist/jobs/database/test-queue-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.js +6 -3
- package/dist/jobs/database/test-queue-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +5 -2
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/twilio-sms-publisher-database.service.d.ts +11 -0
- package/dist/jobs/database/twilio-sms-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/twilio-sms-publisher-database.service.js +39 -0
- package/dist/jobs/database/twilio-sms-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.d.ts +8 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.d.ts.map +1 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.js +10 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.js.map +1 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.d.ts +17 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.js +48 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.js.map +1 -0
- package/dist/jobs/database/whatsapp-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/whatsapp-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/whatsapp-subscriber-database.service.js +6 -3
- package/dist/jobs/database/whatsapp-subscriber-database.service.js.map +1 -1
- package/dist/jobs/sms-subscriber.service.d.ts +1 -1
- package/dist/jobs/sms-subscriber.service.js +1 -1
- package/dist/jobs/sms-subscriber.service.js.map +1 -1
- package/dist/jobs/{email-publisher.service.d.ts → smtp-email-publisher.service.d.ts} +2 -2
- package/dist/jobs/smtp-email-publisher.service.d.ts.map +1 -0
- package/dist/jobs/{email-publisher.service.js → smtp-email-publisher.service.js} +8 -8
- package/dist/jobs/smtp-email-publisher.service.js.map +1 -0
- package/dist/jobs/{email-queue-options.d.ts → smtp-email-queue-options.d.ts} +1 -1
- package/dist/jobs/smtp-email-queue-options.d.ts.map +1 -0
- package/dist/jobs/{email-queue-options.js → smtp-email-queue-options.js} +1 -1
- package/dist/jobs/smtp-email-queue-options.js.map +1 -0
- package/dist/jobs/{email-subscriber.service.d.ts → smtp-email-subscriber.service.d.ts} +3 -3
- package/dist/jobs/smtp-email-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/{email-subscriber.service.js → smtp-email-subscriber.service.js} +8 -8
- package/dist/jobs/smtp-email-subscriber.service.js.map +1 -0
- package/dist/seeders/module-metadata-seeder.service.d.ts +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +42 -31
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/services/authentication.service.d.ts +3 -3
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +16 -12
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/mail/elastic-email.service.d.ts.map +1 -1
- package/dist/services/mail/elastic-email.service.js +2 -0
- package/dist/services/mail/elastic-email.service.js.map +1 -1
- package/dist/services/mail/smtp-email.service.d.ts +6 -6
- package/dist/services/mail/smtp-email.service.d.ts.map +1 -1
- package/dist/services/mail/smtp-email.service.js +17 -11
- package/dist/services/mail/smtp-email.service.js.map +1 -1
- package/dist/services/poller.service.d.ts +24 -0
- package/dist/services/poller.service.d.ts.map +1 -0
- package/dist/services/poller.service.js +131 -0
- package/dist/services/poller.service.js.map +1 -0
- package/dist/services/queues/database-subscriber.service.d.ts +4 -1
- package/dist/services/queues/database-subscriber.service.d.ts.map +1 -1
- package/dist/services/queues/database-subscriber.service.js +13 -13
- package/dist/services/queues/database-subscriber.service.js.map +1 -1
- package/dist/services/sms/Msg91BaseSMSService.d.ts +2 -2
- package/dist/services/sms/Msg91BaseSMSService.d.ts.map +1 -1
- package/dist/services/sms/Msg91BaseSMSService.js.map +1 -1
- package/dist/services/sms/Msg91OTPService.d.ts +1 -1
- package/dist/services/sms/Msg91OTPService.d.ts.map +1 -1
- package/dist/services/sms/Msg91OTPService.js.map +1 -1
- package/dist/services/sms/Msg91SMSService.d.ts +1 -1
- package/dist/services/sms/Msg91SMSService.d.ts.map +1 -1
- package/dist/services/sms/Msg91SMSService.js.map +1 -1
- package/dist/services/sms/TwilioSMSService.d.ts +18 -0
- package/dist/services/sms/TwilioSMSService.d.ts.map +1 -0
- package/dist/services/sms/TwilioSMSService.js +115 -0
- package/dist/services/sms/TwilioSMSService.js.map +1 -0
- package/dist/services/solid-introspect.service.d.ts +1 -0
- package/dist/services/solid-introspect.service.d.ts.map +1 -1
- package/dist/services/solid-introspect.service.js +14 -0
- package/dist/services/solid-introspect.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +21 -8
- package/dist/solid-core.module.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/config/common.config.ts +6 -1
- package/src/decorators/mail-provider.decorator.ts +7 -0
- package/src/factories/mail.factory.ts +38 -0
- package/src/helpers/solid-registry.ts +9 -0
- package/src/index.ts +10 -3
- package/src/interfaces.ts +11 -5
- package/src/jobs/database/api-email-subscriber-database.service.ts +3 -1
- package/src/jobs/database/computed-field-evaluation-subscriber.service.ts +4 -2
- package/src/jobs/database/generate-code-subscriber-database.service.ts +3 -1
- package/src/jobs/database/otp-subscriber-database.service.ts +3 -1
- package/src/jobs/database/sms-subscriber-database.service.ts +4 -2
- package/src/jobs/database/{email-publisher-database.service.ts → smtp-email-publisher-database.service.ts} +2 -2
- package/src/jobs/database/{email-subscriber-database.service.ts → smtp-email-subscriber-database.service.ts} +5 -3
- package/src/jobs/database/test-queue-subscriber-database.service.ts +3 -1
- package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +3 -1
- package/src/jobs/database/twilio-sms-publisher-database.service.ts +23 -0
- package/src/jobs/database/twilio-sms-queue-database-options.ts +9 -0
- package/src/jobs/database/twilio-sms-subscriber-database.service.ts +32 -0
- package/src/jobs/database/whatsapp-subscriber-database.service.ts +3 -1
- package/src/jobs/sms-subscriber.service.ts +1 -1
- package/src/jobs/{email-publisher.service.ts → smtp-email-publisher.service.ts} +2 -2
- package/src/jobs/{email-subscriber.service.ts → smtp-email-subscriber.service.ts} +2 -2
- package/src/seeders/module-metadata-seeder.service.ts +45 -42
- package/src/services/authentication.service.ts +24 -9
- package/src/services/mail/elastic-email.service.ts +2 -0
- package/src/services/mail/smtp-email.service.ts +48 -21
- package/src/services/poller.service.ts +163 -0
- package/src/services/queues/database-subscriber.service.ts +39 -12
- package/src/services/sms/Msg91BaseSMSService.ts +2 -2
- package/src/services/sms/Msg91OTPService.ts +1 -1
- package/src/services/sms/Msg91SMSService.ts +1 -1
- package/src/services/sms/TwilioSMSService.ts +118 -0
- package/src/services/solid-introspect.service.ts +21 -5
- package/src/solid-core.module.ts +24 -8
- package/dist/jobs/database/email-publisher-database.service.d.ts.map +0 -1
- package/dist/jobs/database/email-publisher-database.service.js.map +0 -1
- package/dist/jobs/database/email-queue-options-database.d.ts.map +0 -1
- package/dist/jobs/database/email-queue-options-database.js.map +0 -1
- package/dist/jobs/database/email-subscriber-database.service.d.ts.map +0 -1
- package/dist/jobs/database/email-subscriber-database.service.js.map +0 -1
- package/dist/jobs/email-publisher.service.d.ts.map +0 -1
- package/dist/jobs/email-publisher.service.js.map +0 -1
- package/dist/jobs/email-queue-options.d.ts.map +0 -1
- package/dist/jobs/email-queue-options.js.map +0 -1
- package/dist/jobs/email-subscriber.service.d.ts.map +0 -1
- package/dist/jobs/email-subscriber.service.js.map +0 -1
- /package/src/jobs/database/{email-queue-options-database.ts → smtp-email-queue-options-database.ts} +0 -0
- /package/src/jobs/{email-queue-options.ts → smtp-email-queue-options.ts} +0 -0
|
@@ -134,18 +134,18 @@ export class ModuleMetadataSeederService {
|
|
|
134
134
|
|
|
135
135
|
// Process module metadata first.
|
|
136
136
|
const moduleMetadata: CreateModuleMetadataDto = overallMetadata.moduleMetadata;
|
|
137
|
-
this.logger.
|
|
137
|
+
this.logger.log(`[Start] Processing module metadata for ${moduleMetadata.name}`)
|
|
138
138
|
await this.seedModuleModelFields(moduleMetadata);
|
|
139
|
-
this.logger.
|
|
139
|
+
this.logger.log(`[End] Processing module metadata for ${moduleMetadata.name}`)
|
|
140
140
|
|
|
141
141
|
// Media Storage provider templates
|
|
142
|
-
this.logger.
|
|
142
|
+
this.logger.log(`[Start] Processing Media Storage Provider for ${moduleMetadata.name}`);
|
|
143
143
|
const mediaStorageProviders = overallMetadata.mediaStorageProviders;
|
|
144
144
|
await this.seedMediaStorageProviders(mediaStorageProviders);
|
|
145
|
-
this.logger.
|
|
145
|
+
this.logger.log(`[End] Processing Media Storage Provider for ${moduleMetadata.name}`);
|
|
146
146
|
|
|
147
147
|
// Custom role handling
|
|
148
|
-
this.logger.
|
|
148
|
+
this.logger.log(`[End] Processing roles for ${moduleMetadata.name}`)
|
|
149
149
|
const roles = overallMetadata.roles;
|
|
150
150
|
|
|
151
151
|
// Every role configuration in the seeder json can optionally have a permissions attribute.
|
|
@@ -159,67 +159,67 @@ export class ModuleMetadataSeederService {
|
|
|
159
159
|
await this.roleService.addPermissionsToRole(role.name, role.permissions)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
-
this.logger.
|
|
162
|
+
this.logger.log(`[End] Processing roles for ${moduleMetadata.name}`)
|
|
163
163
|
|
|
164
164
|
// Custom user handling
|
|
165
|
-
this.logger.
|
|
165
|
+
this.logger.log(`[Start] Processing users for ${moduleMetadata.name}`);
|
|
166
166
|
const users = overallMetadata.users;
|
|
167
167
|
usersDetail = users;
|
|
168
168
|
await this.seedUsers(users);
|
|
169
|
-
this.logger.
|
|
169
|
+
this.logger.log(`[End] Processing users for ${moduleMetadata.name}`)
|
|
170
170
|
|
|
171
171
|
// Application Module View handling
|
|
172
|
-
this.logger.
|
|
172
|
+
this.logger.log(`[Start] Processing views for ${moduleMetadata.name}`);
|
|
173
173
|
const views = overallMetadata.views;
|
|
174
174
|
await this.seedViews(views);
|
|
175
|
-
this.logger.
|
|
175
|
+
this.logger.log(`[End] Processing views for ${moduleMetadata.name}`)
|
|
176
176
|
|
|
177
177
|
// Application Module Action handling
|
|
178
|
-
this.logger.
|
|
178
|
+
this.logger.log(`[Start] Processing actions for ${moduleMetadata.name}`);
|
|
179
179
|
const actions = overallMetadata.actions;
|
|
180
180
|
await this.seedActions(actions);
|
|
181
|
-
this.logger.
|
|
181
|
+
this.logger.log(`[End] Processing actions for ${moduleMetadata.name}`)
|
|
182
182
|
|
|
183
183
|
// Application Module Menu handling
|
|
184
|
-
this.logger.
|
|
184
|
+
this.logger.log(`[Start] Processing menus for ${moduleMetadata.name}`);
|
|
185
185
|
const menus = overallMetadata.menus;
|
|
186
186
|
await this.seedMenus(menus);
|
|
187
|
-
this.logger.
|
|
187
|
+
this.logger.log(`[End] Processing menus for ${moduleMetadata.name}`)
|
|
188
188
|
|
|
189
189
|
// Email templates
|
|
190
|
-
this.logger.
|
|
190
|
+
this.logger.log(`[Start] Processing email templates for ${moduleMetadata.name}`);
|
|
191
191
|
const emailTemplates: CreateEmailTemplateDto[] = overallMetadata.emailTemplates;
|
|
192
192
|
await this.seedEmailTemplates(emailTemplates, moduleMetadata);
|
|
193
|
-
this.logger.
|
|
193
|
+
this.logger.log(`[End] Processing email templates for ${moduleMetadata.name}`);
|
|
194
194
|
|
|
195
195
|
// Sms templates
|
|
196
|
-
this.logger.
|
|
196
|
+
this.logger.log(`[Start] Processing sms templates for ${moduleMetadata.name}`);
|
|
197
197
|
const smsTemplates: CreateSmsTemplateDto[] = overallMetadata.smsTemplates;
|
|
198
|
-
await this.seedSmsTemplates(smsTemplates);
|
|
199
|
-
this.logger.
|
|
198
|
+
await this.seedSmsTemplates(smsTemplates, moduleMetadata);
|
|
199
|
+
this.logger.log(`[End] Processing sms templates for ${moduleMetadata.name}`);
|
|
200
200
|
|
|
201
201
|
// Settings
|
|
202
|
-
this.logger.
|
|
202
|
+
this.logger.log(`[Start] Processing settings for ${moduleMetadata.name}`);
|
|
203
203
|
await this.seetingService.seedDefaultSettings();
|
|
204
|
-
this.logger.
|
|
204
|
+
this.logger.log(`[End] Processing settings for ${moduleMetadata.name}`);
|
|
205
205
|
|
|
206
206
|
// Security rules
|
|
207
|
-
this.logger.
|
|
207
|
+
this.logger.log(`[Start] Processing security rules for ${moduleMetadata.name}`);
|
|
208
208
|
const securityRules: CreateSecurityRuleDto[] = overallMetadata.securityRules;
|
|
209
209
|
await this.seedSecurityRules(securityRules);
|
|
210
|
-
this.logger.
|
|
210
|
+
this.logger.log(`[End] Processing security rules for ${moduleMetadata.name}`);
|
|
211
211
|
|
|
212
212
|
// List Of Values
|
|
213
|
-
this.logger.
|
|
213
|
+
this.logger.log(`[Start] Processing List Of Values for ${moduleMetadata.name}`);
|
|
214
214
|
const listOfValues: CreateListOfValuesDto[] = overallMetadata.listOfValues;
|
|
215
215
|
await this.seedListOfValues(listOfValues);
|
|
216
|
-
this.logger.
|
|
216
|
+
this.logger.log(`[End] Processing List Of Values for ${moduleMetadata.name}`);
|
|
217
217
|
|
|
218
218
|
// Dashboards
|
|
219
|
-
this.logger.
|
|
219
|
+
this.logger.log(`[Start] Processing dashboards for ${moduleMetadata.name}`);
|
|
220
220
|
const dashboards: CreateDashboardDto[] = overallMetadata.dashboards;
|
|
221
221
|
await this.seedDashboards(dashboards);
|
|
222
|
-
this.logger.
|
|
222
|
+
this.logger.log(`[End] Processing dashboards for ${moduleMetadata.name}`);
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
this.logger.debug(`[End] module seed data: ${overallMetadata}`);
|
|
@@ -229,7 +229,7 @@ export class ModuleMetadataSeederService {
|
|
|
229
229
|
// Post seed data file processing.
|
|
230
230
|
|
|
231
231
|
// 1. Give all permissions to the Admin role.
|
|
232
|
-
this.logger.
|
|
232
|
+
this.logger.log(`About to add all permissions to the Admin role`);
|
|
233
233
|
await this.roleService.addAllPermissionsToRole("Admin");
|
|
234
234
|
// 2. Give wrapSettings permissions to the Public role.
|
|
235
235
|
const internalRolePermission = [
|
|
@@ -259,7 +259,7 @@ export class ModuleMetadataSeederService {
|
|
|
259
259
|
'AuthenticationController.logout'
|
|
260
260
|
]
|
|
261
261
|
await this.roleService.addPermissionToRole('Internal User', internalRolePermission);
|
|
262
|
-
await this.roleService.addPermissionToRole('Public', ['SettingController.wrapSettings','AuthenticationController.logout']);
|
|
262
|
+
await this.roleService.addPermissionToRole('Public', ['SettingController.wrapSettings', 'AuthenticationController.logout']);
|
|
263
263
|
this.logger.log(`All Seeders finished`);
|
|
264
264
|
this.logger.log(`Newly created username is: ${usersDetail?.length > 0 ? usersDetail[0]?.username : ''} and password is ${usersDetail?.length > 0 ? usersDetail[0]?.password : ''}`);
|
|
265
265
|
}
|
|
@@ -350,7 +350,7 @@ export class ModuleMetadataSeederService {
|
|
|
350
350
|
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
-
async seedSmsTemplates(smsTemplates: CreateSmsTemplateDto[]) {
|
|
353
|
+
async seedSmsTemplates(smsTemplates: CreateSmsTemplateDto[], moduleMetadata: CreateModuleMetadataDto) {
|
|
354
354
|
if (!smsTemplates) {
|
|
355
355
|
return;
|
|
356
356
|
}
|
|
@@ -360,23 +360,26 @@ export class ModuleMetadataSeederService {
|
|
|
360
360
|
this.logger.log(`Found ${smsTemplate.name} sms template`);
|
|
361
361
|
|
|
362
362
|
// We need to load the actual template contents.
|
|
363
|
-
if (
|
|
364
|
-
// const smsTemplateFilePath = path.join(process.cwd(), smsTemplate.body);
|
|
365
|
-
// smsTemplate.body = fs.readFileSync(smsTemplateFilePath, 'utf-8').toString()
|
|
366
|
-
|
|
363
|
+
if (moduleMetadata.name === 'solid-core') {
|
|
367
364
|
const modulePath = path.dirname(require.resolve('@solidstarters/solid-core'));
|
|
368
|
-
|
|
369
|
-
// Resolve the `src` folder
|
|
370
365
|
const seedDataPath = path.join(modulePath, '../src/seeders/seed-data/sms-templates');
|
|
371
|
-
|
|
372
|
-
// Example usage
|
|
373
366
|
const filePath = path.join(seedDataPath, smsTemplate.body);
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
367
|
+
this.logger.log(`Seeding sms template from solid-core at path: ${filePath}`);
|
|
368
|
+
if (fs.existsSync(filePath)) {
|
|
369
|
+
smsTemplate.body = fs.readFileSync(filePath, 'utf-8').toString();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
// Check if file exists
|
|
374
|
+
const emailTemplateHandlebar = `module-metadata/${moduleMetadata.name}/sms-templates/${smsTemplate.body}`
|
|
375
|
+
const fullPath = path.join(process.cwd(), emailTemplateHandlebar);
|
|
376
|
+
this.logger.log(`Seeding custom sms template from consuming model at path: ${fullPath}`);
|
|
377
|
+
if (fs.existsSync(fullPath)) {
|
|
378
|
+
smsTemplate.body = fs.readFileSync(fullPath, 'utf-8').toString();
|
|
379
|
+
}
|
|
378
380
|
}
|
|
379
381
|
|
|
382
|
+
|
|
380
383
|
// Save to DB.
|
|
381
384
|
await this.smsTemplateService.removeByName(smsTemplate.name);
|
|
382
385
|
await this.smsTemplateService.create(smsTemplate);
|
|
@@ -34,7 +34,7 @@ import { ActiveUserData } from '../interfaces/active-user-data.interface';
|
|
|
34
34
|
import { HashingService } from './hashing.service';
|
|
35
35
|
import { InvalidatedRefreshTokenError, RefreshTokenIdsStorageService } from './refresh-token-ids-storage.service';
|
|
36
36
|
import { UserService } from './user.service';
|
|
37
|
-
import { EventDetails, EventType } from "../interfaces";
|
|
37
|
+
import { EventDetails, EventType, IMail } from "../interfaces";
|
|
38
38
|
import {
|
|
39
39
|
ForgotPasswordSendVerificationTokenOn,
|
|
40
40
|
RegistrationValidationSource,
|
|
@@ -48,6 +48,7 @@ import { UserActivityHistoryService } from './user-activity-history.service';
|
|
|
48
48
|
import { RequestContextService } from './request-context.service';
|
|
49
49
|
import { ERROR_MESSAGES } from 'src/constants/error-messages';
|
|
50
50
|
import { SUCCESS_MESSAGES } from 'src/constants/success-messages';
|
|
51
|
+
import { MailFactory } from 'src/factories/mail.factory';
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
enum LoginProvider {
|
|
@@ -64,7 +65,7 @@ interface otp {
|
|
|
64
65
|
@Injectable()
|
|
65
66
|
export class AuthenticationService {
|
|
66
67
|
private readonly logger = new Logger(AuthenticationService.name);
|
|
67
|
-
|
|
68
|
+
// private readonly mailService: IMail;
|
|
68
69
|
constructor(
|
|
69
70
|
private readonly userService: UserService,
|
|
70
71
|
@InjectRepository(User) private readonly userRepository: Repository<User>,
|
|
@@ -77,7 +78,8 @@ export class AuthenticationService {
|
|
|
77
78
|
private readonly iamConfiguration: ConfigType<typeof iamConfig>,
|
|
78
79
|
private readonly refreshTokenIdsStorage: RefreshTokenIdsStorageService,
|
|
79
80
|
private readonly httpService: HttpService,
|
|
80
|
-
private readonly mailService: SMTPEMailService,
|
|
81
|
+
// private readonly mailService: SMTPEMailService,
|
|
82
|
+
private readonly mailServiceFactory: MailFactory,
|
|
81
83
|
private readonly smsService: Msg91OTPService,
|
|
82
84
|
private readonly eventEmitter: EventEmitter2,
|
|
83
85
|
private readonly settingService: SettingService,
|
|
@@ -86,7 +88,9 @@ export class AuthenticationService {
|
|
|
86
88
|
private readonly commonConfiguration: ConfigType<typeof commonConfig>,
|
|
87
89
|
private readonly userActivityHistoryService: UserActivityHistoryService,
|
|
88
90
|
private readonly requestContextService: RequestContextService,
|
|
89
|
-
) {
|
|
91
|
+
) {
|
|
92
|
+
// this.mailService = this.mailServiceFactory.getMailService();
|
|
93
|
+
}
|
|
90
94
|
|
|
91
95
|
private async getConfig(key: string): Promise<any> {
|
|
92
96
|
return this.settingService.getConfigValue(key);
|
|
@@ -253,8 +257,8 @@ export class AuthenticationService {
|
|
|
253
257
|
|
|
254
258
|
private async notifyUserOnForcePasswordChange(user: User, autoGeneratedPwd: string) {
|
|
255
259
|
const companyLogo = await this.getCompanyLogo();
|
|
256
|
-
|
|
257
|
-
|
|
260
|
+
const mailService = this.mailServiceFactory.getMailService();
|
|
261
|
+
mailService.sendEmailUsingTemplate(
|
|
258
262
|
user.email,
|
|
259
263
|
'on-force-password-change',
|
|
260
264
|
{
|
|
@@ -268,6 +272,8 @@ export class AuthenticationService {
|
|
|
268
272
|
companyLogoUrl: companyLogo
|
|
269
273
|
},
|
|
270
274
|
this.commonConfiguration.shouldQueueEmails,
|
|
275
|
+
null,
|
|
276
|
+
null,
|
|
271
277
|
'user',
|
|
272
278
|
user.id
|
|
273
279
|
);
|
|
@@ -375,7 +381,8 @@ export class AuthenticationService {
|
|
|
375
381
|
if (this.iamConfiguration.dummyOtp)
|
|
376
382
|
return; // Do nothing if dummy otp is configured.
|
|
377
383
|
if (registrationValidationSources.includes(RegistrationValidationSource.EMAIL)) {
|
|
378
|
-
this.
|
|
384
|
+
const mailService = this.mailServiceFactory.getMailService();
|
|
385
|
+
mailService.sendEmailUsingTemplate(
|
|
379
386
|
user.email,
|
|
380
387
|
'otp-on-register',
|
|
381
388
|
{
|
|
@@ -387,6 +394,8 @@ export class AuthenticationService {
|
|
|
387
394
|
companyLogoUrl: companyLogo
|
|
388
395
|
},
|
|
389
396
|
this.commonConfiguration.shouldQueueEmails,
|
|
397
|
+
null,
|
|
398
|
+
null,
|
|
390
399
|
'user',
|
|
391
400
|
user.id
|
|
392
401
|
);
|
|
@@ -563,7 +572,8 @@ export class AuthenticationService {
|
|
|
563
572
|
if (this.iamConfiguration.dummyOtp)
|
|
564
573
|
return; // Do nothing if dummy otp is configured.
|
|
565
574
|
if (loginType === RegistrationValidationSource.EMAIL) {
|
|
566
|
-
this.
|
|
575
|
+
const mailService = this.mailServiceFactory.getMailService();
|
|
576
|
+
mailService.sendEmailUsingTemplate(
|
|
567
577
|
user.email,
|
|
568
578
|
'otp-on-login',
|
|
569
579
|
{
|
|
@@ -575,6 +585,8 @@ export class AuthenticationService {
|
|
|
575
585
|
companyLogoUrl: companyLogo
|
|
576
586
|
},
|
|
577
587
|
this.commonConfiguration.shouldQueueEmails,
|
|
588
|
+
null,
|
|
589
|
+
null,
|
|
578
590
|
'user',
|
|
579
591
|
user.id
|
|
580
592
|
);
|
|
@@ -767,7 +779,8 @@ export class AuthenticationService {
|
|
|
767
779
|
const forgotPasswordSendVerificationTokenOn = this.iamConfiguration.forgotPasswordSendVerificationTokenOn;
|
|
768
780
|
|
|
769
781
|
if (forgotPasswordSendVerificationTokenOn == ForgotPasswordSendVerificationTokenOn.EMAIL) {
|
|
770
|
-
this.
|
|
782
|
+
const mailService = this.mailServiceFactory.getMailService();
|
|
783
|
+
mailService.sendEmailUsingTemplate(
|
|
771
784
|
user.email,
|
|
772
785
|
'forgot-password',
|
|
773
786
|
{
|
|
@@ -780,6 +793,8 @@ export class AuthenticationService {
|
|
|
780
793
|
companyLogoUrl: companyLogo
|
|
781
794
|
},
|
|
782
795
|
this.commonConfiguration.shouldQueueEmails,
|
|
796
|
+
null,
|
|
797
|
+
null,
|
|
783
798
|
'user',
|
|
784
799
|
user.id
|
|
785
800
|
);
|
|
@@ -9,10 +9,12 @@ import { PdfService } from '../pdf.service';
|
|
|
9
9
|
import { FileService } from '../file.service';
|
|
10
10
|
import { IMail, MailAttachment, MailAttachmentWrapper } from "../../interfaces";
|
|
11
11
|
import { PublisherFactory } from '../queues/publisher-factory.service';
|
|
12
|
+
import { MailProvider } from 'src/decorators/mail-provider.decorator';
|
|
12
13
|
|
|
13
14
|
const ElasticEmail = require('@elasticemail/elasticemail-client');
|
|
14
15
|
|
|
15
16
|
@Injectable()
|
|
17
|
+
@MailProvider()
|
|
16
18
|
export class ElasticEmailService implements IMail {
|
|
17
19
|
private readonly logger = new Logger(ElasticEmailService.name);
|
|
18
20
|
private readonly emailsApi;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { Inject, Injectable, Logger } from '@nestjs/common';
|
|
2
2
|
import { ConfigType } from '@nestjs/config';
|
|
3
|
-
import
|
|
3
|
+
import Handlebars from "handlebars";
|
|
4
4
|
import commonConfig from 'src/config/common.config';
|
|
5
|
+
import { MailProvider } from 'src/decorators/mail-provider.decorator';
|
|
6
|
+
import { QueueMessage } from 'src/interfaces/mq';
|
|
7
|
+
import { IMail, MailAttachment, MailAttachmentWrapper } from "../../interfaces";
|
|
5
8
|
import { EmailTemplateService } from '../email-template.service';
|
|
6
|
-
import Handlebars from "handlebars";
|
|
7
|
-
import { IMail, MailAttachment } from "../../interfaces";
|
|
8
9
|
import { PublisherFactory } from '../queues/publisher-factory.service';
|
|
9
10
|
|
|
10
11
|
const nodemailer = require("nodemailer");
|
|
11
12
|
|
|
12
13
|
@Injectable()
|
|
14
|
+
@MailProvider()
|
|
13
15
|
export class SMTPEMailService implements IMail {
|
|
14
16
|
private readonly logger = new Logger(SMTPEMailService.name);
|
|
15
17
|
private readonly transporter: any;
|
|
@@ -33,8 +35,20 @@ export class SMTPEMailService implements IMail {
|
|
|
33
35
|
});
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
async sendEmailUsingTemplate(
|
|
37
|
-
|
|
38
|
+
async sendEmailUsingTemplate(
|
|
39
|
+
to: string,
|
|
40
|
+
templateName: string,
|
|
41
|
+
templateParams: any,
|
|
42
|
+
shouldQueueEmails = false,
|
|
43
|
+
wrapperAttachments?: MailAttachmentWrapper[],
|
|
44
|
+
attachments?: MailAttachment[],
|
|
45
|
+
parentEntity?: any,
|
|
46
|
+
parentEntityId?: any,
|
|
47
|
+
cc?: string[],
|
|
48
|
+
bcc?: string[],
|
|
49
|
+
from?: string
|
|
50
|
+
) {
|
|
51
|
+
// Load template and evaluate it.
|
|
38
52
|
const emailTemplate = await this.emailTemplateService.findOneByName(templateName);
|
|
39
53
|
if (!emailTemplate) {
|
|
40
54
|
throw new Error(`Invalid template name ${templateName}`);
|
|
@@ -49,10 +63,22 @@ export class SMTPEMailService implements IMail {
|
|
|
49
63
|
const subject = subjectTemplate(templateParams);
|
|
50
64
|
|
|
51
65
|
// Finally send the email.
|
|
52
|
-
await this.sendEmail(to, subject, body, shouldQueueEmails, parentEntity, parentEntityId,
|
|
66
|
+
return await this.sendEmail(to, subject, body, shouldQueueEmails, wrapperAttachments, attachments, parentEntity, parentEntityId, cc, bcc, from);
|
|
53
67
|
}
|
|
54
68
|
|
|
55
|
-
async sendEmail(
|
|
69
|
+
async sendEmail(
|
|
70
|
+
to: string,
|
|
71
|
+
subject: string,
|
|
72
|
+
body: string,
|
|
73
|
+
shouldQueueEmails = false,
|
|
74
|
+
wrapperAttachments?: MailAttachmentWrapper[],
|
|
75
|
+
attachments?: MailAttachment[],
|
|
76
|
+
parentEntity?: any,
|
|
77
|
+
parentEntityId?: any,
|
|
78
|
+
cc?: string[],
|
|
79
|
+
bcc?: string[],
|
|
80
|
+
from?: string
|
|
81
|
+
) {
|
|
56
82
|
const message = {
|
|
57
83
|
payload: {
|
|
58
84
|
from: from || this.commonConfiguration.smtpMail.from,
|
|
@@ -69,33 +95,34 @@ export class SMTPEMailService implements IMail {
|
|
|
69
95
|
|
|
70
96
|
// Send using queue if the developer has explicitly invoked with true.
|
|
71
97
|
if (shouldQueueEmails === true) {
|
|
72
|
-
this.sendEmailAsynchronously(message);
|
|
98
|
+
return this.sendEmailAsynchronously(message);
|
|
73
99
|
}
|
|
74
100
|
// If developer has not, however system config mandates that we send using queue, still we send.
|
|
75
101
|
else if (shouldQueueEmails == false && this.commonConfiguration.shouldQueueEmails === true) {
|
|
76
|
-
this.sendEmailAsynchronously(message);
|
|
102
|
+
return this.sendEmailAsynchronously(message);
|
|
77
103
|
}
|
|
78
|
-
// Else we send
|
|
104
|
+
// Else we send synchronously
|
|
79
105
|
else {
|
|
80
|
-
await this.sendEmailSynchronously(message);
|
|
106
|
+
return await this.sendEmailSynchronously(message);
|
|
81
107
|
}
|
|
82
108
|
}
|
|
83
109
|
|
|
84
110
|
async sendEmailAsynchronously(message) {
|
|
85
111
|
const { to, subject, body } = message.payload;
|
|
86
|
-
// this.notificationPublisherService.publish(message);
|
|
87
|
-
// this.emailPublisher.publish(message);
|
|
88
|
-
// this.emailDbPublisher.publish(message);
|
|
89
|
-
|
|
90
|
-
this.publisherFactory.publish(message, 'EmailQueuePublisher');
|
|
91
|
-
|
|
92
112
|
this.logger.debug(`Queueing email to ${to} with subject ${subject} and body ${body}`);
|
|
113
|
+
return this.publisherFactory.publish(message, 'SmtpEmailQueuePublisher');
|
|
93
114
|
}
|
|
94
115
|
|
|
95
|
-
async sendEmailSynchronously(message: QueueMessage<any>)
|
|
96
|
-
const { from, to, subject, body, attachments, cc, bcc } = message.payload;
|
|
116
|
+
async sendEmailSynchronously(message: QueueMessage<any>) {
|
|
117
|
+
const { from, to, subject, body, attachments = [], cc, bcc } = message.payload;
|
|
97
118
|
|
|
98
|
-
|
|
119
|
+
// if any of the required fields are missing, throw an error.
|
|
120
|
+
if (!from || !to || !subject || !body) {
|
|
121
|
+
this.logger.error(`Required fields are missing in the email message: ${JSON.stringify(message.payload)}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const attachmentsList = attachments?.map((attachment: MailAttachment) => {
|
|
99
126
|
const attachmentEntry = {
|
|
100
127
|
filename: attachment.filename,
|
|
101
128
|
contentType: attachment.contentType,
|
|
@@ -107,7 +134,7 @@ export class SMTPEMailService implements IMail {
|
|
|
107
134
|
attachmentEntry['content'] = attachment.content;
|
|
108
135
|
}
|
|
109
136
|
return attachmentEntry;
|
|
110
|
-
});
|
|
137
|
+
}) || [];
|
|
111
138
|
|
|
112
139
|
// throw new Error('Random error....');
|
|
113
140
|
const r = await this.transporter.sendMail({
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/common/poller/poller.service.ts
|
|
2
|
+
import {
|
|
3
|
+
Injectable,
|
|
4
|
+
Logger,
|
|
5
|
+
OnModuleDestroy,
|
|
6
|
+
BeforeApplicationShutdown,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
|
|
9
|
+
export interface PollOptions {
|
|
10
|
+
/** Wait after a successful iteration */
|
|
11
|
+
baseDelayMs?: number; // default 1000
|
|
12
|
+
/** Maximum delay after repeated failures */
|
|
13
|
+
maxDelayMs?: number; // default 30000
|
|
14
|
+
/** Per-iteration timeout guard */
|
|
15
|
+
timeoutPerIterationMs?: number; // default 60000
|
|
16
|
+
/** Add jitter to spread load */
|
|
17
|
+
jitter?: boolean; // default true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ProcessNextFn = (queueName: string) => Promise<unknown>;
|
|
21
|
+
|
|
22
|
+
interface PollerState {
|
|
23
|
+
queueName: string;
|
|
24
|
+
processNext: ProcessNextFn;
|
|
25
|
+
opts: Required<PollOptions>;
|
|
26
|
+
inFlight: boolean;
|
|
27
|
+
stopped: boolean;
|
|
28
|
+
backoff: number;
|
|
29
|
+
nextTimer?: NodeJS.Timeout;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Injectable()
|
|
33
|
+
export class PollerService implements OnModuleDestroy, BeforeApplicationShutdown {
|
|
34
|
+
private readonly logger = new Logger(PollerService.name);
|
|
35
|
+
private readonly pollers = new Map<string, PollerState>();
|
|
36
|
+
|
|
37
|
+
start(queueName: string, processNext: ProcessNextFn, options: PollOptions = {}): void {
|
|
38
|
+
if (this.pollers.has(queueName)) {
|
|
39
|
+
this.logger.warn(`Poller "${queueName}" already started; ignoring.`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const opts: Required<PollOptions> = {
|
|
44
|
+
baseDelayMs: options.baseDelayMs ?? 1000,
|
|
45
|
+
maxDelayMs: options.maxDelayMs ?? 30_000,
|
|
46
|
+
timeoutPerIterationMs: options.timeoutPerIterationMs ?? 60_000,
|
|
47
|
+
jitter: options.jitter ?? true,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const state: PollerState = {
|
|
51
|
+
queueName,
|
|
52
|
+
processNext,
|
|
53
|
+
opts,
|
|
54
|
+
inFlight: false,
|
|
55
|
+
stopped: false,
|
|
56
|
+
backoff: opts.baseDelayMs,
|
|
57
|
+
nextTimer: undefined,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
this.pollers.set(queueName, state);
|
|
61
|
+
// kick off on next tick
|
|
62
|
+
setImmediate(() => this.poll(state).catch(() => { }));
|
|
63
|
+
this.logger.log(`Started poller "${queueName}"`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
stop(queueName: string): void {
|
|
67
|
+
const state = this.pollers.get(queueName);
|
|
68
|
+
if (!state) return;
|
|
69
|
+
|
|
70
|
+
state.stopped = true;
|
|
71
|
+
if (state.nextTimer) {
|
|
72
|
+
clearTimeout(state.nextTimer);
|
|
73
|
+
state.nextTimer = undefined;
|
|
74
|
+
}
|
|
75
|
+
this.pollers.delete(queueName);
|
|
76
|
+
this.logger.log(`Stopped poller "${queueName}"`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
stopAll(): void {
|
|
80
|
+
for (const name of Array.from(this.pollers.keys())) {
|
|
81
|
+
this.stop(name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async onModuleDestroy(): Promise<void> {
|
|
86
|
+
this.stopAll();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async beforeApplicationShutdown(): Promise<void> {
|
|
90
|
+
this.stopAll();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---- internals ----
|
|
94
|
+
|
|
95
|
+
private async poll(state: PollerState): Promise<void> {
|
|
96
|
+
if (state.stopped || state.inFlight) return;
|
|
97
|
+
state.inFlight = true;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await this.withTimeout(
|
|
101
|
+
state.processNext(state.queueName),
|
|
102
|
+
state.opts.timeoutPerIterationMs,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// success: reset backoff and schedule next run after base delay
|
|
106
|
+
state.backoff = state.opts.baseDelayMs;
|
|
107
|
+
// this.logger.debug(`[${state.queueName}] iteration completed`);
|
|
108
|
+
this.schedule(state, state.opts.baseDelayMs);
|
|
109
|
+
} catch (err: unknown) {
|
|
110
|
+
const msg = this.errorToString(err);
|
|
111
|
+
this.logger.error(`[${state.queueName}] iteration failed: ${msg}`);
|
|
112
|
+
|
|
113
|
+
// failure: schedule with backoff + optional jitter, then increase backoff
|
|
114
|
+
const wait = this.computeWait(state.backoff, state.opts);
|
|
115
|
+
state.backoff = Math.min(state.backoff * 2, state.opts.maxDelayMs);
|
|
116
|
+
this.schedule(state, wait);
|
|
117
|
+
} finally {
|
|
118
|
+
state.inFlight = false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private schedule(state: PollerState, delayMs: number) {
|
|
123
|
+
if (state.stopped) return;
|
|
124
|
+
if (state.nextTimer) clearTimeout(state.nextTimer);
|
|
125
|
+
|
|
126
|
+
state.nextTimer = setTimeout(() => {
|
|
127
|
+
// clear reference before calling poll to avoid re-entrancy confusion
|
|
128
|
+
state.nextTimer = undefined;
|
|
129
|
+
this.poll(state).catch(() => { });
|
|
130
|
+
}, delayMs);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private computeWait(currentBackoff: number, opts: Required<PollOptions>): number {
|
|
134
|
+
if (!opts.jitter) return currentBackoff;
|
|
135
|
+
// Full jitter: random in [250ms, currentBackoff * 2], clamped to maxDelayMs
|
|
136
|
+
const doubled = Math.min(currentBackoff * 2, opts.maxDelayMs);
|
|
137
|
+
const jittered = Math.floor(Math.random() * doubled);
|
|
138
|
+
return Math.max(250, jittered);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private async withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {
|
|
142
|
+
let timer: NodeJS.Timeout | undefined;
|
|
143
|
+
try {
|
|
144
|
+
return await Promise.race<T>([
|
|
145
|
+
p,
|
|
146
|
+
new Promise<never>((_, rej) => {
|
|
147
|
+
timer = setTimeout(() => rej(new Error(`Iteration timed out after ${ms} ms`)), ms);
|
|
148
|
+
}),
|
|
149
|
+
]);
|
|
150
|
+
} finally {
|
|
151
|
+
if (timer) clearTimeout(timer);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private errorToString(err: unknown): string {
|
|
156
|
+
if (err instanceof Error) return err.stack ?? err.message;
|
|
157
|
+
try {
|
|
158
|
+
return JSON.stringify(err);
|
|
159
|
+
} catch {
|
|
160
|
+
return String(err);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|