@peopl-health/nexus 2.5.11 → 2.6.0
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.
|
@@ -13,7 +13,7 @@ const Message = mongoose.models.Message;
|
|
|
13
13
|
|
|
14
14
|
const getConversationController = async (req, res) => {
|
|
15
15
|
const startTime = Date.now();
|
|
16
|
-
logger.info('Starting getConversationController at', new Date().toISOString());
|
|
16
|
+
logger.info('Starting getConversationController at', { time: new Date().toISOString() });
|
|
17
17
|
|
|
18
18
|
try {
|
|
19
19
|
// Parse pagination parameters
|
|
@@ -34,7 +34,7 @@ const getConversationController = async (req, res) => {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const messageCount = await Message.countDocuments({});
|
|
37
|
-
logger.info('Total message count:', messageCount);
|
|
37
|
+
logger.info('Total message count:', { messageCount });
|
|
38
38
|
|
|
39
39
|
if (messageCount === 0) {
|
|
40
40
|
logger.info('No messages found in database, returning empty conversations list');
|
|
@@ -63,7 +63,7 @@ const getConversationController = async (req, res) => {
|
|
|
63
63
|
const totalPages = Math.ceil(total / limit);
|
|
64
64
|
const totalTime = Date.now() - startTime;
|
|
65
65
|
|
|
66
|
-
logger.info('Number of conversations found:', conversations?.length || 0);
|
|
66
|
+
logger.info('Number of conversations found:', { conversationsCount: conversations?.length || 0 });
|
|
67
67
|
logger.info(`Total controller execution time: ${totalTime}ms`);
|
|
68
68
|
logger.info(`Filter: ${filter}, Pagination: ${conversations.length} of ${total} conversations (page ${page}/${totalPages})`);
|
|
69
69
|
|
|
@@ -83,8 +83,8 @@ const getConversationController = async (req, res) => {
|
|
|
83
83
|
logger.info('Response sent successfully!');
|
|
84
84
|
|
|
85
85
|
} catch (error) {
|
|
86
|
-
logger.error('Error fetching conversations:', error);
|
|
87
|
-
logger.error('Error stack:', error.stack);
|
|
86
|
+
logger.error('Error fetching conversations:', { error });
|
|
87
|
+
logger.error('Error stack:', { error: error.stack });
|
|
88
88
|
res.status(500).json({
|
|
89
89
|
success: false,
|
|
90
90
|
error: error.message || 'Failed to fetch conversations'
|
|
@@ -93,10 +93,10 @@ const getConversationController = async (req, res) => {
|
|
|
93
93
|
};
|
|
94
94
|
|
|
95
95
|
const getConversationMessagesController = async (req, res) => {
|
|
96
|
-
logger.info('Starting getConversationMessagesController at', new Date().toISOString());
|
|
96
|
+
logger.info('Starting getConversationMessagesController at', { time: new Date().toISOString() });
|
|
97
97
|
try {
|
|
98
98
|
const { phoneNumber } = req.params;
|
|
99
|
-
logger.info('Requested conversation for phone number:', phoneNumber);
|
|
99
|
+
logger.info('Requested conversation for phone number:', { phoneNumber });
|
|
100
100
|
|
|
101
101
|
if (!phoneNumber) {
|
|
102
102
|
return res.status(400).json({
|
|
@@ -113,7 +113,7 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
113
113
|
try {
|
|
114
114
|
query.createdAt = { $lt: new Date(before) };
|
|
115
115
|
} catch (parseError) {
|
|
116
|
-
logger.warn('Invalid date format for before parameter:', before, parseError);
|
|
116
|
+
logger.warn('Invalid date format for before parameter:', { before, parseError });
|
|
117
117
|
query.createdAt = { $lt: before };
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -142,12 +142,12 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
142
142
|
|
|
143
143
|
if (msg.media) {
|
|
144
144
|
if (!msg.media || typeof msg.media !== 'object') {
|
|
145
|
-
logger.warn('Found media message with invalid media data:', msg._id);
|
|
145
|
+
logger.warn('Found media message with invalid media data:', { msgId: msg._id });
|
|
146
146
|
return true;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
if (msg.media && (typeof msg.media.data === 'function' || msg.media.data instanceof Buffer)) {
|
|
150
|
-
logger.warn('Found media message with Buffer data that might cause serialization issues:', msg._id);
|
|
150
|
+
logger.warn('Found media message with Buffer data that might cause serialization issues:', { msgId: msg._id });
|
|
151
151
|
return true;
|
|
152
152
|
}
|
|
153
153
|
}
|
|
@@ -163,7 +163,7 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
163
163
|
logger.info('No messages found for this query');
|
|
164
164
|
}
|
|
165
165
|
} catch (err) {
|
|
166
|
-
logger.error('Database query error in message retrieval:', err);
|
|
166
|
+
logger.error('Database query error in message retrieval:', { err });
|
|
167
167
|
messages = [];
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -174,7 +174,7 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
174
174
|
JSON.stringify(msg);
|
|
175
175
|
return msg;
|
|
176
176
|
} catch (serializationError) {
|
|
177
|
-
logger.error(`Found non-serializable message with ID ${msg._id}:`, serializationError);
|
|
177
|
+
logger.error(`Found non-serializable message with ID ${msg._id}:`, { serializationError });
|
|
178
178
|
return {
|
|
179
179
|
_id: msg._id?.toString() || 'unknown',
|
|
180
180
|
numero: msg.numero || phoneNumber,
|
|
@@ -197,7 +197,7 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
197
197
|
});
|
|
198
198
|
logger.info('Successfully sent conversation messages response');
|
|
199
199
|
} catch (error) {
|
|
200
|
-
logger.error(`Error fetching conversation for ${req.params?.phoneNumber || 'unknown'}:`, error);
|
|
200
|
+
logger.error(`Error fetching conversation for ${req.params?.phoneNumber || 'unknown'}:`, { error });
|
|
201
201
|
res.status(500).json({
|
|
202
202
|
success: false,
|
|
203
203
|
error: error.message || 'Failed to fetch conversation'
|
|
@@ -206,7 +206,7 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
206
206
|
};
|
|
207
207
|
|
|
208
208
|
const getConversationReplyController = async (req, res) => {
|
|
209
|
-
logger.info('Starting getConversationReplyController at', new Date().toISOString());
|
|
209
|
+
logger.info('Starting getConversationReplyController at', { time: new Date().toISOString() });
|
|
210
210
|
try {
|
|
211
211
|
const { phoneNumber, message, mediaData, contentSid, variables } = req.body;
|
|
212
212
|
logger.info('Reply request params:', {
|
|
@@ -225,7 +225,7 @@ const getConversationReplyController = async (req, res) => {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
const formattedPhoneNumber = ensureWhatsAppFormat(phoneNumber);
|
|
228
|
-
logger.info('Formatted phone number:', formattedPhoneNumber);
|
|
228
|
+
logger.info('Formatted phone number:', { formattedPhoneNumber });
|
|
229
229
|
|
|
230
230
|
const messageData = {
|
|
231
231
|
code: formattedPhoneNumber,
|
|
@@ -235,7 +235,7 @@ const getConversationReplyController = async (req, res) => {
|
|
|
235
235
|
|
|
236
236
|
// Handle template message (contentSid provided)
|
|
237
237
|
if (contentSid) {
|
|
238
|
-
logger.info('Processing template message with contentSid:', contentSid);
|
|
238
|
+
logger.info('Processing template message with contentSid:', { contentSid });
|
|
239
239
|
messageData.contentSid = contentSid;
|
|
240
240
|
|
|
241
241
|
if (variables && Object.keys(variables).length > 0) {
|
|
@@ -281,10 +281,10 @@ const getConversationReplyController = async (req, res) => {
|
|
|
281
281
|
message: 'Reply sent successfully'
|
|
282
282
|
});
|
|
283
283
|
} catch (error) {
|
|
284
|
-
logger.error('Error sending reply:', error);
|
|
284
|
+
logger.error('Error sending reply:', { error });
|
|
285
285
|
logger.info('Request body', { body: req.body || {} });
|
|
286
286
|
const errorMsg = error.message || 'Failed to send reply';
|
|
287
|
-
logger.error('Responding with error:', errorMsg);
|
|
287
|
+
logger.error('Responding with error:', { errorMsg });
|
|
288
288
|
res.status(500).json({
|
|
289
289
|
success: false,
|
|
290
290
|
error: errorMsg
|
|
@@ -361,7 +361,7 @@ const searchConversationsController = async (req, res) => {
|
|
|
361
361
|
batch.map(p => `{whatsapp_id} = "${p}"`).join(', ') +
|
|
362
362
|
')';
|
|
363
363
|
return getRecordByFilter(Historial_Clinico_ID, 'estado_general', formula).catch(error => {
|
|
364
|
-
logger.error('Error fetching Airtable batch for search:', error);
|
|
364
|
+
logger.error('Error fetching Airtable batch for search:', { error });
|
|
365
365
|
return [];
|
|
366
366
|
});
|
|
367
367
|
});
|
|
@@ -380,7 +380,7 @@ const searchConversationsController = async (req, res) => {
|
|
|
380
380
|
});
|
|
381
381
|
logger.info(`Found ${Object.keys(airtableNameMap).length} names in Airtable for search results (${batches.length} batches)`);
|
|
382
382
|
} catch (error) {
|
|
383
|
-
logger.error('Error fetching names from Airtable for search, falling back to nombre_whatsapp:', error);
|
|
383
|
+
logger.error('Error fetching names from Airtable for search, falling back to nombre_whatsapp:', { error });
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
386
|
|
|
@@ -445,7 +445,7 @@ const searchConversationsController = async (req, res) => {
|
|
|
445
445
|
});
|
|
446
446
|
|
|
447
447
|
} catch (error) {
|
|
448
|
-
logger.error('Error searching conversations:', error);
|
|
448
|
+
logger.error('Error searching conversations:', { error });
|
|
449
449
|
res.status(500).json({
|
|
450
450
|
success: false,
|
|
451
451
|
error: error.message || 'Failed to search conversations'
|
|
@@ -478,7 +478,7 @@ const getConversationsByNameController = async (req, res) => {
|
|
|
478
478
|
}))
|
|
479
479
|
});
|
|
480
480
|
} catch (error) {
|
|
481
|
-
logger.error('Error fetching conversations by name:', error);
|
|
481
|
+
logger.error('Error fetching conversations by name:', { error });
|
|
482
482
|
res.status(500).json({
|
|
483
483
|
success: false,
|
|
484
484
|
error: error.message || 'Failed to fetch conversations by name'
|
|
@@ -524,7 +524,7 @@ const getNewMessagesController = async (req, res) => {
|
|
|
524
524
|
messages: messages
|
|
525
525
|
});
|
|
526
526
|
} catch (error) {
|
|
527
|
-
logger.error(`Error fetching new messages for ${req.params.phoneNumber}:`, error);
|
|
527
|
+
logger.error(`Error fetching new messages for ${req.params.phoneNumber}:`, { error });
|
|
528
528
|
res.status(500).json({
|
|
529
529
|
success: false,
|
|
530
530
|
error: error.message || 'Failed to fetch new messages'
|
|
@@ -533,7 +533,7 @@ const getNewMessagesController = async (req, res) => {
|
|
|
533
533
|
};
|
|
534
534
|
|
|
535
535
|
const markMessagesAsReadController = async (req, res) => {
|
|
536
|
-
logger.info('Starting markMessagesAsReadController at', new Date().toISOString());
|
|
536
|
+
logger.info('Starting markMessagesAsReadController at', { time: new Date().toISOString() });
|
|
537
537
|
try {
|
|
538
538
|
const { phoneNumber } = req.params;
|
|
539
539
|
|
|
@@ -544,7 +544,7 @@ const markMessagesAsReadController = async (req, res) => {
|
|
|
544
544
|
});
|
|
545
545
|
}
|
|
546
546
|
|
|
547
|
-
logger.info('Marking messages as read for phone number:', phoneNumber);
|
|
547
|
+
logger.info('Marking messages as read for phone number:', { phoneNumber });
|
|
548
548
|
const result = await Message.updateMany(
|
|
549
549
|
{
|
|
550
550
|
numero: phoneNumber,
|
|
@@ -563,8 +563,8 @@ const markMessagesAsReadController = async (req, res) => {
|
|
|
563
563
|
modifiedCount: result.nModified || result.modifiedCount
|
|
564
564
|
});
|
|
565
565
|
} catch (error) {
|
|
566
|
-
logger.error(`Error marking messages as read for ${req.params?.phoneNumber || 'unknown'}:`, error);
|
|
567
|
-
logger.error('Error stack:', error.stack);
|
|
566
|
+
logger.error(`Error marking messages as read for ${req.params?.phoneNumber || 'unknown'}:`, { error });
|
|
567
|
+
logger.error('Error stack:', { error: error.stack });
|
|
568
568
|
res.status(500).json({
|
|
569
569
|
success: false,
|
|
570
570
|
error: error.message || 'Failed to mark messages as read'
|
|
@@ -573,7 +573,7 @@ const markMessagesAsReadController = async (req, res) => {
|
|
|
573
573
|
};
|
|
574
574
|
|
|
575
575
|
const sendTemplateToNewNumberController = async (req, res) => {
|
|
576
|
-
logger.info('Starting sendTemplateToNewNumberController at', new Date().toISOString());
|
|
576
|
+
logger.info('Starting sendTemplateToNewNumberController at', { time: new Date().toISOString() });
|
|
577
577
|
try {
|
|
578
578
|
const { phoneNumber, templateId, variables } = req.body;
|
|
579
579
|
|
|
@@ -618,7 +618,7 @@ const sendTemplateToNewNumberController = async (req, res) => {
|
|
|
618
618
|
messageId: message.sid
|
|
619
619
|
});
|
|
620
620
|
} catch (error) {
|
|
621
|
-
logger.error('Error sending template to new number:', error);
|
|
621
|
+
logger.error('Error sending template to new number:', { error });
|
|
622
622
|
res.status(500).json({
|
|
623
623
|
success: false,
|
|
624
624
|
error: error.message || 'Failed to send template to new number'
|
|
@@ -627,14 +627,14 @@ const sendTemplateToNewNumberController = async (req, res) => {
|
|
|
627
627
|
};
|
|
628
628
|
|
|
629
629
|
const getOpenAIThreadMessagesController = async (req, res) => {
|
|
630
|
-
logger.info('Starting getOpenAIThreadMessagesController at', new Date().toISOString());
|
|
630
|
+
logger.info('Starting getOpenAIThreadMessagesController at', { time: new Date().toISOString() });
|
|
631
631
|
try {
|
|
632
632
|
const { phoneNumber } = req.params;
|
|
633
633
|
const { limit = 50, order = 'desc', runId } = req.query;
|
|
634
634
|
const variant = process.env.VARIANT || 'assistants';
|
|
635
635
|
|
|
636
|
-
logger.info('Fetching OpenAI thread messages for:', phoneNumber);
|
|
637
|
-
logger.info('Variant:', variant, 'Limit:', limit, 'Order:', order);
|
|
636
|
+
logger.info('Fetching OpenAI thread messages for:', { phoneNumber });
|
|
637
|
+
logger.info('Variant:', { variant }, 'Limit:', { limit }, 'Order:', { order });
|
|
638
638
|
|
|
639
639
|
if (!phoneNumber) {
|
|
640
640
|
return res.status(400).json({
|
|
@@ -649,7 +649,7 @@ const getOpenAIThreadMessagesController = async (req, res) => {
|
|
|
649
649
|
}).sort({ createdAt: -1 });
|
|
650
650
|
|
|
651
651
|
if (!thread) {
|
|
652
|
-
logger.info('No active OpenAI thread found for:', phoneNumber);
|
|
652
|
+
logger.info('No active OpenAI thread found for:', { phoneNumber });
|
|
653
653
|
return res.status(404).json({
|
|
654
654
|
success: false,
|
|
655
655
|
error: 'No active OpenAI thread found for this phone number'
|
|
@@ -657,14 +657,14 @@ const getOpenAIThreadMessagesController = async (req, res) => {
|
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
let conversationId = thread.conversation_id;
|
|
660
|
-
logger.info('Thread found - Conversation ID:', conversationId);
|
|
660
|
+
logger.info('Thread found - Conversation ID:', { conversationId });
|
|
661
661
|
|
|
662
662
|
const provider = llmConfig.getOpenAIProvider({ instantiate: true, variant });
|
|
663
663
|
if (!provider) {
|
|
664
664
|
throw new Error('OpenAI provider not initialized');
|
|
665
665
|
}
|
|
666
666
|
|
|
667
|
-
logger.info('Using provider variant:', provider.getVariant());
|
|
667
|
+
logger.info('Using provider variant:', { variant: provider.getVariant() });
|
|
668
668
|
|
|
669
669
|
const queryParams = {
|
|
670
670
|
threadId: conversationId,
|
|
@@ -679,7 +679,7 @@ const getOpenAIThreadMessagesController = async (req, res) => {
|
|
|
679
679
|
let messages;
|
|
680
680
|
let threadRecreated = false;
|
|
681
681
|
|
|
682
|
-
logger.info('Calling listMessages with params:', queryParams);
|
|
682
|
+
logger.info('Calling listMessages with params:', { queryParams });
|
|
683
683
|
messages = await withThreadRecovery(
|
|
684
684
|
async (currentThread = thread) => {
|
|
685
685
|
if (currentThread !== thread) {
|
|
@@ -712,8 +712,8 @@ const getOpenAIThreadMessagesController = async (req, res) => {
|
|
|
712
712
|
});
|
|
713
713
|
|
|
714
714
|
} catch (error) {
|
|
715
|
-
logger.error('Error fetching OpenAI thread messages:', error);
|
|
716
|
-
logger.error('Error stack:', error.stack);
|
|
715
|
+
logger.error('Error fetching OpenAI thread messages:', { error });
|
|
716
|
+
logger.error('Error stack:', { error: error.stack });
|
|
717
717
|
res.status(500).json({
|
|
718
718
|
success: false,
|
|
719
719
|
error: error.message || 'Failed to fetch OpenAI thread messages'
|
package/lib/routes/index.js
CHANGED
|
@@ -68,8 +68,8 @@ const templateRouteDefinitions = {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
const threadRouteDefinitions = {
|
|
71
|
-
'PUT /review/:code': 'updateThreadReviewStatus',
|
|
72
71
|
'PUT /review/all': 'updateAllThreadsReviewStatus',
|
|
72
|
+
'PUT /review/:code': 'updateThreadReviewStatus',
|
|
73
73
|
'GET /review': 'getThreadsByReviewStatus'
|
|
74
74
|
};
|
|
75
75
|
|
|
@@ -12,6 +12,7 @@ const fetchConversationData = async (filter, skip, limit) => {
|
|
|
12
12
|
unread: { ...baseMatch, ...unreadMatch },
|
|
13
13
|
recent: { ...baseMatch, createdAt: { $gt: new Date(Date.now() - 24 * 60 * 60 * 1000) } },
|
|
14
14
|
'no-response': baseMatch,
|
|
15
|
+
'pending-review': baseMatch,
|
|
15
16
|
all: baseMatch
|
|
16
17
|
};
|
|
17
18
|
|
|
@@ -24,7 +25,22 @@ const fetchConversationData = async (filter, skip, limit) => {
|
|
|
24
25
|
{ $sort: { createdAt: 1, timestamp: 1 } },
|
|
25
26
|
{ $group: { _id: '$numero', latestMessage: { $last: '$$ROOT' }, messageCount: { $sum: 1 } } },
|
|
26
27
|
...(filter === 'no-response' ? [{ $match: { 'latestMessage.from_me': false } }] : []),
|
|
27
|
-
{
|
|
28
|
+
{
|
|
29
|
+
$lookup: {
|
|
30
|
+
from: 'threads',
|
|
31
|
+
localField: '_id',
|
|
32
|
+
foreignField: 'code',
|
|
33
|
+
as: 'threadInfo'
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
$addFields: {
|
|
38
|
+
review: { $arrayElemAt: ['$threadInfo.review', 0] }
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
{ $project: { threadInfo: 0 } },
|
|
42
|
+
...(filter === 'pending-review' ? [{ $match: { $or: [{ review: false }, { review: null }] } }] : []),
|
|
43
|
+
{ $sort: { 'latestMessage.createdAt': -1, 'latestMessage.timestamp': -1 } },
|
|
28
44
|
{ $skip: skip },
|
|
29
45
|
{ $limit: limit }
|
|
30
46
|
];
|
|
@@ -34,9 +50,12 @@ const fetchConversationData = async (filter, skip, limit) => {
|
|
|
34
50
|
Message.aggregate(pipeline),
|
|
35
51
|
Message.aggregate([{ $match: { ...baseMatch, from_me: false } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', name: { $first: '$nombre_whatsapp' } } }]),
|
|
36
52
|
Message.aggregate([{ $match: { ...baseMatch, ...unreadMatch } }, { $group: { _id: '$numero', unreadCount: { $sum: 1 } } }]),
|
|
37
|
-
Message.aggregate(
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
Message.aggregate(
|
|
54
|
+
filter === 'no-response'
|
|
55
|
+
? [{ $match: baseMatch }, { $project: { numero: 1, from_me: 1, createdAt: 1, timestamp: 1 } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', latestMessage: { $first: '$$ROOT' } } }, { $match: { 'latestMessage.from_me': false } }, { $count: 'total' }]
|
|
56
|
+
: filter === 'pending-review'
|
|
57
|
+
? [{ $match: baseMatch }, { $group: { _id: '$numero' } }, { $lookup: { from: 'threads', localField: '_id', foreignField: 'code', as: 'threadInfo' } }, { $addFields: { review: { $arrayElemAt: ['$threadInfo.review', 0] } } }, { $match: { $or: [{ review: false }, { review: null }] } }, { $count: 'total' }]
|
|
58
|
+
: [{ $match: filterConditions }, { $group: { _id: '$numero' } }, { $count: 'total' }]
|
|
40
59
|
)
|
|
41
60
|
]);
|
|
42
61
|
|
|
@@ -76,7 +95,8 @@ const processConversations = async (conversations, nameMap, unreadMap) => {
|
|
|
76
95
|
unreadCount: unreadMap[phoneNumber] || 0,
|
|
77
96
|
isLastMessageMedia: !!msg?.media,
|
|
78
97
|
lastMessageType: getMediaType(msg?.media),
|
|
79
|
-
lastMessageFromMe: msg?.from_me || false
|
|
98
|
+
lastMessageFromMe: msg?.from_me || false,
|
|
99
|
+
review: conv?.review || false
|
|
80
100
|
};
|
|
81
101
|
};
|
|
82
102
|
|