rez_core 2.2.214 → 2.2.216

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 (22) hide show
  1. package/dist/module/communication/controller/calender-event.controller.d.ts +2 -26
  2. package/dist/module/communication/controller/wrapper.controller.d.ts +1 -17
  3. package/dist/module/communication/service/calendar-event.service.d.ts +2 -26
  4. package/dist/module/communication/service/calendar-event.service.js +67 -81
  5. package/dist/module/communication/service/calendar-event.service.js.map +1 -1
  6. package/dist/module/communication/service/communication.service.js +40 -41
  7. package/dist/module/communication/service/communication.service.js.map +1 -1
  8. package/dist/module/communication/service/wrapper.service.d.ts +1 -17
  9. package/dist/module/communication/service/wrapper.service.js +72 -46
  10. package/dist/module/communication/service/wrapper.service.js.map +1 -1
  11. package/dist/module/communication/strategies/email/sendgrid-api.strategy.d.ts +1 -1
  12. package/dist/module/communication/strategies/email/sendgrid-api.strategy.js.map +1 -1
  13. package/dist/module/user/controller/login.controller.js +8 -2
  14. package/dist/module/user/controller/login.controller.js.map +1 -1
  15. package/dist/tsconfig.build.tsbuildinfo +1 -1
  16. package/package.json +1 -1
  17. package/src/module/communication/service/calendar-event.service.ts +82 -87
  18. package/src/module/communication/service/communication.service.ts +4 -3
  19. package/src/module/communication/service/wrapper.service.ts +117 -71
  20. package/src/module/communication/strategies/email/sendgrid-api.strategy.ts +1 -2
  21. package/src/module/user/controller/login.controller.ts +9 -3
  22. package/src/resources/dev.properties.yaml +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "2.2.214",
3
+ "version": "2.2.216",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -12,112 +12,107 @@ export class GoogleService {
12
12
  /**
13
13
  * Create a Google Calendar event
14
14
  */
15
- async createEvent(googleCred: any, payload: any) {
16
- if (!googleCred?.refreshToken) {
15
+ async createEvent(creds: any, payload: any) {
16
+ if (!creds?.refreshToken) {
17
17
  throw new Error('Missing refresh token');
18
18
  }
19
19
 
20
- try {
21
- const accessToken =
22
- await this.gmailApiStrategy.refreshAccessToken(googleCred);
20
+ const accessToken = await this.gmailApiStrategy.refreshAccessToken(creds);
23
21
 
24
- const event = {
25
- summary: payload.title,
26
- description: payload.description,
27
- location: payload.location,
28
- start: {
29
- dateTime: payload.startTime,
30
- timeZone: payload.timeZone || 'Asia/Kolkata',
31
- },
32
- end: {
33
- dateTime: payload.endTime,
34
- timeZone: payload.timeZone || 'Asia/Kolkata',
35
- },
36
- attendees: payload.attendees?.map((email) => ({ email })) || [],
37
- };
22
+ // Normalize attendees into [{ email: "..." }]
23
+ const normalizeAttendees = (attendees: any[] = []) =>
24
+ attendees
25
+ .flatMap((a) =>
26
+ typeof a === 'string'
27
+ ? [{ email: a }]
28
+ : Array.isArray(a?.email)
29
+ ? a.email.map((e) => ({ email: e }))
30
+ : a?.email
31
+ ? [{ email: a.email }]
32
+ : [],
33
+ )
34
+ .filter((a) => a.email && a.email.trim() !== '');
38
35
 
39
- const response = await axios.post(
40
- 'https://www.googleapis.com/calendar/v3/calendars/primary/events?sendUpdates=all',
41
- event,
42
- {
43
- headers: {
44
- Authorization: `Bearer ${accessToken}`,
45
- 'Content-Type': 'application/json',
46
- },
36
+ const event = {
37
+ summary: payload.title,
38
+ description: payload.description,
39
+ location: payload.location,
40
+ start: {
41
+ dateTime: payload.startTime,
42
+ timeZone: payload.timeZone || 'Asia/Kolkata',
43
+ },
44
+ end: {
45
+ dateTime: payload.endTime,
46
+ timeZone: payload.timeZone || 'Asia/Kolkata',
47
+ },
48
+ attendees: normalizeAttendees(payload.attendees),
49
+ };
50
+
51
+ const response = await axios.post(
52
+ 'https://www.googleapis.com/calendar/v3/calendars/primary/events?sendUpdates=all',
53
+ event,
54
+ {
55
+ headers: {
56
+ Authorization: `Bearer ${accessToken}`,
57
+ 'Content-Type': 'application/json',
47
58
  },
48
- );
59
+ },
60
+ );
49
61
 
50
- this.logger.log(`Event created: ${response.data.id}`);
51
- return {
52
- success: true,
53
- eventId: response.data.id,
54
- htmlLink: response.data.htmlLink,
55
- accessToken,
56
- };
57
- } catch (error: any) {
58
- this.logger.error('CreateEvent error', error.message);
59
- return {
60
- success: false,
61
- error: error.response?.data?.error?.message || error.message,
62
- };
63
- }
62
+ this.logger.debug('Google calendar event created', response.data);
63
+
64
+ return response.data;
64
65
  }
65
66
 
66
67
  /**
67
68
  * Update a Google Calendar event
68
69
  */
69
- async updateEvent(googleCred: any, eventId: string, payload: any) {
70
- if (!googleCred?.refreshToken) {
70
+ async updateEvent(creds: any, eventId: string, payload: any) {
71
+ if (!creds?.refreshToken) {
71
72
  throw new Error('Missing refresh token');
72
73
  }
73
74
 
74
- try {
75
- const accessToken =
76
- await this.gmailApiStrategy.refreshAccessToken(googleCred);
75
+ const accessToken = await this.gmailApiStrategy.refreshAccessToken(creds);
77
76
 
78
- const event = {
79
- summary: payload.title,
80
- description: payload.description,
81
- location: payload.location,
82
- start: {
83
- dateTime: payload.startTime,
84
- timeZone: payload.timeZone || 'Asia/Kolkata',
85
- },
86
- end: {
87
- dateTime: payload.endTime,
88
- timeZone: payload.timeZone || 'Asia/Kolkata',
89
- },
90
- attendees: payload.attendees?.map((email) => ({ email })) || [],
91
- };
77
+ const normalizeAttendees = (attendees: any[] = []) =>
78
+ attendees
79
+ .flatMap((a) =>
80
+ typeof a === 'string'
81
+ ? [{ email: a }]
82
+ : Array.isArray(a?.email)
83
+ ? a.email.map((e) => ({ email: e }))
84
+ : a?.email
85
+ ? [{ email: a.email }]
86
+ : [],
87
+ )
88
+ .filter((a) => a.email && a.email.trim() !== '');
92
89
 
93
- if (payload.meetingType === 'online' && payload.meetingLink) {
94
- event.description = `${event.description || ''}\nJoin here: ${payload.meetingLink}`;
95
- }
90
+ const event = {
91
+ summary: payload.title,
92
+ description: payload.description,
93
+ location: payload.location,
94
+ start: {
95
+ dateTime: payload.startTime,
96
+ timeZone: payload.timeZone || 'Asia/Kolkata',
97
+ },
98
+ end: {
99
+ dateTime: payload.endTime,
100
+ timeZone: payload.timeZone || 'Asia/Kolkata',
101
+ },
102
+ attendees: normalizeAttendees(payload.attendees),
103
+ };
96
104
 
97
- const response = await axios.patch(
98
- `https://www.googleapis.com/calendar/v3/calendars/primary/events/${eventId}?sendUpdates=all`,
99
- event,
100
- {
101
- headers: {
102
- Authorization: `Bearer ${accessToken}`,
103
- 'Content-Type': 'application/json',
104
- },
105
+ const response = await axios.patch(
106
+ `https://www.googleapis.com/calendar/v3/calendars/primary/events/${eventId}?sendUpdates=all`,
107
+ event,
108
+ {
109
+ headers: {
110
+ Authorization: `Bearer ${accessToken}`,
111
+ 'Content-Type': 'application/json',
105
112
  },
106
- );
113
+ },
114
+ );
107
115
 
108
- this.logger.log(`Event updated: ${response.data.id}`);
109
- return {
110
- success: true,
111
- eventId: response.data.id,
112
- htmlLink: response.data.htmlLink,
113
- accessToken,
114
- };
115
- } catch (error: any) {
116
- this.logger.error('UpdateEvent error', error.message);
117
- return {
118
- success: false,
119
- error: error.response?.data?.error?.message || error.message,
120
- };
121
- }
116
+ return response.data;
122
117
  }
123
118
  }
@@ -593,9 +593,9 @@ export class CommunicationService {
593
593
  hasCredentials: !!(configJson?.user && configJson?.password),
594
594
  };
595
595
 
596
- default:
596
+ default: {
597
597
  // Generic details - return safe subset of config
598
- { const safeConfig: any = {};
598
+ const safeConfig: any = {};
599
599
 
600
600
  // Include non-sensitive fields
601
601
  const safeFields = [
@@ -641,7 +641,8 @@ export class CommunicationService {
641
641
  }
642
642
  });
643
643
 
644
- return safeConfig; }
644
+ return safeConfig;
645
+ }
645
646
  }
646
647
  } catch (error) {
647
648
  return { error: 'Unable to extract configuration details' };
@@ -38,6 +38,7 @@ export class WrapperService {
38
38
  FROM cr_communication_hub
39
39
  WHERE level_id = ?
40
40
  AND level_type = ?
41
+ AND status = 1
41
42
  AND communication_config_type = 'EMAIL'`,
42
43
  [level_id, level_type],
43
44
  );
@@ -61,23 +62,8 @@ export class WrapperService {
61
62
  templateCode = template[0]?.rich_text;
62
63
  } else {
63
64
  this.logger.warn(
64
- `Template not found at user level. Falling back to ORG-level template`,
65
+ `No template found for templateCode=${payload.templateCode} and current level`,
65
66
  );
66
- const fallbackTemplate = await this.datasource.query(
67
- `SELECT code FROM cr_wf_comm_template WHERE code = ? AND level_id = ? AND level_type = 'ORG' LIMIT 1`,
68
- [payload.templateCode, organization_id],
69
- );
70
- this.logger.debug(
71
- `Fallback template lookup: ${JSON.stringify(fallbackTemplate)}`,
72
- );
73
-
74
- if (!fallbackTemplate || fallbackTemplate.length === 0) {
75
- throw new Error(
76
- `Template with code ${payload.templateCode} not found at user or org level`,
77
- );
78
- }
79
-
80
- templateCode = fallbackTemplate[0].code;
81
67
  }
82
68
  }
83
69
 
@@ -95,8 +81,8 @@ export class WrapperService {
95
81
  cc: payload.cc,
96
82
  bcc: payload.bcc,
97
83
  html: payload.html,
98
- attachments: payload.attachments,
99
- templateId: templateCode,
84
+ // attachments: payload.attachments,
85
+ // templateId: templateCode,
100
86
  variables: payload.variables,
101
87
  };
102
88
  } else {
@@ -114,7 +100,7 @@ export class WrapperService {
114
100
  );
115
101
 
116
102
  if (!fallbackConfigs || fallbackConfigs.length === 0) {
117
- throw new Error('No active email communication config found');
103
+ this.logger.warn('No active email communication config found');
118
104
  }
119
105
 
120
106
  payloadSendMail = {
@@ -126,9 +112,9 @@ export class WrapperService {
126
112
  type: 'EMAIL',
127
113
  cc: payload.cc,
128
114
  bcc: payload.bcc,
129
- html: payload.message,
130
- attachments: payload.attachments,
131
- templateId: payload.templateId,
115
+ html: payload.html,
116
+ // attachments: payload.attachments,
117
+ // templateId: payload.templateId,
132
118
  variables: payload.variables,
133
119
  };
134
120
  }
@@ -160,103 +146,146 @@ export class WrapperService {
160
146
  /**
161
147
  * Wrapper for scheduling meeting
162
148
  */
149
+
163
150
  async scheduleMeetingWrapper(payload: any, loggedInUser: any) {
164
151
  try {
165
152
  this.logger.log(
166
153
  `scheduleMeetingWrapper called by user=${loggedInUser?.id || 'unknown'} Payload=${JSON.stringify(payload)}`,
167
154
  );
168
155
 
169
- const { level_id, level_type } = loggedInUser;
156
+ const { level_id, level_type, organization_id } = loggedInUser;
170
157
 
171
- // 1. Try user-level config
172
- this.logger.debug(
173
- `Fetching user-level config for scheduling. level_id=${level_id}, level_type=${level_type}`,
174
- );
158
+ // Normalize emails (to, cc, bcc → attendees)
159
+ const cleanEmails = (arr: string[] = []) =>
160
+ arr.filter((e) => e && e.trim() !== '').map((e) => e.trim());
161
+
162
+ const toList = Array.isArray(payload.to) ? cleanEmails(payload.to) : [];
163
+ const ccList = Array.isArray(payload.cc) ? cleanEmails(payload.cc) : [];
164
+ const bccList = Array.isArray(payload.bcc)
165
+ ? cleanEmails(payload.bcc)
166
+ : [];
167
+
168
+ payload.attendees = [
169
+ ...toList.map((e) => ({ email: e })),
170
+ ...ccList.map((e) => ({ email: e })),
171
+ ...bccList.map((e) => ({ email: e })),
172
+ ...(payload.attendees || []).flatMap((a) =>
173
+ typeof a === 'string'
174
+ ? [{ email: a }]
175
+ : Array.isArray(a?.email)
176
+ ? a.email.map((e) => ({ email: e }))
177
+ : a?.email
178
+ ? [{ email: a.email }]
179
+ : [],
180
+ ),
181
+ ];
182
+
183
+ // ---- Step 1: Check user-level configs ----
175
184
  const userConfigs = await this.datasource.query(
176
185
  `SELECT *
177
- FROM cr_communication_hub
178
- WHERE level_id = ?
179
- AND level_type = ?
180
- AND communication_config_type = 'EMAIL'
181
- AND service = 'API'`,
186
+ FROM cr_communication_hub
187
+ WHERE level_id = ?
188
+ AND level_type = ?
189
+ AND status = 1
190
+ AND communication_config_type = 'EMAIL'
191
+ AND service = 'API'`,
182
192
  [level_id, level_type],
183
193
  );
194
+
184
195
  this.logger.debug(`User configs found: ${JSON.stringify(userConfigs)}`);
185
196
 
186
197
  if (userConfigs && userConfigs.length > 0) {
187
198
  const provider = userConfigs[0]?.provider;
188
199
  const configId = userConfigs[0]?.config_id;
189
- this.logger.debug(
190
- `User config provider=${provider}, configId=${configId}`,
191
- );
192
200
 
193
201
  if (provider === 'gmail') {
194
- this.logger.log(`Using Google Calendar for scheduling`);
202
+ this.logger.log(`Using Google Calendar (user-level)`);
195
203
  const creds = await this.getConfigCred(configId);
196
- this.logger.debug(`Google creds: ${JSON.stringify(creds)}`);
204
+
197
205
  if (creds) {
206
+ if (payload.eventId) {
207
+ const result = await this.googleService.updateEvent(
208
+ creds,
209
+ payload.eventId,
210
+ payload,
211
+ );
212
+ return { success: true, data: result };
213
+ }
214
+
198
215
  const result = await this.googleService.createEvent(creds, payload);
199
216
  this.logger.log(
200
- `Google Calendar createEvent SUCCESS: ${JSON.stringify(result)}`,
217
+ `scheduleMeetingWrapper SUCCESS (user-level). Result: ${JSON.stringify(result)}`,
201
218
  );
219
+
202
220
  return { success: true, data: result };
203
221
  }
204
222
  }
205
223
 
206
- this.logger.log(`Non-Gmail provider detected. Falling back to ICS`);
207
- const result = await this.sendIcsFallback(
208
- payload,
209
- level_id,
210
- level_type,
211
- );
212
- return { success: true, data: result };
224
+ // Non-Gmail user-level fallback
225
+ return {
226
+ success: true,
227
+ data: await this.sendIcsFallback(payload, level_id, level_type),
228
+ };
213
229
  }
214
230
 
215
- // 2. Fallback to ORG-level config
216
- this.logger.warn(
217
- `No user-level config found. Checking ORG-level configs...`,
218
- );
231
+ // ---- Step 2: Check ORG-level configs ----
232
+ this.logger.log(`No user-level config found, checking ORG-level...`);
233
+
219
234
  const orgConfigs = await this.datasource.query(
220
235
  `SELECT *
221
- FROM cr_communication_hub
222
- WHERE level_type = 'ORG'
223
- AND level_id = 1
224
- AND communication_config_type = 'EMAIL'
225
- AND service = 'API'`,
236
+ FROM cr_communication_hub
237
+ WHERE level_id = 1
238
+ AND level_type = 'ORG'
239
+ AND communication_config_type = 'EMAIL'
240
+ AND service = 'API'`,
241
+ [],
226
242
  );
243
+
227
244
  this.logger.debug(`ORG configs found: ${JSON.stringify(orgConfigs)}`);
228
245
 
229
246
  if (orgConfigs && orgConfigs.length > 0) {
230
- const provider = orgConfigs[0].provider;
231
- const configId = orgConfigs[0].config_id;
232
- this.logger.debug(
233
- `ORG config provider=${provider}, configId=${configId}`,
234
- );
247
+ const provider = orgConfigs[0]?.provider;
248
+ const configId = orgConfigs[0]?.config_id;
235
249
 
236
250
  if (provider === 'gmail') {
237
- this.logger.log(`Using ORG Google Calendar for scheduling`);
251
+ this.logger.log(`Using Google Calendar (ORG-level)`);
238
252
  const creds = await this.getConfigCred(configId);
239
- this.logger.debug(`ORG Google creds: ${JSON.stringify(creds)}`);
253
+
240
254
  if (creds) {
255
+ if (payload.eventId) {
256
+ const result = await this.googleService.updateEvent(
257
+ creds,
258
+ payload.eventId,
259
+ payload,
260
+ );
261
+ return { success: true, data: result };
262
+ }
263
+
241
264
  const result = await this.googleService.createEvent(creds, payload);
242
265
  this.logger.log(
243
- `ORG Google Calendar createEvent SUCCESS: ${JSON.stringify(result)}`,
266
+ `scheduleMeetingWrapper SUCCESS (ORG-level). Result: ${JSON.stringify(result)}`,
244
267
  );
268
+
245
269
  return { success: true, data: result };
246
270
  }
247
271
  }
248
272
 
249
- this.logger.log(`ORG Non-Gmail provider. Falling back to ICS`);
250
- const result = await this.sendIcsFallback(payload, 1, 'ORG');
251
- return { success: true, data: result };
273
+ // Non-Gmail ORG-level fallback
274
+ return {
275
+ success: true,
276
+ data: await this.sendIcsFallback(payload, 1, 'ORG'),
277
+ };
252
278
  }
253
279
 
254
- // 3. Absolute fallback
255
- this.logger.error(
256
- `No configs found anywhere. Defaulting to ORG ICS fallback`,
280
+ // ---- Step 3: Absolute fallback (no configs at all) ----
281
+ this.logger.warn(
282
+ `No user-level or ORG-level configs found, sending fallback ICS email.`,
257
283
  );
258
- const result = await this.sendIcsFallback(payload, 1, 'ORG');
259
- return { success: true, data: result };
284
+
285
+ return {
286
+ success: true,
287
+ data: await this.sendIcsFallback(payload, level_id, level_type),
288
+ };
260
289
  } catch (error: any) {
261
290
  this.logger.error(
262
291
  `scheduleMeetingWrapper ERROR: ${error.message}`,
@@ -292,13 +321,30 @@ export class WrapperService {
292
321
  this.logger.log(
293
322
  `Generating ICS file for fallback. Payload: ${JSON.stringify(payload)}`,
294
323
  );
324
+
295
325
  const base64String = await this.icsService.generateIcs(payload);
296
326
  this.logger.debug(`ICS generated (base64 length: ${base64String?.length})`);
297
327
 
328
+ // Normalize attendees
329
+ const attendeeEmails =
330
+ payload.attendees?.flatMap((a) =>
331
+ Array.isArray(a.email) ? a.email : [a.email],
332
+ ) || [];
333
+
334
+ // Combine into one list (to + attendees)
335
+ const toList = [
336
+ ...(Array.isArray(payload.to)
337
+ ? payload.to
338
+ : payload.to
339
+ ? [payload.to]
340
+ : []),
341
+ ...attendeeEmails,
342
+ ];
343
+
298
344
  const payloadSendMail: GenericMessageDto = {
299
345
  levelId,
300
346
  levelType,
301
- to: payload.to,
347
+ to: toList,
302
348
  message: payload.message,
303
349
  subject: payload.subject,
304
350
  type: 'EMAIL',
@@ -2,10 +2,9 @@ import { Injectable } from '@nestjs/common';
2
2
  import * as sgMail from '@sendgrid/mail';
3
3
  import axios from 'axios';
4
4
  import {
5
- CommunicationStrategy,
6
5
  CommunicationResult,
6
+ CommunicationStrategy,
7
7
  EmailAttachment,
8
- EmailAttachmentValidator,
9
8
  } from '../communication.strategy';
10
9
 
11
10
  @Injectable()
@@ -72,7 +72,6 @@ export class LoginController {
72
72
  // Passport automatically redirects to Google
73
73
  }
74
74
 
75
- // 2️⃣ Handles Google callback, validates user, creates session, and returns JWT
76
75
  @Get('google/callback')
77
76
  @UseGuards(GoogleAuthGuard)
78
77
  async googleAuthRedirect(@Req() req: any, @Res() res: Response) {
@@ -155,11 +154,18 @@ export class LoginController {
155
154
  `${this.configService.get('BASE_URL')}/auth?email=${email}&error='User not found'`,
156
155
  );
157
156
 
157
+ const profile = this.configService.get('PROFILE');
158
158
  const { accessToken, appcode } = data;
159
- if (data.slug) {
159
+ if (profile && profile === 'dev') {
160
160
  return res.redirect(
161
- `https://${data.slug}.${this.configService.get('DOMAIN_URL')}/auth?token=${accessToken}&appcode=${appcode}`,
161
+ `${this.configService.get('FE_URL')}/auth?token=${accessToken}&appcode=${appcode}`,
162
162
  );
163
+ } else {
164
+ if (data.slug) {
165
+ return res.redirect(
166
+ `https://${data.slug}.${this.configService.get('DOMAIN_URL')}/auth?token=${accessToken}&appcode=${appcode}`,
167
+ );
168
+ }
163
169
  }
164
170
 
165
171
  return res.redirect(
@@ -1,4 +1,5 @@
1
1
  DB_HOST: '13.234.25.234'
2
+ PROFILE: 'dev'
2
3
  # DB_HOST: 'localhost'
3
4
  DB_PORT: '3306'
4
5
  DB_USER: 'root'
@@ -10,6 +11,7 @@ SECRET_KEY: '1hard_to_guess_secret7890a'
10
11
  CLIENT_ID: '819281384645-2tnuvm80sul1n2ahqa4eg6kjd19pnbu9.apps.googleusercontent.com'
11
12
  CLIENT_SECRET: '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'
12
13
  CALLBACK_URL: 'http://localhost:5001/auth/google/callback'
14
+ FE_URL: 'http://localhost:3000'
13
15
  BASE_URL: 'http://localhost:5001'
14
16
  DOMAIN_URL: 'rezolut.in'
15
17
  OTP_EXPIRY: '10'