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.
- package/dist/module/communication/controller/communication.controller.d.ts +12 -2
- package/dist/module/communication/controller/communication.controller.js +16 -2
- 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.js +1 -0
- 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/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/controller/otp.controller.d.ts +2 -2
- package/dist/module/notification/service/email.service.js +10 -5
- package/dist/module/notification/service/email.service.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/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/module/communication/controller/communication.controller.ts +12 -1
- package/src/module/communication/dto/create-config.dto.ts +26 -0
- package/src/module/communication/service/communication.service.ts +1 -0
- package/src/module/communication/strategies/email/sendgrid-api.strategy.ts +65 -0
- 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/controller/otp.controller.ts +2 -2
- package/src/module/notification/service/email.service.ts +15 -7
- package/src/module/notification/service/notification.service.ts +45 -20
package/package.json
CHANGED
|
@@ -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(
|
|
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
|
|
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
|
}
|
|
@@ -66,8 +66,8 @@ export class EmailService {
|
|
|
66
66
|
email: string,
|
|
67
67
|
subject: string,
|
|
68
68
|
templateCode: string,
|
|
69
|
-
cc
|
|
70
|
-
bcc
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
}
|