rez_core 2.2.188 → 2.2.189
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/module/communication/controller/communication.controller.d.ts +33 -5
- package/dist/module/communication/controller/communication.controller.js +83 -3
- package/dist/module/communication/controller/communication.controller.js.map +1 -1
- package/dist/module/communication/dto/create-config.dto.d.ts +12 -0
- package/dist/module/communication/dto/create-config.dto.js +36 -1
- package/dist/module/communication/dto/create-config.dto.js.map +1 -1
- package/dist/module/communication/service/communication.service.d.ts +2 -30
- package/dist/module/communication/service/communication.service.js +30 -73
- package/dist/module/communication/service/communication.service.js.map +1 -1
- package/dist/module/communication/strategies/email/sendgrid-api.strategy.d.ts +8 -0
- package/dist/module/communication/strategies/email/sendgrid-api.strategy.js +52 -0
- package/dist/module/communication/strategies/email/sendgrid-api.strategy.js.map +1 -1
- package/dist/module/ics/service/ics.service.d.ts +2 -3
- package/dist/module/ics/service/ics.service.js +41 -35
- package/dist/module/ics/service/ics.service.js.map +1 -1
- package/dist/module/listmaster/service/list-master.service.js +2 -4
- package/dist/module/listmaster/service/list-master.service.js.map +1 -1
- package/dist/module/notification/controller/notification.controller.d.ts +5 -1
- package/dist/module/notification/controller/notification.controller.js +16 -3
- package/dist/module/notification/controller/notification.controller.js.map +1 -1
- package/dist/module/notification/service/notification.service.d.ts +7 -1
- package/dist/module/notification/service/notification.service.js +27 -12
- package/dist/module/notification/service/notification.service.js.map +1 -1
- package/dist/module/workflow/controller/comm-template.controller.d.ts +1 -1
- package/dist/module/workflow/controller/comm-template.controller.js.map +1 -1
- package/dist/module/workflow/repository/comm-template.repository.d.ts +1 -1
- package/dist/module/workflow/repository/comm-template.repository.js +17 -11
- package/dist/module/workflow/repository/comm-template.repository.js.map +1 -1
- package/dist/module/workflow/service/comm-template.service.d.ts +1 -1
- package/dist/module/workflow/service/comm-template.service.js.map +1 -1
- package/dist/module/workflow/service/task.service.js +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/module/communication/controller/communication.controller.ts +94 -11
- package/src/module/communication/dto/create-config.dto.ts +26 -0
- package/src/module/communication/service/communication.service.ts +68 -127
- package/src/module/communication/strategies/email/sendgrid-api.strategy.ts +65 -0
- package/src/module/ics/service/ics.service.ts +49 -37
- package/src/module/listmaster/service/list-master.service.ts +3 -6
- package/src/module/notification/controller/notification.controller.ts +21 -3
- package/src/module/notification/service/notification.service.ts +45 -20
- package/src/module/workflow/controller/comm-template.controller.ts +1 -1
- package/src/module/workflow/repository/comm-template.repository.ts +30 -19
- package/src/module/workflow/service/comm-template.service.ts +2 -2
- package/src/module/workflow/service/task.service.ts +1 -1
package/package.json
CHANGED
|
@@ -5,12 +5,15 @@ import {
|
|
|
5
5
|
Param,
|
|
6
6
|
Post,
|
|
7
7
|
Put,
|
|
8
|
+
Delete,
|
|
8
9
|
ParseIntPipe,
|
|
9
10
|
HttpStatus,
|
|
10
11
|
HttpCode,
|
|
11
12
|
Query,
|
|
13
|
+
BadRequestException,
|
|
12
14
|
} from '@nestjs/common';
|
|
13
15
|
import { CommunicationService } from '../service/communication.service';
|
|
16
|
+
import { SendGridApiStrategy } from '../strategies/email/sendgrid-api.strategy';
|
|
14
17
|
import {
|
|
15
18
|
CreateConfigDto,
|
|
16
19
|
UpdateConfigStatusDto,
|
|
@@ -18,6 +21,7 @@ import {
|
|
|
18
21
|
BulkMessageDto,
|
|
19
22
|
GmailOAuthInitDto,
|
|
20
23
|
OutlookOAuthInitDto,
|
|
24
|
+
SendGridVerifiedSendersDto,
|
|
21
25
|
} from '../dto/create-config.dto';
|
|
22
26
|
|
|
23
27
|
export class ScheduledMessageDto extends GenericSendMessageDto {
|
|
@@ -27,7 +31,10 @@ export class ScheduledMessageDto extends GenericSendMessageDto {
|
|
|
27
31
|
|
|
28
32
|
@Controller('communication')
|
|
29
33
|
export class CommunicationController {
|
|
30
|
-
constructor(
|
|
34
|
+
constructor(
|
|
35
|
+
private readonly communicationService: CommunicationService,
|
|
36
|
+
private readonly sendGridApiStrategy: SendGridApiStrategy,
|
|
37
|
+
) {}
|
|
31
38
|
|
|
32
39
|
@Post('send')
|
|
33
40
|
@HttpCode(HttpStatus.OK)
|
|
@@ -64,16 +71,66 @@ export class CommunicationController {
|
|
|
64
71
|
|
|
65
72
|
@Post('config')
|
|
66
73
|
async createConfig(@Body() createConfigDto: CreateConfigDto) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
try {
|
|
75
|
+
const result = await this.communicationService.createCommunicationConfig(
|
|
76
|
+
createConfigDto.levelId,
|
|
77
|
+
createConfigDto.levelType,
|
|
78
|
+
createConfigDto.configType,
|
|
79
|
+
createConfigDto.service,
|
|
80
|
+
createConfigDto.provider,
|
|
81
|
+
createConfigDto.config,
|
|
82
|
+
createConfigDto.priority,
|
|
83
|
+
createConfigDto.is_default,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
success: true,
|
|
88
|
+
data: result,
|
|
89
|
+
message: 'Communication configuration created successfully',
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
// Handle validation errors with BadRequest status
|
|
93
|
+
if (
|
|
94
|
+
error.message?.includes('active') &&
|
|
95
|
+
error.message?.includes('configuration already exists')
|
|
96
|
+
) {
|
|
97
|
+
throw new BadRequestException({
|
|
98
|
+
success: false,
|
|
99
|
+
error: 'DUPLICATE_CONFIGURATION',
|
|
100
|
+
message: error.message,
|
|
101
|
+
code: 'VALIDATION_ERROR',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Handle unsupported combination errors
|
|
106
|
+
if (error.message?.includes('Unsupported combination')) {
|
|
107
|
+
throw new BadRequestException({
|
|
108
|
+
success: false,
|
|
109
|
+
error: 'UNSUPPORTED_COMBINATION',
|
|
110
|
+
message: error.message,
|
|
111
|
+
code: 'VALIDATION_ERROR',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Handle OAuth redirect responses (return as success with OAuth info)
|
|
116
|
+
if (error.authUrl && error.state) {
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
requiresOAuth: true,
|
|
120
|
+
authUrl: error.authUrl,
|
|
121
|
+
state: error.state,
|
|
122
|
+
message: error.message || 'OAuth authorization required',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handle other errors as BadRequest
|
|
127
|
+
throw new BadRequestException({
|
|
128
|
+
success: false,
|
|
129
|
+
error: 'CONFIGURATION_ERROR',
|
|
130
|
+
message: error.message || 'Failed to create communication configuration',
|
|
131
|
+
code: 'INTERNAL_ERROR',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
77
134
|
}
|
|
78
135
|
|
|
79
136
|
@Get('supported-combinations')
|
|
@@ -141,4 +198,30 @@ export class CommunicationController {
|
|
|
141
198
|
initDto.email,
|
|
142
199
|
);
|
|
143
200
|
}
|
|
201
|
+
|
|
202
|
+
@Post('sendgrid/verified-senders')
|
|
203
|
+
@HttpCode(HttpStatus.OK)
|
|
204
|
+
async getSendGridVerifiedSenders(@Body() dto: SendGridVerifiedSendersDto) {
|
|
205
|
+
return this.sendGridApiStrategy.getVerifiedSenders(dto.apiKey);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@Post('config/delete/:hubId')
|
|
209
|
+
@HttpCode(HttpStatus.OK)
|
|
210
|
+
async disconnectConfig(@Param('hubId', ParseIntPipe) hubId: number) {
|
|
211
|
+
try {
|
|
212
|
+
await this.communicationService.deleteConfiguration(hubId);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
message: 'Communication configuration deleted successfully',
|
|
217
|
+
};
|
|
218
|
+
} catch (error) {
|
|
219
|
+
throw new BadRequestException({
|
|
220
|
+
success: false,
|
|
221
|
+
error: 'DELETE_ERROR',
|
|
222
|
+
message: error.message || 'Failed to delete communication configuration',
|
|
223
|
+
code: 'CONFIGURATION_ERROR',
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
144
227
|
}
|
|
@@ -251,3 +251,29 @@ export class OutlookOAuthInitDto {
|
|
|
251
251
|
@IsString()
|
|
252
252
|
email?: string;
|
|
253
253
|
}
|
|
254
|
+
|
|
255
|
+
export class SendGridVerifiedSendersDto {
|
|
256
|
+
@IsNotEmpty()
|
|
257
|
+
@IsString()
|
|
258
|
+
apiKey: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export class VerifiedSenderDto {
|
|
262
|
+
@IsString()
|
|
263
|
+
label: string;
|
|
264
|
+
|
|
265
|
+
@IsString()
|
|
266
|
+
value: string;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export class VerifiedSendersResponseDto {
|
|
270
|
+
@IsBoolean()
|
|
271
|
+
success: boolean;
|
|
272
|
+
|
|
273
|
+
@IsOptional()
|
|
274
|
+
data?: VerifiedSenderDto[];
|
|
275
|
+
|
|
276
|
+
@IsOptional()
|
|
277
|
+
@IsString()
|
|
278
|
+
error?: string;
|
|
279
|
+
}
|
|
@@ -267,6 +267,9 @@ export class CommunicationService {
|
|
|
267
267
|
): Promise<
|
|
268
268
|
CommunicationHub | { authUrl: string; state: string; message: string }
|
|
269
269
|
> {
|
|
270
|
+
// Validate that no active configuration of the same type exists (first check)
|
|
271
|
+
await this.validateUniqueActiveConfig(levelId, levelType, configType);
|
|
272
|
+
|
|
270
273
|
// Validate service/provider combination using supported combinations
|
|
271
274
|
const supportedCombinations = await this.getSupportedCombinations();
|
|
272
275
|
const isValidCombination = supportedCombinations.some(
|
|
@@ -647,6 +650,25 @@ export class CommunicationService {
|
|
|
647
650
|
await this.hubRepository.update(hubId, { status });
|
|
648
651
|
}
|
|
649
652
|
|
|
653
|
+
async deleteConfiguration(hubId: number): Promise<void> {
|
|
654
|
+
// Find the hub to get the config_id
|
|
655
|
+
const hub = await this.hubRepository.findOne({
|
|
656
|
+
where: { id: hubId },
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
if (!hub) {
|
|
660
|
+
throw new Error('Communication configuration not found');
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
await this.hubRepository.delete(hubId);
|
|
664
|
+
|
|
665
|
+
await this.configRepository.delete(hub.config_id);
|
|
666
|
+
|
|
667
|
+
this.logger.log(
|
|
668
|
+
`Communication configuration deleted: Hub ${hubId}, Config ${hub.config_id}`,
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
650
672
|
async sendGenericMessage(
|
|
651
673
|
messageDto: GenericMessageDto,
|
|
652
674
|
): Promise<CommunicationResult> {
|
|
@@ -1075,6 +1097,7 @@ export class CommunicationService {
|
|
|
1075
1097
|
'https://www.googleapis.com/auth/gmail.send',
|
|
1076
1098
|
'https://www.googleapis.com/auth/gmail.readonly',
|
|
1077
1099
|
'https://www.googleapis.com/auth/userinfo.email',
|
|
1100
|
+
'https://www.googleapis.com/auth/calendar',
|
|
1078
1101
|
];
|
|
1079
1102
|
|
|
1080
1103
|
const authUrl = oauth2Client.generateAuthUrl({
|
|
@@ -1153,6 +1176,13 @@ export class CommunicationService {
|
|
|
1153
1176
|
expiryDate: tokens.expiry_date,
|
|
1154
1177
|
};
|
|
1155
1178
|
|
|
1179
|
+
// Validate that no active EMAIL configuration exists
|
|
1180
|
+
await this.validateUniqueActiveConfig(
|
|
1181
|
+
oauthState.levelId,
|
|
1182
|
+
oauthState.levelType,
|
|
1183
|
+
CommunicationConfigType.EMAIL,
|
|
1184
|
+
);
|
|
1185
|
+
|
|
1156
1186
|
// Create communication config
|
|
1157
1187
|
const communicationConfig = this.configRepository.create({
|
|
1158
1188
|
config_json: gmailConfig as any,
|
|
@@ -1209,6 +1239,13 @@ export class CommunicationService {
|
|
|
1209
1239
|
throw new Error('Email mismatch with OAuth hint');
|
|
1210
1240
|
}
|
|
1211
1241
|
|
|
1242
|
+
// Validate that no active EMAIL configuration exists
|
|
1243
|
+
await this.validateUniqueActiveConfig(
|
|
1244
|
+
oauthState.levelId,
|
|
1245
|
+
oauthState.levelType,
|
|
1246
|
+
CommunicationConfigType.EMAIL,
|
|
1247
|
+
);
|
|
1248
|
+
|
|
1212
1249
|
// Pure SSO configuration - no client credentials stored per user
|
|
1213
1250
|
const gmailConfig = {
|
|
1214
1251
|
email: email,
|
|
@@ -1298,6 +1335,27 @@ export class CommunicationService {
|
|
|
1298
1335
|
return Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
1299
1336
|
}
|
|
1300
1337
|
|
|
1338
|
+
private async validateUniqueActiveConfig(
|
|
1339
|
+
levelId: number,
|
|
1340
|
+
levelType: string,
|
|
1341
|
+
configType: CommunicationConfigType,
|
|
1342
|
+
): Promise<void> {
|
|
1343
|
+
const existingActiveConfig = await this.hubRepository.findOne({
|
|
1344
|
+
where: {
|
|
1345
|
+
level_id: levelId,
|
|
1346
|
+
level_type: levelType,
|
|
1347
|
+
communication_config_type: configType,
|
|
1348
|
+
status: 1, // Active status
|
|
1349
|
+
},
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
if (existingActiveConfig) {
|
|
1353
|
+
throw new Error(
|
|
1354
|
+
`An active ${configType} configuration already exists for ${levelType} ${levelId}. Please deactivate the existing configuration first.`,
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1301
1359
|
private requiresOAuthFlow(
|
|
1302
1360
|
configType: CommunicationConfigType,
|
|
1303
1361
|
service: string,
|
|
@@ -1378,6 +1436,9 @@ export class CommunicationService {
|
|
|
1378
1436
|
priority?: number,
|
|
1379
1437
|
is_default?: boolean,
|
|
1380
1438
|
): Promise<CommunicationHub> {
|
|
1439
|
+
// Validate that no active configuration of the same type exists
|
|
1440
|
+
await this.validateUniqueActiveConfig(levelId, levelType, configType);
|
|
1441
|
+
|
|
1381
1442
|
// If setting as default, remove default from other configurations of same type
|
|
1382
1443
|
if (is_default) {
|
|
1383
1444
|
await this.hubRepository.update(
|
|
@@ -1537,6 +1598,13 @@ export class CommunicationService {
|
|
|
1537
1598
|
throw new Error('Failed to get user email');
|
|
1538
1599
|
}
|
|
1539
1600
|
|
|
1601
|
+
// Validate that no active EMAIL configuration exists
|
|
1602
|
+
await this.validateUniqueActiveConfig(
|
|
1603
|
+
oauthState.levelId,
|
|
1604
|
+
oauthState.levelType,
|
|
1605
|
+
CommunicationConfigType.EMAIL,
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1540
1608
|
const outlookConfig = {
|
|
1541
1609
|
clientId,
|
|
1542
1610
|
tenantId,
|
|
@@ -1583,131 +1651,4 @@ export class CommunicationService {
|
|
|
1583
1651
|
throw new Error(`Failed to complete Outlook OAuth: ${error.message}`);
|
|
1584
1652
|
}
|
|
1585
1653
|
}
|
|
1586
|
-
|
|
1587
|
-
// Simple Queue Integration Methods
|
|
1588
|
-
|
|
1589
|
-
async queueMessage(
|
|
1590
|
-
messageDto: GenericMessageDto & { useQueue?: boolean; scheduledFor?: Date },
|
|
1591
|
-
): Promise<{
|
|
1592
|
-
queued: boolean;
|
|
1593
|
-
messageId?: string;
|
|
1594
|
-
result?: CommunicationResult;
|
|
1595
|
-
}> {
|
|
1596
|
-
if (!this.queueService || !messageDto.useQueue) {
|
|
1597
|
-
// Fallback to immediate sending if queue is not available or not requested
|
|
1598
|
-
const result = await this.sendGenericMessage(messageDto);
|
|
1599
|
-
return { queued: false, result };
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
try {
|
|
1603
|
-
const priority = this.mapPriorityToSimpleQueue(messageDto.priority);
|
|
1604
|
-
|
|
1605
|
-
const messageId = await this.queueService.queueMessage(
|
|
1606
|
-
messageDto.levelId,
|
|
1607
|
-
messageDto.levelType,
|
|
1608
|
-
Array.isArray(messageDto.to) ? messageDto.to[0] : messageDto.to,
|
|
1609
|
-
messageDto.message,
|
|
1610
|
-
messageDto.type,
|
|
1611
|
-
priority,
|
|
1612
|
-
messageDto.scheduledFor,
|
|
1613
|
-
{
|
|
1614
|
-
subject: messageDto.subject,
|
|
1615
|
-
html: messageDto.html,
|
|
1616
|
-
cc: messageDto.cc,
|
|
1617
|
-
bcc: messageDto.bcc,
|
|
1618
|
-
attachments: messageDto.attachments,
|
|
1619
|
-
mediaUrl: messageDto.mediaUrl,
|
|
1620
|
-
templateId: messageDto.templateId,
|
|
1621
|
-
variables: messageDto.variables,
|
|
1622
|
-
},
|
|
1623
|
-
);
|
|
1624
|
-
|
|
1625
|
-
return { queued: true, messageId };
|
|
1626
|
-
} catch (error) {
|
|
1627
|
-
this.logger.error(
|
|
1628
|
-
'Error queuing message, falling back to immediate send:',
|
|
1629
|
-
error.message,
|
|
1630
|
-
);
|
|
1631
|
-
// Fallback to immediate sending
|
|
1632
|
-
const result = await this.sendGenericMessage(messageDto);
|
|
1633
|
-
return { queued: false, result };
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
async queueBulkMessage(
|
|
1638
|
-
bulkDto: BulkMessageDto & { useQueue?: boolean; scheduledFor?: Date },
|
|
1639
|
-
): Promise<{ queued: boolean; messageIds?: string[]; result?: any }> {
|
|
1640
|
-
if (!this.queueService || !bulkDto.useQueue) {
|
|
1641
|
-
// Fallback to immediate sending if queue is not available or not requested
|
|
1642
|
-
const result = await this.sendBulkMessage(bulkDto);
|
|
1643
|
-
return { queued: false, result };
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
try {
|
|
1647
|
-
const priority = this.mapPriorityToSimpleQueue(bulkDto.priority) || 'low';
|
|
1648
|
-
|
|
1649
|
-
const messageIds = await this.queueService.queueBulkMessages(
|
|
1650
|
-
bulkDto.levelId,
|
|
1651
|
-
bulkDto.levelType,
|
|
1652
|
-
bulkDto.recipients,
|
|
1653
|
-
bulkDto.message,
|
|
1654
|
-
bulkDto.type,
|
|
1655
|
-
priority,
|
|
1656
|
-
{
|
|
1657
|
-
subject: bulkDto.subject,
|
|
1658
|
-
html: bulkDto.html,
|
|
1659
|
-
templateId: bulkDto.templateId,
|
|
1660
|
-
variables: bulkDto.variables,
|
|
1661
|
-
},
|
|
1662
|
-
);
|
|
1663
|
-
|
|
1664
|
-
return { queued: true, messageIds };
|
|
1665
|
-
} catch (error) {
|
|
1666
|
-
this.logger.error(
|
|
1667
|
-
'Error queuing bulk message, falling back to immediate send:',
|
|
1668
|
-
error.message,
|
|
1669
|
-
);
|
|
1670
|
-
// Fallback to immediate sending
|
|
1671
|
-
const result = await this.sendBulkMessage(bulkDto);
|
|
1672
|
-
return { queued: false, result };
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
// Helper method to check if queue service is available
|
|
1677
|
-
isQueueAvailable(): boolean {
|
|
1678
|
-
return !!this.queueService;
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
// Helper method to get queue stats if available
|
|
1682
|
-
getQueueStats() {
|
|
1683
|
-
if (!this.queueService) {
|
|
1684
|
-
return null;
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
return this.queueService.getQueueStats();
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
// Helper method to cancel queued messages
|
|
1691
|
-
cancelQueuedMessage(messageId: string): boolean {
|
|
1692
|
-
if (!this.queueService) {
|
|
1693
|
-
return false;
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
return this.queueService.cancelMessage(messageId);
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
private mapPriorityToSimpleQueue(
|
|
1700
|
-
priority?: string,
|
|
1701
|
-
): 'high' | 'medium' | 'low' {
|
|
1702
|
-
switch (priority?.toLowerCase()) {
|
|
1703
|
-
case 'high':
|
|
1704
|
-
case 'urgent':
|
|
1705
|
-
return 'high';
|
|
1706
|
-
case 'low':
|
|
1707
|
-
return 'low';
|
|
1708
|
-
case 'medium':
|
|
1709
|
-
default:
|
|
1710
|
-
return 'medium';
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
1654
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
2
|
import * as sgMail from '@sendgrid/mail';
|
|
3
|
+
import axios from 'axios';
|
|
3
4
|
import {
|
|
4
5
|
CommunicationStrategy,
|
|
5
6
|
CommunicationResult,
|
|
@@ -114,4 +115,68 @@ export class SendGridApiStrategy implements CommunicationStrategy {
|
|
|
114
115
|
validateConfig(config: any): boolean {
|
|
115
116
|
return !!(config.apiKey && config.fromEmail);
|
|
116
117
|
}
|
|
118
|
+
|
|
119
|
+
async getVerifiedSenders(apiKey: string): Promise<{
|
|
120
|
+
success: boolean;
|
|
121
|
+
data?: Array<{
|
|
122
|
+
label: string;
|
|
123
|
+
value: string;
|
|
124
|
+
}>;
|
|
125
|
+
error?: string;
|
|
126
|
+
}> {
|
|
127
|
+
try {
|
|
128
|
+
if (!apiKey) {
|
|
129
|
+
throw new Error('SendGrid API key is required');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const response = await axios.get(
|
|
133
|
+
'https://api.sendgrid.com/v3/verified_senders',
|
|
134
|
+
{
|
|
135
|
+
headers: {
|
|
136
|
+
Authorization: `Bearer ${apiKey}`,
|
|
137
|
+
'Content-Type': 'application/json',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const verifiedSenders = response.data.results || response.data || [];
|
|
143
|
+
|
|
144
|
+
// Format the response for dropdown usage with label and value both as from_email
|
|
145
|
+
const formattedSenders = verifiedSenders
|
|
146
|
+
.filter((sender: any) => sender.verified === true)
|
|
147
|
+
.map((sender: any) => {
|
|
148
|
+
const fromEmail = sender.from_email || sender.email;
|
|
149
|
+
return {
|
|
150
|
+
label: fromEmail,
|
|
151
|
+
value: fromEmail,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
data: formattedSenders,
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
let errorMessage = 'Failed to fetch verified senders';
|
|
161
|
+
|
|
162
|
+
if (error.response?.status === 401) {
|
|
163
|
+
errorMessage = 'Invalid SendGrid API key';
|
|
164
|
+
} else if (error.response?.status === 403) {
|
|
165
|
+
errorMessage = 'SendGrid API key does not have required permissions';
|
|
166
|
+
} else if (error.response?.data?.errors) {
|
|
167
|
+
errorMessage = error.response.data.errors
|
|
168
|
+
.map((err: any) => err.message)
|
|
169
|
+
.join(', ');
|
|
170
|
+
} else if (error.response?.data?.message) {
|
|
171
|
+
errorMessage = error.response.data.message;
|
|
172
|
+
} else if (error.message) {
|
|
173
|
+
errorMessage = error.message;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: errorMessage,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
117
182
|
}
|
|
@@ -1,48 +1,60 @@
|
|
|
1
|
-
// src/calendar/calendar.service.ts
|
|
2
1
|
import { Injectable } from '@nestjs/common';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import * as moment from 'moment';
|
|
5
4
|
|
|
6
5
|
@Injectable()
|
|
7
6
|
export class IcsMeetingService {
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
return [
|
|
11
|
-
d.getUTCFullYear(),
|
|
12
|
-
d.getUTCMonth() + 1,
|
|
13
|
-
d.getUTCDate(),
|
|
14
|
-
d.getUTCHours(),
|
|
15
|
-
d.getUTCMinutes(),
|
|
16
|
-
];
|
|
7
|
+
private formatUtc(dateIso: string | Date): string {
|
|
8
|
+
return moment.utc(dateIso).format('YYYYMMDDTHHmmss[Z]');
|
|
17
9
|
}
|
|
18
10
|
|
|
19
|
-
async generateIcs(dto:
|
|
20
|
-
const
|
|
11
|
+
async generateIcs(dto: any): Promise<string> {
|
|
12
|
+
const uid = `${uuidv4()}@etherapp.in`;
|
|
13
|
+
const now = this.formatUtc(new Date());
|
|
21
14
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
minutes: dto.durationMinutes % 60,
|
|
27
|
-
},
|
|
28
|
-
title: dto.title,
|
|
29
|
-
description: dto.description ?? '',
|
|
30
|
-
location: dto.location ?? 'Online',
|
|
31
|
-
url: dto.meetingUrl,
|
|
32
|
-
organizer: dto.organizerEmail
|
|
33
|
-
? { name: dto.organizerName ?? 'Organizer', email: dto.organizerEmail }
|
|
34
|
-
: undefined,
|
|
35
|
-
attendees: dto.attendees?.map((a) => ({ name: a.name, email: a.email })),
|
|
36
|
-
status: 'CONFIRMED' as const,
|
|
37
|
-
};
|
|
15
|
+
const dtStart = this.formatUtc(dto.startsAt);
|
|
16
|
+
const dtEnd = this.formatUtc(
|
|
17
|
+
new Date(new Date(dto.startsAt).getTime() + dto.durationMinutes * 60000),
|
|
18
|
+
);
|
|
38
19
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
20
|
+
const organizer = dto.organizerEmail
|
|
21
|
+
? `ORGANIZER;CN=${dto.organizerName ?? 'Organizer'}:mailto:${dto.organizerEmail}`
|
|
22
|
+
: '';
|
|
42
23
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
24
|
+
const attendees = dto.attendees
|
|
25
|
+
?.map(
|
|
26
|
+
(a) =>
|
|
27
|
+
`ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=${a.partStat ?? 'NEEDS-ACTION'};CN=${a.name};X-NUM-GUESTS=0:mailto:${a.email}`,
|
|
28
|
+
)
|
|
29
|
+
.join('\r\n');
|
|
30
|
+
|
|
31
|
+
const icsContent = [
|
|
32
|
+
'BEGIN:VCALENDAR',
|
|
33
|
+
'PRODID:-//Google Inc//Google Calendar 70.9054//EN',
|
|
34
|
+
'VERSION:2.0',
|
|
35
|
+
'CALSCALE:GREGORIAN',
|
|
36
|
+
'METHOD:REPLY',
|
|
37
|
+
'BEGIN:VEVENT',
|
|
38
|
+
`DTSTART:${dtStart}`,
|
|
39
|
+
`DTEND:${dtEnd}`,
|
|
40
|
+
`DTSTAMP:${now}`,
|
|
41
|
+
organizer,
|
|
42
|
+
`UID:${uid}`,
|
|
43
|
+
attendees ?? '',
|
|
44
|
+
`CREATED:${now}`,
|
|
45
|
+
`DESCRIPTION:${dto.description ?? ''}`,
|
|
46
|
+
`LAST-MODIFIED:${now}`,
|
|
47
|
+
`LOCATION:${dto.location ?? 'Google Meet'}`,
|
|
48
|
+
'SEQUENCE:0',
|
|
49
|
+
'STATUS:CONFIRMED',
|
|
50
|
+
`SUMMARY:${dto.title}`,
|
|
51
|
+
'TRANSP:OPAQUE',
|
|
52
|
+
'END:VEVENT',
|
|
53
|
+
'END:VCALENDAR',
|
|
54
|
+
]
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
.join('\r\n');
|
|
57
|
+
|
|
58
|
+
return Buffer.from(icsContent, 'utf-8').toString('base64');
|
|
47
59
|
}
|
|
48
60
|
}
|
|
@@ -296,16 +296,13 @@ export class ListMasterService {
|
|
|
296
296
|
async createEntity(entityData: any, loggedInUser: UserData): Promise<any> {
|
|
297
297
|
// Trim and validate name and code
|
|
298
298
|
const name = entityData.name?.trim();
|
|
299
|
-
const code = entityData.code?.trim();
|
|
299
|
+
// const code = entityData.code?.trim();
|
|
300
300
|
|
|
301
|
-
if (!name
|
|
302
|
-
throw new BadRequestException(
|
|
303
|
-
'Name and Code are required and cannot be empty',
|
|
304
|
-
);
|
|
301
|
+
if (!name) {
|
|
302
|
+
throw new BadRequestException('Name is required and cannot be empty');
|
|
305
303
|
}
|
|
306
304
|
|
|
307
305
|
entityData.name = name;
|
|
308
|
-
entityData.code = code;
|
|
309
306
|
entityData.is_factory = entityData.is_factory || 0;
|
|
310
307
|
entityData.source = entityData.source || 'master';
|
|
311
308
|
entityData.status = entityData.status || 'ACTIVE';
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Body,
|
|
3
|
+
Controller,
|
|
4
|
+
Get,
|
|
5
|
+
Post,
|
|
6
|
+
Query,
|
|
7
|
+
Req,
|
|
8
|
+
UseGuards,
|
|
9
|
+
} from '@nestjs/common';
|
|
2
10
|
import { JwtAuthGuard } from 'src/module/auth/guards/jwt.guard';
|
|
3
11
|
import { NotificationsService } from '../service/notification.service';
|
|
4
12
|
|
|
@@ -26,8 +34,18 @@ export class NotificationsController {
|
|
|
26
34
|
|
|
27
35
|
@Get('all')
|
|
28
36
|
@UseGuards(JwtAuthGuard)
|
|
29
|
-
async getNotifications(@Req() req: any) {
|
|
37
|
+
async getNotifications(@Req() req: any, @Query() filterQuery: any) {
|
|
30
38
|
const loggedInUser = req.user.userData;
|
|
31
|
-
return this.notificationsService.getAllNotifications(
|
|
39
|
+
return this.notificationsService.getAllNotifications(
|
|
40
|
+
loggedInUser,
|
|
41
|
+
filterQuery,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Post('mark-all-read')
|
|
46
|
+
@UseGuards(JwtAuthGuard)
|
|
47
|
+
async markAllAsRead(@Req() req: any) {
|
|
48
|
+
const loggedInUser = req.user.userData;
|
|
49
|
+
return this.notificationsService.markAllAsRead(loggedInUser);
|
|
32
50
|
}
|
|
33
51
|
}
|