rez_core 2.2.185 → 2.2.187

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 (33) hide show
  1. package/dist/module/communication/controller/communication.controller.d.ts +12 -2
  2. package/dist/module/communication/controller/communication.controller.js +16 -2
  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.js +1 -0
  8. package/dist/module/communication/service/communication.service.js.map +1 -1
  9. package/dist/module/communication/strategies/email/sendgrid-api.strategy.d.ts +8 -0
  10. package/dist/module/communication/strategies/email/sendgrid-api.strategy.js +52 -0
  11. package/dist/module/communication/strategies/email/sendgrid-api.strategy.js.map +1 -1
  12. package/dist/module/listmaster/service/list-master.service.js +2 -4
  13. package/dist/module/listmaster/service/list-master.service.js.map +1 -1
  14. package/dist/module/notification/controller/notification.controller.d.ts +5 -1
  15. package/dist/module/notification/controller/notification.controller.js +16 -3
  16. package/dist/module/notification/controller/notification.controller.js.map +1 -1
  17. package/dist/module/notification/controller/otp.controller.d.ts +2 -2
  18. package/dist/module/notification/service/email.service.js +10 -5
  19. package/dist/module/notification/service/email.service.js.map +1 -1
  20. package/dist/module/notification/service/notification.service.d.ts +7 -1
  21. package/dist/module/notification/service/notification.service.js +27 -12
  22. package/dist/module/notification/service/notification.service.js.map +1 -1
  23. package/dist/tsconfig.build.tsbuildinfo +1 -1
  24. package/package.json +1 -1
  25. package/src/module/communication/controller/communication.controller.ts +12 -1
  26. package/src/module/communication/dto/create-config.dto.ts +26 -0
  27. package/src/module/communication/service/communication.service.ts +1 -0
  28. package/src/module/communication/strategies/email/sendgrid-api.strategy.ts +65 -0
  29. package/src/module/listmaster/service/list-master.service.ts +3 -6
  30. package/src/module/notification/controller/notification.controller.ts +21 -3
  31. package/src/module/notification/controller/otp.controller.ts +2 -2
  32. package/src/module/notification/service/email.service.ts +15 -7
  33. package/src/module/notification/service/notification.service.ts +45 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "2.2.185",
3
+ "version": "2.2.187",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -11,6 +11,7 @@ import {
11
11
  Query,
12
12
  } from '@nestjs/common';
13
13
  import { CommunicationService } from '../service/communication.service';
14
+ import { SendGridApiStrategy } from '../strategies/email/sendgrid-api.strategy';
14
15
  import {
15
16
  CreateConfigDto,
16
17
  UpdateConfigStatusDto,
@@ -18,6 +19,7 @@ import {
18
19
  BulkMessageDto,
19
20
  GmailOAuthInitDto,
20
21
  OutlookOAuthInitDto,
22
+ SendGridVerifiedSendersDto,
21
23
  } from '../dto/create-config.dto';
22
24
 
23
25
  export class ScheduledMessageDto extends GenericSendMessageDto {
@@ -27,7 +29,10 @@ export class ScheduledMessageDto extends GenericSendMessageDto {
27
29
 
28
30
  @Controller('communication')
29
31
  export class CommunicationController {
30
- constructor(private readonly communicationService: CommunicationService) {}
32
+ constructor(
33
+ private readonly communicationService: CommunicationService,
34
+ private readonly sendGridApiStrategy: SendGridApiStrategy,
35
+ ) {}
31
36
 
32
37
  @Post('send')
33
38
  @HttpCode(HttpStatus.OK)
@@ -141,4 +146,10 @@ export class CommunicationController {
141
146
  initDto.email,
142
147
  );
143
148
  }
149
+
150
+ @Post('sendgrid/verified-senders')
151
+ @HttpCode(HttpStatus.OK)
152
+ async getSendGridVerifiedSenders(@Body() dto: SendGridVerifiedSendersDto) {
153
+ return this.sendGridApiStrategy.getVerifiedSenders(dto.apiKey);
154
+ }
144
155
  }
@@ -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
+ }
@@ -1075,6 +1075,7 @@ export class CommunicationService {
1075
1075
  'https://www.googleapis.com/auth/gmail.send',
1076
1076
  'https://www.googleapis.com/auth/gmail.readonly',
1077
1077
  'https://www.googleapis.com/auth/userinfo.email',
1078
+ 'https://www.googleapis.com/auth/calendar',
1078
1079
  ];
1079
1080
 
1080
1081
  const authUrl = oauth2Client.generateAuthUrl({
@@ -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
  }
@@ -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
  }
@@ -62,8 +62,8 @@ export class OtpController {
62
62
  @Body()
63
63
  body: {
64
64
  to: string;
65
- cc: string;
66
- bcc: string;
65
+ cc?: string;
66
+ bcc?: string;
67
67
  subject: string;
68
68
  message: string;
69
69
  templateCode: string;
@@ -66,8 +66,8 @@ export class EmailService {
66
66
  email: string,
67
67
  subject: string,
68
68
  templateCode: string,
69
- cc: string[] = [],
70
- bcc: string[] = [],
69
+ cc?: string[],
70
+ bcc?: string[],
71
71
  message?: string,
72
72
  context?: any,
73
73
  icsPayload?: any,
@@ -104,15 +104,23 @@ export class EmailService {
104
104
  }
105
105
  }
106
106
 
107
- // Send the email
108
- await this.mailerService.sendMail({
107
+ const mailOptions: any = {
109
108
  to: email,
110
- cc: cc.length > 0 ? cc : undefined,
111
- bcc: bcc.length > 0 ? bcc : undefined,
112
109
  subject,
113
110
  html: htmlContent,
114
111
  attachments,
115
- });
112
+ };
113
+
114
+ if (cc && cc.length > 0) {
115
+ mailOptions.cc = cc;
116
+ }
117
+
118
+ if (bcc && bcc.length > 0) {
119
+ mailOptions.bcc = bcc;
120
+ }
121
+
122
+ // Send the email
123
+ await this.mailerService.sendMail(mailOptions);
116
124
 
117
125
  return { success: true, message: 'Email sent successfully' };
118
126
  }
@@ -37,11 +37,24 @@ export class NotificationsService {
37
37
  return this.sendToDevice(token, title, body);
38
38
  }
39
39
 
40
- async getAllNotifications(loggedInUser: any) {
40
+ async getAllNotifications(
41
+ loggedInUser: any,
42
+ filterQuery?: {
43
+ is_read?: string;
44
+ },
45
+ ) {
46
+ // if no filterQuery provided, return all notifications
41
47
  const { id, level_id, level_type } = loggedInUser;
42
48
 
43
- const notifications = await this.dataSource.query(
44
- `
49
+ // Convert query param ("true"/"false") → SQL-friendly value (1/0)
50
+ let isReadFilter: number | undefined = undefined;
51
+ if (filterQuery && filterQuery.is_read !== undefined) {
52
+ const isReadBool = String(filterQuery.is_read).toLowerCase() === 'true';
53
+ isReadFilter = isReadBool ? 1 : 0;
54
+ }
55
+
56
+ // Build query dynamically
57
+ let query = `
45
58
  SELECT
46
59
  n.*,
47
60
  u.name AS user_name,
@@ -49,15 +62,24 @@ export class NotificationsService {
49
62
  FROM cr_notification n
50
63
  LEFT JOIN eth_user_profile u ON n.user_id = u.parent_id
51
64
  WHERE n.user_id = ? AND n.level_id = ? AND n.level_type = ?
52
- ORDER BY n.created_date DESC
53
- `,
54
- [id, level_id, 'SCH'],
55
- );
65
+ `;
66
+
67
+ const params: any[] = [id, level_id, level_type];
68
+
69
+ if (isReadFilter !== undefined) {
70
+ query += ` AND n.is_read = ?`;
71
+ params.push(isReadFilter);
72
+ }
73
+
74
+ query += ` ORDER BY n.created_date DESC`;
75
+
76
+ const notifications = await this.dataSource.query(query, params);
56
77
 
57
78
  // Profile media enrichment
58
79
  const mediaCache = new Map();
59
80
  for (const notification of notifications) {
60
- notification.is_read = true;
81
+ // Ensure is_read is returned as boolean
82
+ notification.is_read = !!notification.is_read;
61
83
 
62
84
  if (notification.user_profile) {
63
85
  if (!mediaCache.has(notification.user_profile)) {
@@ -73,18 +95,21 @@ export class NotificationsService {
73
95
  }
74
96
  }
75
97
 
76
- // Batch update all fetched notifications
77
- if (notifications.length > 0) {
78
- await this.dataSource.query(
79
- `
80
- UPDATE cr_notification
81
- SET is_read = 1
82
- WHERE user_id = ? AND level_id = ? AND level_type = ?
83
- `,
84
- [id, level_id, level_type],
85
- );
86
- }
87
-
88
98
  return notifications;
89
99
  }
100
+
101
+ async markAllAsRead(loggedInUser: any) {
102
+ const { id, level_id, level_type } = loggedInUser;
103
+
104
+ const query = `
105
+ UPDATE cr_notification
106
+ SET is_read = 1
107
+ WHERE user_id = ? AND level_id = ? AND level_type = ? AND is_read = 0
108
+ `;
109
+
110
+ const params = [id, level_id, level_type];
111
+
112
+ const result = await this.dataSource.query(query, params);
113
+ return { success: true, affectedRows: result.affectedRows || 0 };
114
+ }
90
115
  }