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.
Files changed (45) hide show
  1. package/dist/module/communication/controller/communication.controller.d.ts +33 -5
  2. package/dist/module/communication/controller/communication.controller.js +83 -3
  3. package/dist/module/communication/controller/communication.controller.js.map +1 -1
  4. package/dist/module/communication/dto/create-config.dto.d.ts +12 -0
  5. package/dist/module/communication/dto/create-config.dto.js +36 -1
  6. package/dist/module/communication/dto/create-config.dto.js.map +1 -1
  7. package/dist/module/communication/service/communication.service.d.ts +2 -30
  8. package/dist/module/communication/service/communication.service.js +30 -73
  9. package/dist/module/communication/service/communication.service.js.map +1 -1
  10. package/dist/module/communication/strategies/email/sendgrid-api.strategy.d.ts +8 -0
  11. package/dist/module/communication/strategies/email/sendgrid-api.strategy.js +52 -0
  12. package/dist/module/communication/strategies/email/sendgrid-api.strategy.js.map +1 -1
  13. package/dist/module/ics/service/ics.service.d.ts +2 -3
  14. package/dist/module/ics/service/ics.service.js +41 -35
  15. package/dist/module/ics/service/ics.service.js.map +1 -1
  16. package/dist/module/listmaster/service/list-master.service.js +2 -4
  17. package/dist/module/listmaster/service/list-master.service.js.map +1 -1
  18. package/dist/module/notification/controller/notification.controller.d.ts +5 -1
  19. package/dist/module/notification/controller/notification.controller.js +16 -3
  20. package/dist/module/notification/controller/notification.controller.js.map +1 -1
  21. package/dist/module/notification/service/notification.service.d.ts +7 -1
  22. package/dist/module/notification/service/notification.service.js +27 -12
  23. package/dist/module/notification/service/notification.service.js.map +1 -1
  24. package/dist/module/workflow/controller/comm-template.controller.d.ts +1 -1
  25. package/dist/module/workflow/controller/comm-template.controller.js.map +1 -1
  26. package/dist/module/workflow/repository/comm-template.repository.d.ts +1 -1
  27. package/dist/module/workflow/repository/comm-template.repository.js +17 -11
  28. package/dist/module/workflow/repository/comm-template.repository.js.map +1 -1
  29. package/dist/module/workflow/service/comm-template.service.d.ts +1 -1
  30. package/dist/module/workflow/service/comm-template.service.js.map +1 -1
  31. package/dist/module/workflow/service/task.service.js +1 -1
  32. package/dist/tsconfig.build.tsbuildinfo +1 -1
  33. package/package.json +1 -1
  34. package/src/module/communication/controller/communication.controller.ts +94 -11
  35. package/src/module/communication/dto/create-config.dto.ts +26 -0
  36. package/src/module/communication/service/communication.service.ts +68 -127
  37. package/src/module/communication/strategies/email/sendgrid-api.strategy.ts +65 -0
  38. package/src/module/ics/service/ics.service.ts +49 -37
  39. package/src/module/listmaster/service/list-master.service.ts +3 -6
  40. package/src/module/notification/controller/notification.controller.ts +21 -3
  41. package/src/module/notification/service/notification.service.ts +45 -20
  42. package/src/module/workflow/controller/comm-template.controller.ts +1 -1
  43. package/src/module/workflow/repository/comm-template.repository.ts +30 -19
  44. package/src/module/workflow/service/comm-template.service.ts +2 -2
  45. package/src/module/workflow/service/task.service.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "2.2.188",
3
+ "version": "2.2.189",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -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(private readonly communicationService: CommunicationService) {}
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
- return this.communicationService.createCommunicationConfig(
68
- createConfigDto.levelId,
69
- createConfigDto.levelType,
70
- createConfigDto.configType,
71
- createConfigDto.service,
72
- createConfigDto.provider,
73
- createConfigDto.config,
74
- createConfigDto.priority,
75
- createConfigDto.is_default,
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 { createEvent } from 'ics';
4
- import { CreateInviteDto } from '../dto/ics.dto';
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 toDateParts(iso: string): number[] {
9
- const d = new Date(iso);
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: CreateInviteDto): Promise<string> {
20
- const startParts = this.toDateParts(dto.startsAt);
11
+ async generateIcs(dto: any): Promise<string> {
12
+ const uid = `${uuidv4()}@etherapp.in`;
13
+ const now = this.formatUtc(new Date());
21
14
 
22
- const event: any = {
23
- start: startParts,
24
- duration: {
25
- hours: Math.floor(dto.durationMinutes / 60),
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
- return new Promise((resolve, reject) => {
40
- createEvent(event, (err, value) => {
41
- if (err) reject(err);
20
+ const organizer = dto.organizerEmail
21
+ ? `ORGANIZER;CN=${dto.organizerName ?? 'Organizer'}:mailto:${dto.organizerEmail}`
22
+ : '';
42
23
 
43
- const base64 = Buffer.from(value, 'utf-8').toString('base64');
44
- resolve(base64);
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 || !code) {
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 { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
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(loggedInUser);
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
  }