rez_core 2.2.263 → 2.3.2
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/enterprise/repository/school.repository.js +0 -1
- package/dist/module/enterprise/repository/school.repository.js.map +1 -1
- package/dist/module/filter/repository/saved-filter.repository.js +0 -1
- package/dist/module/filter/repository/saved-filter.repository.js.map +1 -1
- package/dist/module/integration/controller/integration.controller.d.ts +9 -0
- package/dist/module/integration/controller/integration.controller.js +34 -0
- package/dist/module/integration/controller/integration.controller.js.map +1 -1
- package/dist/module/integration/factories/whatsapp.factory.d.ts +5 -1
- package/dist/module/integration/factories/whatsapp.factory.js +14 -2
- package/dist/module/integration/factories/whatsapp.factory.js.map +1 -1
- package/dist/module/integration/integration.module.js +4 -0
- package/dist/module/integration/integration.module.js.map +1 -1
- package/dist/module/integration/service/integration.service.d.ts +1 -0
- package/dist/module/integration/service/integration.service.js +14 -0
- package/dist/module/integration/service/integration.service.js.map +1 -1
- package/dist/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.d.ts +19 -0
- package/dist/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.js +257 -0
- package/dist/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.js.map +1 -0
- package/dist/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.d.ts +22 -0
- package/dist/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.js +295 -0
- package/dist/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.js.map +1 -0
- package/dist/module/layout_preference/service/layout_preference.service.js +0 -2
- package/dist/module/layout_preference/service/layout_preference.service.js.map +1 -1
- package/dist/module/listmaster/service/list-master.service.js +0 -1
- package/dist/module/listmaster/service/list-master.service.js.map +1 -1
- package/dist/module/master/service/master.service.js +0 -2
- package/dist/module/master/service/master.service.js.map +1 -1
- package/dist/module/meta/repository/entity-table.repository.js +0 -1
- package/dist/module/meta/repository/entity-table.repository.js.map +1 -1
- package/dist/module/meta/service/entity-dynamic.service.js +0 -1
- package/dist/module/meta/service/entity-dynamic.service.js.map +1 -1
- package/dist/module/meta/service/entity-service-impl.service.js +0 -2
- package/dist/module/meta/service/entity-service-impl.service.js.map +1 -1
- package/dist/module/meta/service/entity-table.service.js +0 -2
- package/dist/module/meta/service/entity-table.service.js.map +1 -1
- package/dist/module/meta/service/field-group.service.js +0 -1
- package/dist/module/meta/service/field-group.service.js.map +1 -1
- package/dist/module/module/service/module-access.service.js +0 -6
- package/dist/module/module/service/module-access.service.js.map +1 -1
- package/dist/module/notification/controller/notification.controller.d.ts +1 -0
- package/dist/module/notification/controller/notification.controller.js +1 -1
- package/dist/module/notification/controller/notification.controller.js.map +1 -1
- package/dist/module/notification/service/email.service.js +0 -1
- package/dist/module/notification/service/email.service.js.map +1 -1
- package/dist/module/notification/service/notification.service.d.ts +1 -1
- package/dist/module/notification/service/notification.service.js +11 -2
- package/dist/module/notification/service/notification.service.js.map +1 -1
- package/dist/module/user/service/login.service.js +0 -1
- package/dist/module/user/service/login.service.js.map +1 -1
- package/dist/module/workflow/repository/stage.repository.js +0 -1
- package/dist/module/workflow/repository/stage.repository.js.map +1 -1
- package/dist/module/workflow/service/action.service.js +0 -1
- package/dist/module/workflow/service/action.service.js.map +1 -1
- package/dist/module/workflow/service/comm-template.service.js +0 -1
- package/dist/module/workflow/service/comm-template.service.js.map +1 -1
- package/dist/module/workflow/service/populate-workflow.service.js +0 -5
- package/dist/module/workflow/service/populate-workflow.service.js.map +1 -1
- package/dist/module/workflow/service/task.service.js +0 -1
- package/dist/module/workflow/service/task.service.js.map +1 -1
- package/dist/module/workflow/service/workflow-meta.service.js +0 -2
- package/dist/module/workflow/service/workflow-meta.service.js.map +1 -1
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.js +0 -1
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/modules/integration.md +55 -1
- package/package.json +1 -1
- package/src/module/enterprise/repository/school.repository.ts +0 -1
- package/src/module/filter/repository/saved-filter.repository.ts +0 -1
- package/src/module/integration/controller/integration.controller.ts +37 -0
- package/src/module/integration/factories/whatsapp.factory.ts +10 -0
- package/src/module/integration/integration.module.ts +4 -0
- package/src/module/integration/service/integration.service.ts +21 -0
- package/src/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.ts +360 -0
- package/src/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.ts +419 -0
- package/src/module/layout_preference/service/layout_preference.service.ts +0 -2
- package/src/module/listmaster/service/list-master.service.ts +1 -10
- package/src/module/master/service/master.service.ts +0 -2
- package/src/module/meta/repository/entity-table.repository.ts +0 -1
- package/src/module/meta/service/entity-dynamic.service.ts +1 -5
- package/src/module/meta/service/entity-service-impl.service.ts +0 -4
- package/src/module/meta/service/entity-table.service.ts +0 -2
- package/src/module/meta/service/field-group.service.ts +0 -1
- package/src/module/module/service/module-access.service.ts +0 -13
- package/src/module/notification/controller/notification.controller.ts +8 -1
- package/src/module/notification/service/email.service.ts +0 -1
- package/src/module/notification/service/notification.service.ts +19 -4
- package/src/module/user/service/login.service.ts +0 -1
- package/src/module/workflow/repository/stage.repository.ts +1 -2
- package/src/module/workflow/service/action.service.ts +0 -1
- package/src/module/workflow/service/comm-template.service.ts +0 -1
- package/src/module/workflow/service/populate-workflow.service.ts +2 -12
- package/src/module/workflow/service/task.service.ts +0 -1
- package/src/module/workflow/service/workflow-meta.service.ts +1 -4
- package/src/module/workflow-automation/service/workflow-automation-engine.service.ts +0 -1
- package/src/resources/dev.properties.yaml +1 -1
- package/.vscode/extensions.json +0 -5
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import axios, { AxiosResponse } from 'axios';
|
|
3
|
+
import {
|
|
4
|
+
IntegrationResult,
|
|
5
|
+
IntegrationStrategy,
|
|
6
|
+
} from '../integration.strategy';
|
|
7
|
+
|
|
8
|
+
interface TubelightWhatsAppConfig {
|
|
9
|
+
userName: string;
|
|
10
|
+
password: string;
|
|
11
|
+
tenantId: string;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
sourceNumber?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface TubelightMessage {
|
|
17
|
+
to: string;
|
|
18
|
+
message: string;
|
|
19
|
+
messageType?: string;
|
|
20
|
+
mediaUrl?: string;
|
|
21
|
+
caption?: string;
|
|
22
|
+
filename?: string;
|
|
23
|
+
templateId?: string;
|
|
24
|
+
templateParams?: any[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface TubelightApiResponse {
|
|
28
|
+
success: boolean;
|
|
29
|
+
messageId?: string;
|
|
30
|
+
id?: string;
|
|
31
|
+
status?: string;
|
|
32
|
+
message?: string;
|
|
33
|
+
error?: string;
|
|
34
|
+
details?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Injectable()
|
|
38
|
+
export class TubelightWhatsAppStrategy implements IntegrationStrategy {
|
|
39
|
+
private readonly logger = new Logger(TubelightWhatsAppStrategy.name);
|
|
40
|
+
private readonly defaultBaseUrl =
|
|
41
|
+
'https://portal.tubelightcommunications.com/whatsapp/api/v1';
|
|
42
|
+
|
|
43
|
+
async sendMessage(
|
|
44
|
+
to: string,
|
|
45
|
+
message: string,
|
|
46
|
+
config: any,
|
|
47
|
+
): Promise<IntegrationResult> {
|
|
48
|
+
try {
|
|
49
|
+
if (!this.validateConfig(config)) {
|
|
50
|
+
throw new Error('Invalid Tubelight WhatsApp configuration');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const tubelightConfig = config as TubelightWhatsAppConfig;
|
|
54
|
+
const token = await this.authenticate(tubelightConfig);
|
|
55
|
+
|
|
56
|
+
if (!token) {
|
|
57
|
+
throw new Error('Failed to authenticate with Tubelight');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const baseUrl = tubelightConfig.baseUrl || this.defaultBaseUrl;
|
|
61
|
+
const url = `${baseUrl}/messages/send`;
|
|
62
|
+
|
|
63
|
+
const payload = this.prepareMessagePayload(to, message, config);
|
|
64
|
+
|
|
65
|
+
const response: AxiosResponse<TubelightApiResponse> = await axios.post(
|
|
66
|
+
url,
|
|
67
|
+
payload,
|
|
68
|
+
{
|
|
69
|
+
headers: {
|
|
70
|
+
'X-TENANT-ID': tubelightConfig.tenantId,
|
|
71
|
+
'Content-Type': 'application/json',
|
|
72
|
+
Authorization: `Bearer ${token}`,
|
|
73
|
+
},
|
|
74
|
+
timeout: 30000,
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
response.data?.success &&
|
|
80
|
+
(response.data?.messageId || response.data?.id)
|
|
81
|
+
) {
|
|
82
|
+
const messageId = response.data.messageId || response.data.id;
|
|
83
|
+
this.logger.log(
|
|
84
|
+
`Tubelight WhatsApp message sent successfully to ${to}, messageId: ${messageId}`,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
messageId: messageId,
|
|
90
|
+
provider: 'tubelight',
|
|
91
|
+
service: 'API',
|
|
92
|
+
timestamp: new Date(),
|
|
93
|
+
};
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error(
|
|
96
|
+
response.data?.error ||
|
|
97
|
+
response.data?.message ||
|
|
98
|
+
'Invalid response from Tubelight API',
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.logger.error(
|
|
103
|
+
`Failed to send Tubelight WhatsApp message to ${to}`,
|
|
104
|
+
error.response?.data || error.message,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
provider: 'tubelight',
|
|
110
|
+
service: 'API',
|
|
111
|
+
error: this.extractErrorMessage(error),
|
|
112
|
+
timestamp: new Date(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private async authenticate(
|
|
118
|
+
config: TubelightWhatsAppConfig,
|
|
119
|
+
): Promise<string | null> {
|
|
120
|
+
try {
|
|
121
|
+
const baseUrl = config.baseUrl || this.defaultBaseUrl;
|
|
122
|
+
const authUrl = baseUrl.replace('/whatsapp/api/v1', '/auth/api/v1');
|
|
123
|
+
|
|
124
|
+
const response = await axios.post(
|
|
125
|
+
`${authUrl}/login`,
|
|
126
|
+
{
|
|
127
|
+
user_name: config.userName,
|
|
128
|
+
password: config.password,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
headers: {
|
|
132
|
+
'Content-Type': 'application/json',
|
|
133
|
+
'X-TENANT-ID': config.tenantId,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const data = response.data as {
|
|
139
|
+
bearer_token?: string;
|
|
140
|
+
access_token?: string;
|
|
141
|
+
token?: string;
|
|
142
|
+
success?: boolean;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
if (data.success !== false) {
|
|
146
|
+
return data?.bearer_token || data?.access_token || data?.token || null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return null;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
this.logger.error(
|
|
152
|
+
'Failed to authenticate with Tubelight:',
|
|
153
|
+
error.response?.data || error.message,
|
|
154
|
+
);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private prepareMessagePayload(to: string, message: string, config: any): any {
|
|
160
|
+
const basePayload = {
|
|
161
|
+
to: this.formatPhoneNumber(to),
|
|
162
|
+
from: config.sourceNumber || config.from,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Handle different message types based on config
|
|
166
|
+
if (config.messageType) {
|
|
167
|
+
switch (config.messageType) {
|
|
168
|
+
case 'template':
|
|
169
|
+
return {
|
|
170
|
+
...basePayload,
|
|
171
|
+
messageType: 'TEMPLATE',
|
|
172
|
+
templateId: config.templateId,
|
|
173
|
+
templateParams: config.templateParams || [],
|
|
174
|
+
language: config.templateLanguage || 'en',
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
case 'image':
|
|
178
|
+
return {
|
|
179
|
+
...basePayload,
|
|
180
|
+
messageType: 'IMAGE',
|
|
181
|
+
mediaUrl: config.mediaUrl,
|
|
182
|
+
caption: message || config.caption || '',
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
case 'document':
|
|
186
|
+
return {
|
|
187
|
+
...basePayload,
|
|
188
|
+
messageType: 'DOCUMENT',
|
|
189
|
+
mediaUrl: config.mediaUrl,
|
|
190
|
+
filename: config.filename || 'document',
|
|
191
|
+
caption: message || config.caption || '',
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
case 'audio':
|
|
195
|
+
return {
|
|
196
|
+
...basePayload,
|
|
197
|
+
messageType: 'AUDIO',
|
|
198
|
+
mediaUrl: config.mediaUrl,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
case 'video':
|
|
202
|
+
return {
|
|
203
|
+
...basePayload,
|
|
204
|
+
messageType: 'VIDEO',
|
|
205
|
+
mediaUrl: config.mediaUrl,
|
|
206
|
+
caption: message || config.caption || '',
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
case 'location':
|
|
210
|
+
return {
|
|
211
|
+
...basePayload,
|
|
212
|
+
messageType: 'LOCATION',
|
|
213
|
+
latitude: config.latitude,
|
|
214
|
+
longitude: config.longitude,
|
|
215
|
+
name: config.locationName || '',
|
|
216
|
+
address: config.locationAddress || '',
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
case 'interactive':
|
|
220
|
+
return {
|
|
221
|
+
...basePayload,
|
|
222
|
+
messageType: 'INTERACTIVE',
|
|
223
|
+
interactiveType: config.interactiveType || 'button',
|
|
224
|
+
text: message,
|
|
225
|
+
buttons: config.buttons || [],
|
|
226
|
+
sections: config.sections || [],
|
|
227
|
+
header: config.header,
|
|
228
|
+
footer: config.footer,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
default:
|
|
232
|
+
// Fall back to text message
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Default text message
|
|
238
|
+
return {
|
|
239
|
+
...basePayload,
|
|
240
|
+
messageType: 'TEXT',
|
|
241
|
+
text: message,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private formatPhoneNumber(phoneNumber: string): string {
|
|
246
|
+
// Remove any non-digit characters except +
|
|
247
|
+
let formatted = phoneNumber.replace(/[^\d+]/g, '');
|
|
248
|
+
|
|
249
|
+
// If it doesn't start with +, assume it needs country code
|
|
250
|
+
if (!formatted.startsWith('+')) {
|
|
251
|
+
formatted = `+${formatted}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return formatted;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private extractErrorMessage(error: any): string {
|
|
258
|
+
if (error.response?.data?.error) {
|
|
259
|
+
return error.response.data.error;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (error.response?.data?.message) {
|
|
263
|
+
return error.response.data.message;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (error.response?.data?.details) {
|
|
267
|
+
return error.response.data.details;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (error.message) {
|
|
271
|
+
return error.message;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return 'Unknown Tubelight API error';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
validateConfig(config: any): boolean {
|
|
278
|
+
if (!config || typeof config !== 'object') {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Required fields
|
|
283
|
+
if (!config.userName || !config.password || !config.tenantId) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Validate message type specific requirements
|
|
288
|
+
if (config.messageType) {
|
|
289
|
+
switch (config.messageType) {
|
|
290
|
+
case 'template':
|
|
291
|
+
if (!config.templateId) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
|
|
296
|
+
case 'image':
|
|
297
|
+
case 'document':
|
|
298
|
+
case 'audio':
|
|
299
|
+
case 'video':
|
|
300
|
+
if (!config.mediaUrl) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
|
|
305
|
+
case 'location':
|
|
306
|
+
if (
|
|
307
|
+
typeof config.latitude !== 'number' ||
|
|
308
|
+
typeof config.longitude !== 'number'
|
|
309
|
+
) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
break;
|
|
313
|
+
|
|
314
|
+
case 'interactive':
|
|
315
|
+
if (
|
|
316
|
+
!config.buttons &&
|
|
317
|
+
!config.sections &&
|
|
318
|
+
(!config.interactiveType ||
|
|
319
|
+
!['button', 'list'].includes(config.interactiveType))
|
|
320
|
+
) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async getAccountInfo(config: TubelightWhatsAppConfig): Promise<any> {
|
|
331
|
+
try {
|
|
332
|
+
const token = await this.authenticate(config);
|
|
333
|
+
if (!token) {
|
|
334
|
+
throw new Error('Authentication failed');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const baseUrl = config.baseUrl || this.defaultBaseUrl;
|
|
338
|
+
const url = `${baseUrl}/account/info`;
|
|
339
|
+
|
|
340
|
+
const response = await axios.get(url, {
|
|
341
|
+
headers: {
|
|
342
|
+
'X-TENANT-ID': config.tenantId,
|
|
343
|
+
Authorization: `Bearer ${token}`,
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
return response.data;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
this.logger.error(
|
|
350
|
+
'Failed to get Tubelight account info',
|
|
351
|
+
error.response?.data || error.message,
|
|
352
|
+
);
|
|
353
|
+
throw new Error(
|
|
354
|
+
`Failed to get account info: ${this.extractErrorMessage(error)}`,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async getTemplates(config: TubelightWhatsAppConfig): Promise<any> {
|
|
360
|
+
try {
|
|
361
|
+
const token = await this.authenticate(config);
|
|
362
|
+
if (!token) {
|
|
363
|
+
throw new Error('Authentication failed');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const baseUrl = config.baseUrl || this.defaultBaseUrl;
|
|
367
|
+
const url = `${baseUrl}/templates`;
|
|
368
|
+
|
|
369
|
+
const response = await axios.get(url, {
|
|
370
|
+
headers: {
|
|
371
|
+
'X-TENANT-ID': config.tenantId,
|
|
372
|
+
Authorization: `Bearer ${token}`,
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
return response.data;
|
|
377
|
+
} catch (error) {
|
|
378
|
+
this.logger.error(
|
|
379
|
+
'Failed to get Tubelight templates',
|
|
380
|
+
error.response?.data || error.message,
|
|
381
|
+
);
|
|
382
|
+
throw new Error(
|
|
383
|
+
`Failed to get templates: ${this.extractErrorMessage(error)}`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async getMessageStatus(
|
|
389
|
+
config: TubelightWhatsAppConfig,
|
|
390
|
+
messageId: string,
|
|
391
|
+
): Promise<any> {
|
|
392
|
+
try {
|
|
393
|
+
const token = await this.authenticate(config);
|
|
394
|
+
if (!token) {
|
|
395
|
+
throw new Error('Authentication failed');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const baseUrl = config.baseUrl || this.defaultBaseUrl;
|
|
399
|
+
const url = `${baseUrl}/messages/${messageId}/status`;
|
|
400
|
+
|
|
401
|
+
const response = await axios.get(url, {
|
|
402
|
+
headers: {
|
|
403
|
+
'X-TENANT-ID': config.tenantId,
|
|
404
|
+
Authorization: `Bearer ${token}`,
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
return response.data;
|
|
409
|
+
} catch (error) {
|
|
410
|
+
this.logger.error(
|
|
411
|
+
'Failed to get message status',
|
|
412
|
+
error.response?.data || error.message,
|
|
413
|
+
);
|
|
414
|
+
throw new Error(
|
|
415
|
+
`Failed to get message status: ${this.extractErrorMessage(error)}`,
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
@@ -22,7 +22,6 @@ export class LayoutPreferenceService extends EntityServiceImpl {
|
|
|
22
22
|
throw new Error('User ID is required to create layout preference.');
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
console.log(mapped_entity_type, loggedInUser, userId);
|
|
26
25
|
const existingLayoutPreference =
|
|
27
26
|
await this.layoutPreferenceRepository.findByEntityUserId(
|
|
28
27
|
mapped_entity_type,
|
|
@@ -32,7 +31,6 @@ export class LayoutPreferenceService extends EntityServiceImpl {
|
|
|
32
31
|
type,
|
|
33
32
|
);
|
|
34
33
|
|
|
35
|
-
console.log(existingLayoutPreference, 'existingLayoutPreference');
|
|
36
34
|
if (existingLayoutPreference) {
|
|
37
35
|
return await super.updateEntity(
|
|
38
36
|
{ ...entityData, id: existingLayoutPreference.id },
|
|
@@ -98,16 +98,7 @@ export class ListMasterService {
|
|
|
98
98
|
inactiveIdsArray?: number[],
|
|
99
99
|
loggedInUser?: UserData,
|
|
100
100
|
) {
|
|
101
|
-
|
|
102
|
-
sourceList,
|
|
103
|
-
'sourceList',
|
|
104
|
-
params,
|
|
105
|
-
'params',
|
|
106
|
-
inactiveIdsArray,
|
|
107
|
-
'inactiveIdsArray',
|
|
108
|
-
loggedInUser,
|
|
109
|
-
'loggedInUser',
|
|
110
|
-
);
|
|
101
|
+
|
|
111
102
|
let result: { label: string; value: number }[] = [];
|
|
112
103
|
if (!sourceList) return result;
|
|
113
104
|
|
|
@@ -229,12 +229,10 @@ export class MasterService {
|
|
|
229
229
|
loggedInUser,
|
|
230
230
|
);
|
|
231
231
|
|
|
232
|
-
console.log(attributes, 'attributes');
|
|
233
232
|
const uniqueFields = attributes
|
|
234
233
|
.filter((attr) => attr.is_unique)
|
|
235
234
|
.map((attr) => attr.attribute_key);
|
|
236
235
|
|
|
237
|
-
console.log(uniqueFields, 'uniqueFields');
|
|
238
236
|
|
|
239
237
|
if (uniqueFields.length === 0) {
|
|
240
238
|
throw new Error(`No unique fields found for entityType: ${entityType}`);
|
|
@@ -523,11 +523,7 @@ export class EntityDynamicService {
|
|
|
523
523
|
row[attr.attribute_key] !== undefined
|
|
524
524
|
) {
|
|
525
525
|
// fetch media data
|
|
526
|
-
|
|
527
|
-
'Fetching media for',
|
|
528
|
-
attr.attribute_key,
|
|
529
|
-
row[attr.attribute_key],
|
|
530
|
-
);
|
|
526
|
+
|
|
531
527
|
|
|
532
528
|
row[attr.attribute_key] =
|
|
533
529
|
row[attr.attribute_key] != null
|
|
@@ -108,10 +108,6 @@ export class EntityServiceImpl implements EntityService<BaseEntity> {
|
|
|
108
108
|
[STATUS_ACTIVE, loggedInUser?.organization_id],
|
|
109
109
|
);
|
|
110
110
|
|
|
111
|
-
console.log('Status List:', statusList); // Debug log
|
|
112
|
-
console.log(
|
|
113
|
-
`status_code,${STATUS_ACTIVE},statusList, ${loggedInUser?.organization_id}`,
|
|
114
|
-
); // Debug log
|
|
115
111
|
|
|
116
112
|
entityData.created_date = new Date();
|
|
117
113
|
if (loggedInUser) {
|
|
@@ -79,14 +79,12 @@ export class EntityTableService {
|
|
|
79
79
|
displayType: string,
|
|
80
80
|
loggedInUser: any,
|
|
81
81
|
) {
|
|
82
|
-
console.log(entityType, listType, displayType, loggedInUser);
|
|
83
82
|
const entityTable = await this.findByEntityTypeAndListTypeAndDisplayType(
|
|
84
83
|
entityType,
|
|
85
84
|
listType,
|
|
86
85
|
displayType,
|
|
87
86
|
loggedInUser?.organization_id,
|
|
88
87
|
);
|
|
89
|
-
console.log(entityTable, 'entityTable');
|
|
90
88
|
const entityTableDto = entityTable as EntityTableDto;
|
|
91
89
|
if (entityTable) {
|
|
92
90
|
entityTableDto.column_list =
|
|
@@ -53,7 +53,6 @@ export class FieldGroupService extends EntityServiceImpl {
|
|
|
53
53
|
|
|
54
54
|
if (layoutJson?.form?.children && Array.isArray(layoutJson.form.children)) {
|
|
55
55
|
const firstChild = layoutJson.form.children[0];
|
|
56
|
-
console.log('First Child:', firstChild);
|
|
57
56
|
|
|
58
57
|
if (firstChild.type === 'wizard' && Array.isArray(firstChild.steps)) {
|
|
59
58
|
console.log('Wizard Steps:', firstChild.steps);
|
|
@@ -149,14 +149,6 @@ export class ModuleAccessService {
|
|
|
149
149
|
level_id: number;
|
|
150
150
|
organization_id: number;
|
|
151
151
|
}) {
|
|
152
|
-
console.log(
|
|
153
|
-
'step 1',
|
|
154
|
-
userId,
|
|
155
|
-
appcode,
|
|
156
|
-
level_type,
|
|
157
|
-
level_id,
|
|
158
|
-
organization_id,
|
|
159
|
-
);
|
|
160
152
|
|
|
161
153
|
// Step 1: Resolve roles
|
|
162
154
|
const roleCodes = await this.menuRepository.resolveUserRoles(
|
|
@@ -166,21 +158,18 @@ export class ModuleAccessService {
|
|
|
166
158
|
level_id,
|
|
167
159
|
);
|
|
168
160
|
|
|
169
|
-
console.log(roleCodes, 'roleCodes');
|
|
170
161
|
|
|
171
162
|
if (!roleCodes || roleCodes.length === 0) {
|
|
172
163
|
return [];
|
|
173
164
|
}
|
|
174
165
|
|
|
175
166
|
const roleIds = roleCodes.map(Number);
|
|
176
|
-
console.log('step 2', roleIds);
|
|
177
167
|
// Step 2: Get full permissions
|
|
178
168
|
const allPermissions =
|
|
179
169
|
await this.moduleAccessRepository.getModuleAccessByRoles(
|
|
180
170
|
roleIds,
|
|
181
171
|
appcode,
|
|
182
172
|
);
|
|
183
|
-
console.log('step 2', allPermissions);
|
|
184
173
|
|
|
185
174
|
// Step 3: If level_type is SCH, check school status using raw query
|
|
186
175
|
if (level_type === 'SCH') {
|
|
@@ -190,7 +179,6 @@ export class ModuleAccessService {
|
|
|
190
179
|
);
|
|
191
180
|
|
|
192
181
|
const school = result?.[0];
|
|
193
|
-
console.log('step 3', school);
|
|
194
182
|
|
|
195
183
|
const resolveStatus = await this.listMasterService.getResolvedListCode(
|
|
196
184
|
STATUS_INACTIVE,
|
|
@@ -202,7 +190,6 @@ export class ModuleAccessService {
|
|
|
202
190
|
return allPermissions.filter((perm) => perm.action === 'VIEW');
|
|
203
191
|
}
|
|
204
192
|
}
|
|
205
|
-
console.log('step 4', allPermissions);
|
|
206
193
|
// Step 4: Return all permissions normally
|
|
207
194
|
return allPermissions;
|
|
208
195
|
}
|
|
@@ -23,12 +23,19 @@ export class NotificationsController {
|
|
|
23
23
|
|
|
24
24
|
@Post('send')
|
|
25
25
|
async sendNotification(
|
|
26
|
-
@Body()
|
|
26
|
+
@Body()
|
|
27
|
+
body: {
|
|
28
|
+
token: string;
|
|
29
|
+
title: string;
|
|
30
|
+
message: string;
|
|
31
|
+
data?: Record<string, any>;
|
|
32
|
+
},
|
|
27
33
|
) {
|
|
28
34
|
return this.notificationsService.sendToDevice(
|
|
29
35
|
body.token,
|
|
30
36
|
body.title,
|
|
31
37
|
body.message,
|
|
38
|
+
body.data,
|
|
32
39
|
);
|
|
33
40
|
}
|
|
34
41
|
|
|
@@ -20,13 +20,28 @@ export class NotificationsService {
|
|
|
20
20
|
return { success: true, token };
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async sendToDevice(
|
|
24
|
-
|
|
23
|
+
async sendToDevice(
|
|
24
|
+
token: string,
|
|
25
|
+
title: string,
|
|
26
|
+
body: string,
|
|
27
|
+
data?: Record<string, any>,
|
|
28
|
+
) {
|
|
29
|
+
const message: admin.messaging.Message = {
|
|
25
30
|
token,
|
|
26
|
-
notification: { title, body },
|
|
31
|
+
notification: { title, body }, // system notification
|
|
32
|
+
data: data
|
|
33
|
+
? Object.fromEntries(
|
|
34
|
+
Object.entries(data).map(([k, v]) => [k, v.toString()]),
|
|
35
|
+
)
|
|
36
|
+
: undefined, // FCM requires all values to be strings
|
|
27
37
|
};
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
try {
|
|
40
|
+
return await this.firebaseAdmin.messaging().send(message);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error(error);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
30
45
|
}
|
|
31
46
|
|
|
32
47
|
// Helper: send to a registered user by userId
|
|
@@ -107,8 +107,7 @@ export class StageRepository {
|
|
|
107
107
|
// [stageGroup.workflow_id, organization_id],
|
|
108
108
|
// );
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
// console.log(rows2, '-----------INNER JOIN----------');
|
|
110
|
+
|
|
112
111
|
|
|
113
112
|
if (!rows || rows.length === 0) {
|
|
114
113
|
return [];
|
|
@@ -122,7 +122,6 @@ export class ActionService extends EntityServiceImpl {
|
|
|
122
122
|
entityData: BaseEntity,
|
|
123
123
|
loggedInUser: UserData,
|
|
124
124
|
): Promise<any> {
|
|
125
|
-
console.log('entityData', entityData);
|
|
126
125
|
|
|
127
126
|
// Extract template from entityData, keep the rest for ActionEntity
|
|
128
127
|
const { template, stage_id, form, ...actionData } =
|
|
@@ -40,7 +40,6 @@ export class CommTemplateService extends EntityServiceImpl {
|
|
|
40
40
|
async updateEntity(entityData: any, loggedInUser: UserData): Promise<any> {
|
|
41
41
|
const { attachments, ...templateData } = entityData;
|
|
42
42
|
|
|
43
|
-
console.log('update for template hit', entityData, loggedInUser);
|
|
44
43
|
|
|
45
44
|
if (
|
|
46
45
|
loggedInUser.level_type === 'SCH' &&
|