@app-connect/core 1.7.8 → 1.7.11

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 (69) hide show
  1. package/connector/developerPortal.js +43 -0
  2. package/connector/proxy/index.js +10 -3
  3. package/connector/registry.js +8 -6
  4. package/handlers/admin.js +44 -21
  5. package/handlers/auth.js +97 -69
  6. package/handlers/calldown.js +10 -4
  7. package/handlers/contact.js +45 -112
  8. package/handlers/disposition.js +4 -142
  9. package/handlers/log.js +174 -259
  10. package/handlers/user.js +19 -6
  11. package/index.js +310 -122
  12. package/lib/analytics.js +3 -1
  13. package/lib/authSession.js +68 -0
  14. package/lib/callLogComposer.js +498 -420
  15. package/lib/errorHandler.js +206 -0
  16. package/lib/jwt.js +2 -0
  17. package/lib/logger.js +190 -0
  18. package/lib/oauth.js +21 -12
  19. package/lib/ringcentral.js +2 -10
  20. package/lib/sharedSMSComposer.js +471 -0
  21. package/mcp/SupportedPlatforms.md +12 -0
  22. package/mcp/lib/validator.js +91 -0
  23. package/mcp/mcpHandler.js +166 -0
  24. package/mcp/tools/checkAuthStatus.js +90 -0
  25. package/mcp/tools/collectAuthInfo.js +86 -0
  26. package/mcp/tools/createCallLog.js +299 -0
  27. package/mcp/tools/createMessageLog.js +283 -0
  28. package/mcp/tools/doAuth.js +185 -0
  29. package/mcp/tools/findContactByName.js +87 -0
  30. package/mcp/tools/findContactByPhone.js +96 -0
  31. package/mcp/tools/getCallLog.js +98 -0
  32. package/mcp/tools/getHelp.js +39 -0
  33. package/mcp/tools/getPublicConnectors.js +46 -0
  34. package/mcp/tools/index.js +58 -0
  35. package/mcp/tools/logout.js +63 -0
  36. package/mcp/tools/rcGetCallLogs.js +73 -0
  37. package/mcp/tools/setConnector.js +64 -0
  38. package/mcp/tools/updateCallLog.js +122 -0
  39. package/models/accountDataModel.js +34 -0
  40. package/models/cacheModel.js +3 -0
  41. package/package.json +6 -4
  42. package/releaseNotes.json +36 -0
  43. package/test/connector/registry.test.js +145 -0
  44. package/test/handlers/admin.test.js +583 -0
  45. package/test/handlers/auth.test.js +355 -0
  46. package/test/handlers/contact.test.js +852 -0
  47. package/test/handlers/log.test.js +872 -0
  48. package/test/lib/callLogComposer.test.js +1231 -0
  49. package/test/lib/debugTracer.test.js +328 -0
  50. package/test/lib/logger.test.js +206 -0
  51. package/test/lib/oauth.test.js +359 -0
  52. package/test/lib/ringcentral.test.js +473 -0
  53. package/test/lib/sharedSMSComposer.test.js +1084 -0
  54. package/test/lib/util.test.js +282 -0
  55. package/test/mcp/tools/collectAuthInfo.test.js +192 -0
  56. package/test/mcp/tools/createCallLog.test.js +412 -0
  57. package/test/mcp/tools/createMessageLog.test.js +580 -0
  58. package/test/mcp/tools/doAuth.test.js +363 -0
  59. package/test/mcp/tools/findContactByName.test.js +263 -0
  60. package/test/mcp/tools/findContactByPhone.test.js +284 -0
  61. package/test/mcp/tools/getCallLog.test.js +286 -0
  62. package/test/mcp/tools/getPublicConnectors.test.js +128 -0
  63. package/test/mcp/tools/logout.test.js +169 -0
  64. package/test/mcp/tools/setConnector.test.js +177 -0
  65. package/test/mcp/tools/updateCallLog.test.js +346 -0
  66. package/test/models/accountDataModel.test.js +98 -0
  67. package/test/models/dynamo/connectorSchema.test.js +189 -0
  68. package/test/models/models.test.js +539 -0
  69. package/test/setup.js +176 -176
package/index.js CHANGED
@@ -1,15 +1,16 @@
1
1
  const express = require('express');
2
2
  const cors = require('cors')
3
3
  const bodyParser = require('body-parser');
4
+ require('body-parser-xml')(bodyParser);
4
5
  const dynamoose = require('dynamoose');
5
6
  const axios = require('axios');
6
7
  const { UserModel } = require('./models/userModel');
7
8
  const { CallDownListModel } = require('./models/callDownListModel');
8
- const { Op } = require('sequelize');
9
9
  const { CallLogModel } = require('./models/callLogModel');
10
10
  const { MessageLogModel } = require('./models/messageLogModel');
11
11
  const { AdminConfigModel } = require('./models/adminConfigModel');
12
12
  const { CacheModel } = require('./models/cacheModel');
13
+ const { AccountDataModel } = require('./models/accountDataModel');
13
14
  const jwt = require('./lib/jwt');
14
15
  const logCore = require('./handlers/log');
15
16
  const contactCore = require('./handlers/contact');
@@ -24,14 +25,19 @@ const analytics = require('./lib/analytics');
24
25
  const util = require('./lib/util');
25
26
  const connectorRegistry = require('./connector/registry');
26
27
  const calldown = require('./handlers/calldown');
28
+ const mcpHandler = require('./mcp/mcpHandler');
29
+ const logger = require('./lib/logger');
27
30
  const { DebugTracer } = require('./lib/debugTracer');
28
31
  const s3ErrorLogReport = require('./lib/s3ErrorLogReport');
32
+ const { handleDatabaseError } = require('./lib/errorHandler');
33
+ const { updateAuthSession } = require('./lib/authSession');
29
34
 
30
35
  let packageJson = null;
31
36
  try {
32
37
  packageJson = require('./package.json');
33
38
  }
34
39
  catch (e) {
40
+ logger.error('Error loading package.json', { stack: e.stack });
35
41
  packageJson = require('../package.json');
36
42
  }
37
43
 
@@ -39,18 +45,25 @@ catch (e) {
39
45
  if (process.env.DYNAMODB_LOCALHOST) {
40
46
  dynamoose.aws.ddb.local(process.env.DYNAMODB_LOCALHOST);
41
47
  }
42
-
48
+ // log axios requests
49
+ if (process.env.IS_PROD === 'false') {
50
+ axios.interceptors.request.use(request => {
51
+ console.log('Request:', `[${request.method}]`, request.url);
52
+ return request;
53
+ });
54
+ }
43
55
  axios.defaults.headers.common['Unified-CRM-Extension-Version'] = packageJson.version;
44
56
 
45
57
  async function initDB() {
46
58
  if (!process.env.DISABLE_SYNC_DB_TABLE) {
47
- console.log('creating db tables if not exist...');
59
+ logger.info('creating db tables if not exist...');
48
60
  await UserModel.sync();
49
61
  await CallLogModel.sync();
50
62
  await MessageLogModel.sync();
51
63
  await AdminConfigModel.sync();
52
64
  await CacheModel.sync();
53
65
  await CallDownListModel.sync();
66
+ await AccountDataModel.sync();
54
67
  }
55
68
  }
56
69
 
@@ -120,6 +133,7 @@ function createCoreRouter() {
120
133
  }
121
134
  }
122
135
  catch (e) {
136
+ logger.error('Error getting crm manifest', { stack: e.stack });
123
137
  res.status(400).send('Platform not found');
124
138
  }
125
139
  });
@@ -159,11 +173,11 @@ function createCoreRouter() {
159
173
  result.getLicenseStatus = !!platformModule.getLicenseStatus;
160
174
  result.getLogFormatType = !!platformModule.getLogFormatType;
161
175
  result.cacheCallNote = !!process.env.USE_CACHE;
162
- res.status(200).send(tracer ? tracer.wrapResponse({ result }) : { result });
176
+ res.status(200).send(tracer ? tracer.wrapResponse(result) : result);
163
177
  }
164
178
  else {
165
179
  tracer?.trace('implementedInterfaces:noPlatform', {});
166
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please provide platform.' }) : { error: 'Please provide platform.' });
180
+ res.status(400).send(tracer ? tracer.wrapResponse('Please provide platform.') : 'Please provide platform.');
167
181
  return;
168
182
  }
169
183
  }
@@ -187,11 +201,11 @@ function createCoreRouter() {
187
201
  platformName = platform;
188
202
  if (!userId) {
189
203
  tracer?.trace('licenseStatus:noUserId', {});
190
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'No user ID' }) : { error: 'No user ID' });
204
+ res.status(400).send(tracer ? tracer.wrapResponse('No user ID') : 'No user ID');
191
205
  success = true;
192
206
  }
193
207
  const licenseStatus = await authCore.getLicenseStatus({ userId, platform });
194
- res.status(200).send(tracer ? tracer.wrapResponse({ licenseStatus }) : { licenseStatus });
208
+ res.status(200).send(tracer ? tracer.wrapResponse(licenseStatus) : licenseStatus);
195
209
  success = true;
196
210
  }
197
211
  else {
@@ -253,7 +267,7 @@ function createCoreRouter() {
253
267
  const decodedToken = jwt.decodeJwt(jwtToken);
254
268
  if (!decodedToken) {
255
269
  tracer?.trace('authValidation:invalidJwtToken', {});
256
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Invalid JWT token' }) : { error: 'Invalid JWT token' });
270
+ res.status(400).send(tracer ? tracer.wrapResponse('Invalid JWT token') : 'Invalid JWT token');
257
271
  return;
258
272
  }
259
273
  const { id: userId, platform } = decodedToken;
@@ -267,12 +281,12 @@ function createCoreRouter() {
267
281
  }
268
282
  else {
269
283
  tracer?.trace('authValidation:noToken', {});
270
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
284
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
271
285
  success = false;
272
286
  }
273
287
  }
274
288
  catch (e) {
275
- console.log(`platform: ${platformName} \n${e.stack}`);
289
+ logger.error('Auth validation failed', { platform: platformName, stack: e.stack });
276
290
  tracer?.traceError('authValidation:error', e);
277
291
  statusCode = e.response?.status ?? 'unknown';
278
292
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
@@ -314,17 +328,17 @@ function createCoreRouter() {
314
328
  const hashedRcAccountId = util.getHashValue(rcAccountId, process.env.HASH_KEY);
315
329
  if (isValidated) {
316
330
  await adminCore.upsertAdminSettings({ hashedRcAccountId, adminSettings: req.body.adminSettings });
317
- res.status(200).send(tracer ? tracer.wrapResponse({ message: 'Admin settings updated' }) : { message: 'Admin settings updated' });
331
+ res.status(200).send(tracer ? tracer.wrapResponse('Admin settings updated') : 'Admin settings updated');
318
332
  success = true;
319
333
  }
320
334
  else {
321
335
  tracer?.trace('setAdminSettings:adminValidationFailed', {});
322
- res.status(401).send(tracer ? tracer.wrapResponse({ error: 'Admin validation failed' }) : { error: 'Admin validation failed' });
336
+ res.status(401).send(tracer ? tracer.wrapResponse('Admin validation failed') : 'Admin validation failed');
323
337
  success = false;
324
338
  }
325
339
  }
326
340
  catch (e) {
327
- console.log(`${e.stack}`);
341
+ logger.error('Set admin settings failed', { stack: e.stack });
328
342
  tracer?.traceError('setAdminSettings:error', e);
329
343
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
330
344
  success = false;
@@ -358,7 +372,7 @@ function createCoreRouter() {
358
372
  const user = await UserModel.findByPk(unAuthData?.id);
359
373
  if (!user) {
360
374
  tracer?.trace('getAdminSettings:userNotFound', {});
361
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
375
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
362
376
  return;
363
377
  }
364
378
  const { isValidated, rcAccountId } = await adminCore.validateAdminRole({ rcAccessToken: req.query.rcAccessToken });
@@ -366,7 +380,7 @@ function createCoreRouter() {
366
380
  if (isValidated) {
367
381
  const adminSettings = await adminCore.getAdminSettings({ hashedRcAccountId });
368
382
  if (adminSettings) {
369
- res.status(200).send(tracer ? tracer.wrapResponse({ adminSettings }) : { adminSettings });
383
+ res.status(200).send(tracer ? tracer.wrapResponse(adminSettings) : adminSettings);
370
384
  }
371
385
  else {
372
386
  res.status(200).send(tracer ? tracer.wrapResponse({
@@ -381,13 +395,13 @@ function createCoreRouter() {
381
395
  }
382
396
  else {
383
397
  tracer?.trace('getAdminSettings:adminValidationFailed', {});
384
- res.status(401).send(tracer ? tracer.wrapResponse({ error: 'Admin validation failed' }) : { error: 'Admin validation failed' });
398
+ res.status(401).send(tracer ? tracer.wrapResponse('Admin validation failed') : 'Admin validation failed');
385
399
  success = true;
386
400
  }
387
401
  }
388
402
  else {
389
403
  tracer?.trace('getAdminSettings:noToken', {});
390
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
404
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
391
405
  success = false;
392
406
  }
393
407
  }
@@ -426,30 +440,30 @@ function createCoreRouter() {
426
440
  const user = await UserModel.findByPk(unAuthData?.id);
427
441
  if (!user) {
428
442
  tracer?.trace('getUserMapping:userNotFound', {});
429
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
443
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
430
444
  return;
431
445
  }
432
446
  const { isValidated, rcAccountId } = await adminCore.validateAdminRole({ rcAccessToken: req.query.rcAccessToken });
433
447
  const hashedRcAccountId = util.getHashValue(rcAccountId, process.env.HASH_KEY);
434
448
  if (isValidated) {
435
449
  const userMapping = await adminCore.getUserMapping({ user, hashedRcAccountId, rcExtensionList: req.body.rcExtensionList });
436
- res.status(200).send(tracer ? tracer.wrapResponse({ userMapping }) : { userMapping });
450
+ res.status(200).send(tracer ? tracer.wrapResponse(userMapping) : userMapping);
437
451
  success = true;
438
452
  }
439
453
  else {
440
454
  tracer?.trace('getUserMapping:adminValidationFailed', {});
441
- res.status(401).send(tracer ? tracer.wrapResponse({ error: 'Admin validation failed' }) : { error: 'Admin validation failed' });
455
+ res.status(401).send(tracer ? tracer.wrapResponse('Admin validation failed') : 'Admin validation failed');
442
456
  success = true;
443
457
  }
444
458
  }
445
459
  else {
446
460
  tracer?.trace('getUserMapping:noToken', {});
447
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
461
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
448
462
  success = false;
449
463
  }
450
464
  }
451
465
  catch (e) {
452
- console.log(`${e.stack}`);
466
+ logger.error('Get user mapping failed', { stack: e.stack });
453
467
  tracer?.traceError('getUserMapping:error', e);
454
468
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
455
469
  }
@@ -477,7 +491,7 @@ function createCoreRouter() {
477
491
  const jwtToken = req.query.jwtToken;
478
492
  if (!jwtToken) {
479
493
  tracer?.trace('getServerLoggingSettings:noToken', {});
480
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
494
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
481
495
  return;
482
496
  }
483
497
  const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
@@ -485,22 +499,22 @@ function createCoreRouter() {
485
499
  const unAuthData = jwt.decodeJwt(jwtToken);
486
500
  if (!unAuthData?.id) {
487
501
  tracer?.trace('getServerLoggingSettings:noToken', {});
488
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
502
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
489
503
  return;
490
504
  }
491
505
  platformName = unAuthData?.platform ?? 'Unknown';
492
506
  const user = await UserModel.findByPk(unAuthData?.id);
493
507
  if (!user) {
494
508
  tracer?.trace('getServerLoggingSettings:userNotFound', {});
495
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
509
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
496
510
  return;
497
511
  }
498
512
  const serverLoggingSettings = await adminCore.getServerLoggingSettings({ user });
499
- res.status(200).send(tracer ? tracer.wrapResponse({ serverLoggingSettings }) : { serverLoggingSettings });
513
+ res.status(200).send(tracer ? tracer.wrapResponse(serverLoggingSettings) : serverLoggingSettings);
500
514
  success = true;
501
515
  }
502
516
  catch (e) {
503
- console.log(`${e.stack}`);
517
+ logger.error('Get server logging settings failed', { stack: e.stack });
504
518
  tracer?.traceError('getServerLoggingSettings:error', e);
505
519
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
506
520
  }
@@ -528,12 +542,12 @@ function createCoreRouter() {
528
542
  const jwtToken = req.query.jwtToken;
529
543
  if (!jwtToken) {
530
544
  tracer?.trace('setServerLoggingSettings:noToken', {});
531
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
545
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
532
546
  return;
533
547
  }
534
548
  if (!req.body.additionalFieldValues) {
535
549
  tracer?.trace('setServerLoggingSettings:missingAdditionalFieldValues', {});
536
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Missing additionalFieldValues' }) : { error: 'Missing additionalFieldValues' });
550
+ res.status(400).send(tracer ? tracer.wrapResponse('Missing additionalFieldValues') : 'Missing additionalFieldValues');
537
551
  return;
538
552
  }
539
553
  const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
@@ -541,14 +555,14 @@ function createCoreRouter() {
541
555
  const unAuthData = jwt.decodeJwt(jwtToken);
542
556
  if (!unAuthData?.id) {
543
557
  tracer?.trace('setServerLoggingSettings:noToken', {});
544
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
558
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
545
559
  return;
546
560
  }
547
561
  platformName = unAuthData?.platform ?? 'Unknown';
548
562
  const user = await UserModel.findByPk(unAuthData?.id);
549
563
  if (!user) {
550
564
  tracer?.trace('setServerLoggingSettings:userNotFound', {});
551
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
565
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
552
566
  return;
553
567
  }
554
568
  const { successful, returnMessage } = await adminCore.updateServerLoggingSettings({ user, additionalFieldValues: req.body.additionalFieldValues });
@@ -556,7 +570,7 @@ function createCoreRouter() {
556
570
  success = true;
557
571
  }
558
572
  catch (e) {
559
- console.log(`${e.stack}`);
573
+ logger.error('Set server logging settings failed', { stack: e.stack });
560
574
  tracer?.traceError('setServerLoggingSettings:error', e);
561
575
  res.status(400).send(tracer ? tracer.wrapResponse({ successful: false, returnMessage: { messageType: 'warning', message: 'Server logging settings update failed', ttl: 5000 } }) : { successful: false, returnMessage: { messageType: 'warning', message: 'Server logging settings update failed', ttl: 5000 } });
562
576
  success = false;
@@ -584,15 +598,15 @@ function createCoreRouter() {
584
598
  const rcAccountId = req.query.rcAccountId;
585
599
  if (rcAccessToken || rcAccountId) {
586
600
  const userSettings = await userCore.getUserSettingsByAdmin({ rcAccessToken, rcAccountId });
587
- res.status(200).send(tracer ? tracer.wrapResponse({ userSettings }) : { userSettings });
601
+ res.status(200).send(tracer ? tracer.wrapResponse(userSettings) : userSettings);
588
602
  }
589
603
  else {
590
604
  tracer?.trace('getUserSettingsByAdmin:noRcAccessTokenOrRcAccountId', {});
591
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Cannot find rc user login' }) : { error: 'Cannot find rc user login' });
605
+ res.status(400).send(tracer ? tracer.wrapResponse('Cannot find rc user login') : 'Cannot find rc user login');
592
606
  }
593
607
  }
594
608
  catch (e) {
595
- console.log(`${e.stack}`);
609
+ logger.error('Get user preload settings failed', { stack: e.stack });
596
610
  tracer?.traceError('getUserSettingsByAdmin:error', e);
597
611
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
598
612
  }
@@ -613,7 +627,7 @@ function createCoreRouter() {
613
627
  const user = await UserModel.findByPk(unAuthData?.id);
614
628
  if (!user) {
615
629
  tracer?.trace('getUserSettings:userNotFound', {});
616
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
630
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
617
631
  return;
618
632
  }
619
633
  else {
@@ -621,17 +635,17 @@ function createCoreRouter() {
621
635
  const rcAccountId = req.query.rcAccountId;
622
636
  const userSettings = await userCore.getUserSettings({ user, rcAccessToken, rcAccountId });
623
637
  success = true;
624
- res.status(200).send(tracer ? tracer.wrapResponse({ userSettings }) : { userSettings });
638
+ res.status(200).send(tracer ? tracer.wrapResponse(userSettings) : userSettings);
625
639
  }
626
640
  }
627
641
  else {
628
642
  success = false;
629
643
  tracer?.trace('getUserSettings:noToken', {});
630
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
644
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
631
645
  }
632
646
  }
633
647
  catch (e) {
634
- console.log(`platform: ${platformName} \n${e.stack}`);
648
+ logger.error('Get user settings failed', { platform: platformName, stack: e.stack });
635
649
  tracer?.traceError('getUserSettings:error', e, { platform: platformName });
636
650
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
637
651
  }
@@ -664,13 +678,13 @@ function createCoreRouter() {
664
678
  platformName = unAuthData?.platform;
665
679
  if (!platformName) {
666
680
  tracer?.trace('setUserSettings:unknownPlatform', {});
667
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Unknown platform' }) : { error: 'Unknown platform' });
681
+ res.status(400).send(tracer ? tracer.wrapResponse('Unknown platform') : 'Unknown platform');
668
682
  return;
669
683
  }
670
684
  const user = await UserModel.findByPk(unAuthData?.id);
671
685
  if (!user) {
672
686
  tracer?.trace('setUserSettings:userNotFound', {});
673
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
687
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
674
688
  return;
675
689
  }
676
690
  const { userSettings } = await userCore.updateUserSettings({ user, userSettings: req.body.userSettings, platformName });
@@ -679,12 +693,12 @@ function createCoreRouter() {
679
693
  }
680
694
  else {
681
695
  tracer?.trace('setUserSettings:noToken', {});
682
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
696
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
683
697
  success = false;
684
698
  }
685
699
  }
686
700
  catch (e) {
687
- console.log(`platform: ${platformName} \n${e.stack}`);
701
+ logger.error('Set user settings failed', { platform: platformName, stack: e.stack });
688
702
  tracer?.traceError('setUserSettings:error', e, { platform: platformName });
689
703
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
690
704
  }
@@ -713,18 +727,18 @@ function createCoreRouter() {
713
727
  const user = await UserModel.findByPk(unAuthData?.id);
714
728
  if (!user) {
715
729
  tracer?.trace('hostname:userNotFound', {});
716
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
730
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
717
731
  return;
718
732
  }
719
- res.status(200).send(tracer ? tracer.wrapResponse({ hostname: user.hostname }) : { hostname: user.hostname });
733
+ res.status(200).send(tracer ? tracer.wrapResponse(user.hostname) : user.hostname);
720
734
  }
721
735
  else {
722
736
  tracer?.trace('hostname:noToken', {});
723
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
737
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
724
738
  }
725
739
  }
726
740
  catch (e) {
727
- console.log(`${e.stack}`);
741
+ logger.error('Get hostname failed', { stack: e.stack });
728
742
  tracer?.traceError('hostname:error', e);
729
743
  res.status(500).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
730
744
  }
@@ -735,21 +749,32 @@ function createCoreRouter() {
735
749
  tracer?.trace('oauth-callback:start', { query: req.query });
736
750
  let platformName = null;
737
751
  let success = false;
752
+ let sessionId = null;
738
753
  const { hashedExtensionId, hashedAccountId, userAgent, ip, author, eventAddedVia } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
739
754
  try {
740
755
  if (!req.query?.callbackUri || req.query.callbackUri === 'undefined') {
741
- tracer?.trace('oauth-callback:missingCallbackUri', {});
742
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Missing callbackUri' }) : { error: 'Missing callbackUri' });
743
- return;
756
+ // case: from mcp
757
+ if (req.query.code) {
758
+ // eslint-disable-next-line no-param-reassign
759
+ req.query.callbackUri = `${process.env.APP_SERVER}/oauth-callback?code=${req.query.code}`;
760
+ }
761
+ else {
762
+ tracer?.trace('oauth-callback:missingCallbackUri', {});
763
+ res.status(400).send(tracer ? tracer.wrapResponse('Missing callbackUri') : 'Missing callbackUri');
764
+ return;
765
+ }
744
766
  }
745
- platformName = req.query.state ?
746
- req.query.state.split('platform=')[1] :
747
- decodeURIComponent(req.originalUrl).split('state=')[1].split('&')[0].split('platform=')[1];
748
- const hostname = req.query.hostname;
767
+ const state = new URL(req.query.callbackUri).searchParams.get('state') ?? req.query.state;
768
+ const stateParams = new URLSearchParams(state ? decodeURIComponent(state) : '');
769
+ platformName = stateParams.get('platform');
770
+ // Extract mcp auth sessionId if present
771
+ sessionId = stateParams?.get('sessionId');
772
+ const isFromMCP = !!sessionId;
773
+ const hostname = req.query.hostname ?? stateParams.get('hostname');
749
774
  const tokenUrl = req.query.tokenUrl;
750
775
  if (!platformName) {
751
776
  tracer?.trace('oauth-callback:missingPlatformName', {});
752
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Missing platform name' }) : { error: 'Missing platform name' });
777
+ res.status(400).send(tracer ? tracer.wrapResponse('Missing platform name') : 'Missing platform name');
753
778
  return;
754
779
  }
755
780
  const hasAuthCodeInCallbackUri = req.query.callbackUri.includes('code=');
@@ -761,29 +786,53 @@ function createCoreRouter() {
761
786
  platform: platformName,
762
787
  hostname,
763
788
  tokenUrl,
764
- callbackUri: req.query.callbackUri,
765
- apiUrl: req.query.apiUrl,
766
- username: req.query.username,
767
789
  query: req.query,
768
- proxyId: req.query.proxyId
790
+ proxyId: req.query.proxyId,
791
+ isFromMCP
769
792
  });
770
793
  if (userInfo) {
771
794
  const jwtToken = jwt.generateJwt({
772
795
  id: userInfo.id.toString(),
773
796
  platform: platformName
774
797
  });
775
- res.status(200).send(tracer ? tracer.wrapResponse({ jwtToken, name: userInfo.name, returnMessage }) : { jwtToken, name: userInfo.name, returnMessage });
776
- success = true;
798
+ // Store in session if sessionId exists (MCP flow)
799
+ if (isFromMCP) {
800
+ await updateAuthSession(sessionId, {
801
+ status: 'completed',
802
+ jwtToken,
803
+ userInfo: {
804
+ id: userInfo.id,
805
+ name: userInfo.name
806
+ }
807
+ });
808
+ res.status(200).send("Authentication successful. Please go back to AI Agent and confirm it.");
809
+ success = true;
810
+ }
811
+ else {
812
+ res.status(200).send(tracer ? tracer.wrapResponse({ jwtToken, name: userInfo.name, returnMessage }) : { jwtToken, name: userInfo.name, returnMessage });
813
+ success = true;
814
+ }
777
815
  }
778
816
  else {
779
817
  res.status(200).send(tracer ? tracer.wrapResponse({ returnMessage }) : { returnMessage });
780
- success = false;
818
+ await updateAuthSession(sessionId, {
819
+ status: 'failed',
820
+ errorMessage: returnMessage?.message || 'Authentication failed'
821
+ });
781
822
  }
823
+ success = false;
782
824
  }
783
825
  catch (e) {
784
- console.log(`platform: ${platformName} \n${e.stack}`);
826
+ logger.error('OAuth callback failed', { platform: platformName, stack: e.stack });
785
827
  tracer?.traceError('oauth-callback:error', e, { platform: platformName });
786
828
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
829
+ if (sessionId) {
830
+ await updateAuthSession(sessionId, {
831
+ status: 'failed',
832
+ errorMessage: e.message || e.toString()
833
+ });
834
+ }
835
+
787
836
  success = false;
788
837
  }
789
838
  const requestEndTime = new Date().getTime();
@@ -800,7 +849,7 @@ function createCoreRouter() {
800
849
  author,
801
850
  eventAddedVia
802
851
  });
803
- })
852
+ });
804
853
  router.post('/apiKeyLogin', async function (req, res) {
805
854
  const requestStartTime = new Date().getTime();
806
855
  const tracer = req.headers['is-debug'] === 'true' ? DebugTracer.fromRequest(req) : null;
@@ -817,12 +866,12 @@ function createCoreRouter() {
817
866
  const additionalInfo = req.body.additionalInfo;
818
867
  if (!platform) {
819
868
  tracer?.trace('apiKeyLogin:missingPlatform', {});
820
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Missing platform name' }) : { error: 'Missing platform name' });
869
+ res.status(400).send(tracer ? tracer.wrapResponse('Missing platform name') : 'Missing platform name');
821
870
  return;
822
871
  }
823
872
  if (!apiKey) {
824
873
  tracer?.trace('apiKeyLogin:missingApiKey', {});
825
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Missing api key' }) : { error: 'Missing api key' });
874
+ res.status(400).send(tracer ? tracer.wrapResponse('Missing api key') : 'Missing api key');
826
875
  return;
827
876
  }
828
877
  const { userInfo, returnMessage } = await authCore.onApiKeyLogin({ platform, hostname, apiKey, proxyId, additionalInfo });
@@ -840,7 +889,7 @@ function createCoreRouter() {
840
889
  }
841
890
  }
842
891
  catch (e) {
843
- console.log(`platform: ${platformName} \n${e.stack}`);
892
+ logger.error('API key login failed', { platform: platformName, stack: e.stack });
844
893
  tracer?.traceError('apiKeyLogin:error', e, { platform: platformName });
845
894
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
846
895
  success = false;
@@ -875,22 +924,22 @@ function createCoreRouter() {
875
924
  const userToLogout = await UserModel.findByPk(unAuthData?.id);
876
925
  if (!userToLogout) {
877
926
  tracer?.trace('unAuthorize:userNotFound', {});
878
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
927
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
879
928
  return;
880
929
  }
881
930
  const platformModule = connectorRegistry.getConnector(unAuthData?.platform ?? 'Unknown');
882
931
  const { returnMessage } = await platformModule.unAuthorize({ user: userToLogout });
883
- res.status(200).send(tracer ? tracer.wrapResponse({ returnMessage }) : { returnMessage });
932
+ res.status(200).send(tracer ? tracer.wrapResponse(returnMessage) : returnMessage);
884
933
  success = true;
885
934
  }
886
935
  else {
887
936
  tracer?.trace('unAuthorize:noToken', {});
888
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
937
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
889
938
  success = false;
890
939
  }
891
940
  }
892
941
  catch (e) {
893
- console.log(`platform: ${platformName} \n${e.stack}`);
942
+ logger.error('Unauthorize failed', { platform: platformName, stack: e.stack });
894
943
  tracer?.traceError('unAuthorize:error', e, { platform: platformName });
895
944
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
896
945
  success = false;
@@ -919,7 +968,7 @@ function createCoreRouter() {
919
968
  res.status(200).send(tracer ? tracer.wrapResponse({ extensionId, accountId }) : { extensionId, accountId });
920
969
  }
921
970
  catch (e) {
922
- console.log(`${e.stack}`);
971
+ logger.error('Get user info hash failed', { stack: e.stack });
923
972
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
924
973
  tracer?.traceError('userInfoHash:error', e);
925
974
  }
@@ -941,12 +990,20 @@ function createCoreRouter() {
941
990
  tracer?.trace('findContact:jwtDecoded', { decodedToken });
942
991
  if (!decodedToken) {
943
992
  tracer?.trace('findContact:invalidToken', {});
944
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
993
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
945
994
  return;
946
995
  }
947
996
  const { id: userId, platform } = decodedToken;
948
997
  platformName = platform;
949
- const { successful, returnMessage, contact, extraDataTracking } = await contactCore.findContact({ platform, userId, phoneNumber: req.query.phoneNumber.replace(' ', '+'), overridingFormat: req.query.overridingFormat, isExtension: req.query?.isExtension ?? false, tracer });
998
+ const { successful, returnMessage, contact, extraDataTracking } = await contactCore.findContact({
999
+ platform,
1000
+ userId,
1001
+ phoneNumber: req.query.phoneNumber.replace(' ', '+'),
1002
+ overridingFormat: req.query.overridingFormat,
1003
+ isExtension: req.query?.isExtension ?? false,
1004
+ tracer,
1005
+ isForceRefreshAccountData: req.query?.isForceRefreshAccountData === 'true'
1006
+ });
950
1007
  tracer?.trace('findContact:result', { successful, returnMessage, contact });
951
1008
  res.status(200).send(tracer ? tracer.wrapResponse({ successful, returnMessage, contact }) : { successful, returnMessage, contact });
952
1009
  if (successful) {
@@ -960,12 +1017,12 @@ function createCoreRouter() {
960
1017
  }
961
1018
  else {
962
1019
  tracer?.trace('findContact:noToken', {});
963
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1020
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
964
1021
  success = false;
965
1022
  }
966
1023
  }
967
1024
  catch (e) {
968
- console.log(`platform: ${platformName} \n${e.stack}`);
1025
+ logger.error('Find contact failed', { platform: platformName, stack: e.stack });
969
1026
  tracer?.traceError('findContact:error', e, { platform: platformName });
970
1027
  extraData.statusCode = e.response?.status ?? 'unknown';
971
1028
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
@@ -1004,7 +1061,7 @@ function createCoreRouter() {
1004
1061
  const decodedToken = jwt.decodeJwt(jwtToken);
1005
1062
  if (!decodedToken) {
1006
1063
  tracer?.trace('createContact:invalidToken', {});
1007
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1064
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1008
1065
  return;
1009
1066
  }
1010
1067
  const { id: userId, platform } = decodedToken;
@@ -1018,12 +1075,12 @@ function createCoreRouter() {
1018
1075
  }
1019
1076
  else {
1020
1077
  tracer?.trace('createContact:noToken', {});
1021
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1078
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1022
1079
  success = false;
1023
1080
  }
1024
1081
  }
1025
1082
  catch (e) {
1026
- console.log(`platform: ${platformName} \n${e.stack}`);
1083
+ logger.error('Create contact failed', { platform: platformName, stack: e.stack });
1027
1084
  tracer?.traceError('createContact:error', e, { platform: platformName });
1028
1085
  extraData.statusCode = e.response?.status ?? 'unknown';
1029
1086
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
@@ -1061,12 +1118,12 @@ function createCoreRouter() {
1061
1118
  const decodedToken = jwt.decodeJwt(jwtToken);
1062
1119
  if (!decodedToken) {
1063
1120
  tracer?.trace('saveNoteCache:invalidToken', {});
1064
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1121
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1065
1122
  return;
1066
1123
  }
1067
1124
  const { id: userId, platform } = decodedToken;
1068
1125
  platformName = platform;
1069
- const { successful, returnMessage, extraDataTracking } = await logCore.saveNoteCache({ sessionId: req.body.sessionId, note: req.body.note });
1126
+ const { successful, returnMessage, extraDataTracking } = await logCore.saveNoteCache({ platform, userId, sessionId: req.body.sessionId, note: req.body.note });
1070
1127
  res.status(200).send(tracer ? tracer.wrapResponse({ successful, returnMessage }) : { successful, returnMessage });
1071
1128
  success = true;
1072
1129
  if (extraDataTracking) {
@@ -1074,7 +1131,7 @@ function createCoreRouter() {
1074
1131
  }
1075
1132
  }
1076
1133
  } catch (e) {
1077
- console.log(`platform: ${platformName} \n${e.stack}`);
1134
+ logger.error('Save note cache failed', { platform: platformName, stack: e.stack });
1078
1135
  tracer?.traceError('saveNoteCache:error', e, { platform: platformName });
1079
1136
  extraData.statusCode = e.response?.status ?? 'unknown';
1080
1137
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
@@ -1088,6 +1145,14 @@ function createCoreRouter() {
1088
1145
  accountId: hashedAccountId,
1089
1146
  extensionId: hashedExtensionId,
1090
1147
  success,
1148
+ requestDuration: (requestEndTime - requestStartTime) / 1000,
1149
+ userAgent,
1150
+ ip,
1151
+ author,
1152
+ eventAddedVia,
1153
+ extras: {
1154
+ ...extraData
1155
+ }
1091
1156
  });
1092
1157
  })
1093
1158
  router.get('/callLog', async function (req, res) {
@@ -1104,7 +1169,7 @@ function createCoreRouter() {
1104
1169
  const decodedToken = jwt.decodeJwt(jwtToken);
1105
1170
  if (!decodedToken) {
1106
1171
  tracer?.trace('getCallLog:invalidToken', {});
1107
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1172
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1108
1173
  return;
1109
1174
  }
1110
1175
  const { id: userId, platform } = decodedToken;
@@ -1119,12 +1184,12 @@ function createCoreRouter() {
1119
1184
  }
1120
1185
  else {
1121
1186
  tracer?.trace('getCallLog:noToken', {});
1122
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1187
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1123
1188
  success = false;
1124
1189
  }
1125
1190
  }
1126
1191
  catch (e) {
1127
- console.log(`platform: ${platformName} \n${e.stack}`);
1192
+ logger.error('Get call log failed', { platform: platformName, stack: e.stack });
1128
1193
  extraData.statusCode = e.response?.status ?? 'unknown';
1129
1194
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1130
1195
  tracer?.traceError('getCallLog:error', e, { platform: platformName });
@@ -1162,7 +1227,7 @@ function createCoreRouter() {
1162
1227
  const decodedToken = jwt.decodeJwt(jwtToken);
1163
1228
  if (!decodedToken) {
1164
1229
  tracer?.trace('createCallLog:invalidToken', {});
1165
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1230
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1166
1231
  return;
1167
1232
  }
1168
1233
  const { id: userId, platform } = decodedToken;
@@ -1176,12 +1241,12 @@ function createCoreRouter() {
1176
1241
  }
1177
1242
  else {
1178
1243
  tracer?.trace('createCallLog:noToken', {});
1179
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1244
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1180
1245
  success = false;
1181
1246
  }
1182
1247
  }
1183
1248
  catch (e) {
1184
- console.log(`platform: ${platformName} \n${e.stack}`);
1249
+ logger.error('Create call log failed', { platform: platformName, stack: e.stack });
1185
1250
  extraData.statusCode = e.response?.status ?? 'unknown';
1186
1251
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1187
1252
  tracer?.traceError('createCallLog:error', e, { platform: platformName });
@@ -1219,7 +1284,7 @@ function createCoreRouter() {
1219
1284
  const decodedToken = jwt.decodeJwt(jwtToken);
1220
1285
  if (!decodedToken) {
1221
1286
  tracer?.trace('updateCallLog:invalidToken', {});
1222
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1287
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1223
1288
  return;
1224
1289
  }
1225
1290
  const { id: userId, platform } = decodedToken;
@@ -1233,12 +1298,12 @@ function createCoreRouter() {
1233
1298
  }
1234
1299
  else {
1235
1300
  tracer?.trace('updateCallLog:noToken', {});
1236
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1301
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1237
1302
  success = false;
1238
1303
  }
1239
1304
  }
1240
1305
  catch (e) {
1241
- console.log(`platform: ${platformName} \n${e.stack}`);
1306
+ logger.error('Update call log failed', { platform: platformName, stack: e.stack });
1242
1307
  extraData.statusCode = e.response?.status ?? 'unknown';
1243
1308
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1244
1309
  tracer?.traceError('updateCallLog:error', e, { platform: platformName });
@@ -1277,7 +1342,7 @@ function createCoreRouter() {
1277
1342
  platformName = platform;
1278
1343
  if (!userId) {
1279
1344
  tracer?.trace('upsertCallDisposition:invalidToken', {});
1280
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1345
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1281
1346
  return;
1282
1347
  }
1283
1348
  const { successful, returnMessage, extraDataTracking } = await dispositionCore.upsertCallDisposition({
@@ -1295,12 +1360,12 @@ function createCoreRouter() {
1295
1360
  }
1296
1361
  else {
1297
1362
  tracer?.trace('upsertCallDisposition:noToken', {});
1298
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1363
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1299
1364
  success = false;
1300
1365
  }
1301
1366
  }
1302
1367
  catch (e) {
1303
- console.log(`platform: ${platformName} \n${e.stack}`);
1368
+ logger.error('Upsert call disposition failed', { platform: platformName, stack: e.stack });
1304
1369
  extraData.statusCode = e.response?.status ?? 'unknown';
1305
1370
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1306
1371
  tracer?.traceError('upsertCallDisposition:error', e, { platform: platformName });
@@ -1339,7 +1404,7 @@ function createCoreRouter() {
1339
1404
  const decodedToken = jwt.decodeJwt(jwtToken);
1340
1405
  if (!decodedToken) {
1341
1406
  tracer?.trace('createMessageLog:invalidToken', {});
1342
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1407
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1343
1408
  return;
1344
1409
  }
1345
1410
  const { id: userId, platform } = decodedToken;
@@ -1353,12 +1418,12 @@ function createCoreRouter() {
1353
1418
  }
1354
1419
  else {
1355
1420
  tracer?.trace('createMessageLog:noToken', {});
1356
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1421
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1357
1422
  success = false;
1358
1423
  }
1359
1424
  }
1360
1425
  catch (e) {
1361
- console.log(`platform: ${platformName} \n${e.stack}`);
1426
+ logger.error('Create message log failed', { platform: platformName, stack: e.stack });
1362
1427
  statusCode = e.response?.status ?? 'unknown';
1363
1428
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1364
1429
  tracer?.traceError('createMessageLog:error', e, { platform: platformName });
@@ -1395,14 +1460,19 @@ function createCoreRouter() {
1395
1460
  const jwtToken = req.query.jwtToken;
1396
1461
  if (!jwtToken) {
1397
1462
  tracer?.trace('scheduleCallDown:noToken', {});
1398
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1463
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1399
1464
  return;
1400
1465
  }
1401
- const { id } = await calldown.schedule({ jwtToken, rcAccessToken: req.query.rcAccessToken, body: req.body });
1402
- success = true;
1403
- res.status(200).send(tracer ? tracer.wrapResponse({ successful: true, id }) : { successful: true, id });
1466
+ try {
1467
+ const { id } = await calldown.schedule({ jwtToken, rcAccessToken: req.query.rcAccessToken, body: req.body });
1468
+ success = true;
1469
+ res.status(200).send(tracer ? tracer.wrapResponse({ successful: true, id }) : { successful: true, id });
1470
+ }
1471
+ catch (e) {
1472
+ return handleDatabaseError(e, 'Error scheduling call down');
1473
+ }
1404
1474
  } catch (e) {
1405
- console.log(`platform: ${platformName} \n${e.stack}`);
1475
+ logger.error('Schedule call down failed', { platform: platformName, stack: e.stack });
1406
1476
  statusCode = e.response?.status ?? 'unknown';
1407
1477
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1408
1478
  tracer?.traceError('scheduleCallDown:error', e, { platform: platformName });
@@ -1438,14 +1508,14 @@ function createCoreRouter() {
1438
1508
  const jwtToken = req.query.jwtToken;
1439
1509
  if (!jwtToken) {
1440
1510
  tracer?.trace('getCallDownList:noToken', {});
1441
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1511
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1442
1512
  return;
1443
1513
  }
1444
1514
  const { items } = await calldown.list({ jwtToken, status: req.query.status });
1445
1515
  success = true;
1446
1516
  res.status(200).send(tracer ? tracer.wrapResponse({ successful: true, items }) : { successful: true, items });
1447
1517
  } catch (e) {
1448
- console.log(`platform: ${platformName} \n${e.stack}`);
1518
+ logger.error('Get call down list failed', { platform: platformName, stack: e.stack });
1449
1519
  statusCode = e.response?.status ?? 'unknown';
1450
1520
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1451
1521
  tracer?.traceError('getCallDownList:error', e, { platform: platformName });
@@ -1480,20 +1550,20 @@ function createCoreRouter() {
1480
1550
  const id = req.query.id;
1481
1551
  if (!jwtToken) {
1482
1552
  tracer?.trace('deleteCallDownItem:noToken', {});
1483
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1553
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1484
1554
  return;
1485
1555
  }
1486
1556
  const rid = req.params.id || id;
1487
1557
  if (!rid) {
1488
1558
  tracer?.trace('deleteCallDownItem:missingId', {});
1489
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Missing id' }) : { error: 'Missing id' });
1559
+ res.status(400).send(tracer ? tracer.wrapResponse('Missing id') : 'Missing id');
1490
1560
  return;
1491
1561
  }
1492
1562
  await calldown.remove({ jwtToken, id: rid });
1493
1563
  success = true;
1494
1564
  res.status(200).send(tracer ? tracer.wrapResponse({ successful: true }) : { successful: true });
1495
1565
  } catch (e) {
1496
- console.log(`platform: ${platformName} \n${e.stack}`);
1566
+ logger.error('Delete call down item failed', { platform: platformName, stack: e.stack });
1497
1567
  statusCode = e.response?.status ?? 'unknown';
1498
1568
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1499
1569
  tracer?.traceError('deleteCallDownItem:error', e, { platform: platformName });
@@ -1527,20 +1597,20 @@ function createCoreRouter() {
1527
1597
  const jwtToken = req.query.jwtToken;
1528
1598
  if (!jwtToken) {
1529
1599
  tracer?.trace('markCallDownCalled:noToken', {});
1530
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1600
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1531
1601
  return;
1532
1602
  }
1533
1603
  const id = req.params.id || req.body?.id;
1534
1604
  if (!id) {
1535
1605
  tracer?.trace('markCallDownCalled:missingId', {});
1536
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Missing id' }) : { error: 'Missing id' });
1606
+ res.status(400).send(tracer ? tracer.wrapResponse('Missing id') : 'Missing id');
1537
1607
  return;
1538
1608
  }
1539
1609
  await calldown.update({ jwtToken, id, updateData: req.body });
1540
1610
  success = true;
1541
1611
  res.status(200).send(tracer ? tracer.wrapResponse({ successful: true }) : { successful: true });
1542
1612
  } catch (e) {
1543
- console.log(`platform: ${platformName} \n${e.stack}`);
1613
+ logger.error('Mark call down called failed', { platform: platformName, stack: e.stack });
1544
1614
  statusCode = e.response?.status ?? 'unknown';
1545
1615
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1546
1616
  tracer?.traceError('markCallDownCalled:error', e, { platform: platformName });
@@ -1568,7 +1638,6 @@ function createCoreRouter() {
1568
1638
  tracer?.trace('contactSearchByName:start', { query: req.query });
1569
1639
  let platformName = null;
1570
1640
  let success = false;
1571
- let resultCount = 0;
1572
1641
  let statusCode = 200;
1573
1642
  const { hashedExtensionId, hashedAccountId, userAgent, ip, author } = getAnalyticsVariablesInReqHeaders({ headers: req.headers })
1574
1643
  try {
@@ -1582,13 +1651,13 @@ function createCoreRouter() {
1582
1651
  }
1583
1652
  else {
1584
1653
  tracer?.trace('contactSearchByName:noToken', {});
1585
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Please go to Settings and authorize CRM platform' }) : { error: 'Please go to Settings and authorize CRM platform' });
1654
+ res.status(400).send(tracer ? tracer.wrapResponse('Please go to Settings and authorize CRM platform') : 'Please go to Settings and authorize CRM platform');
1586
1655
  success = false;
1587
1656
  }
1588
1657
 
1589
1658
  }
1590
1659
  catch (e) {
1591
- console.log(`platform: ${platformName} \n${e.stack}`);
1660
+ logger.error('Contact search by name failed', { platform: platformName, stack: e.stack });
1592
1661
  statusCode = e.response?.status ?? 'unknown';
1593
1662
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1594
1663
  tracer?.traceError('contactSearchByName:error', e, { platform: platformName });
@@ -1625,7 +1694,7 @@ function createCoreRouter() {
1625
1694
  const user = await UserModel.findByPk(unAuthData?.id);
1626
1695
  if (!user) {
1627
1696
  tracer?.trace('getAdminReport:userNotFound', {});
1628
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
1697
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
1629
1698
  return;
1630
1699
  }
1631
1700
  const report = await adminCore.getAdminReport({ rcAccountId: user.rcAccountId, timezone: req.query.timezone, timeFrom: req.query.timeFrom, timeTo: req.query.timeTo, groupBy: req.query.groupBy });
@@ -1634,11 +1703,11 @@ function createCoreRouter() {
1634
1703
  return;
1635
1704
  }
1636
1705
  tracer?.trace('getAdminReport:invalidRequest', {});
1637
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Invalid request' }) : { error: 'Invalid request' });
1706
+ res.status(400).send(tracer ? tracer.wrapResponse('Invalid request') : 'Invalid request');
1638
1707
  success = false;
1639
1708
  }
1640
1709
  catch (e) {
1641
- console.log(`${e.stack}`);
1710
+ logger.error('Get admin report failed', { stack: e.stack });
1642
1711
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1643
1712
  tracer?.traceError('getAdminReport:error', e, { platform: platformName });
1644
1713
  }
@@ -1671,7 +1740,7 @@ function createCoreRouter() {
1671
1740
  const user = await UserModel.findByPk(unAuthData?.id);
1672
1741
  if (!user) {
1673
1742
  tracer?.trace('getUserReport:userNotFound', {});
1674
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
1743
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
1675
1744
  return;
1676
1745
  }
1677
1746
  const report = await adminCore.getUserReport({ rcAccountId: user.rcAccountId, rcExtensionId: req.query.rcExtensionId, timezone: req.query.timezone, timeFrom: req.query.timeFrom, timeTo: req.query.timeTo });
@@ -1679,11 +1748,11 @@ function createCoreRouter() {
1679
1748
  return;
1680
1749
  }
1681
1750
  tracer?.trace('getUserReport:invalidRequest', {});
1682
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Invalid request' }) : { error: 'Invalid request' });
1751
+ res.status(400).send(tracer ? tracer.wrapResponse('Invalid request') : 'Invalid request');
1683
1752
  success = false;
1684
1753
  }
1685
1754
  catch (e) {
1686
- console.log(`${e.stack}`);
1755
+ logger.error('Get user report failed', { stack: e.stack });
1687
1756
  res.status(400).send(tracer ? tracer.wrapResponse({ error: e.message || e }) : { error: e.message || e });
1688
1757
  tracer?.traceError('getUserReport:error', e, { platform: platformName });
1689
1758
  }
@@ -1712,7 +1781,7 @@ function createCoreRouter() {
1712
1781
  const user = await UserModel.findByPk(unAuthData?.id);
1713
1782
  if (!user) {
1714
1783
  tracer?.trace('onRingcentralOAuthCallback:userNotFound', {});
1715
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'User not found' }) : { error: 'User not found' });
1784
+ res.status(400).send(tracer ? tracer.wrapResponse('User not found') : 'User not found');
1716
1785
  return;
1717
1786
  }
1718
1787
  await authCore.onRingcentralOAuthCallback({ code, rcAccountId: user.rcAccountId });
@@ -1720,7 +1789,7 @@ function createCoreRouter() {
1720
1789
  return;
1721
1790
  }
1722
1791
  tracer?.trace('onRingcentralOAuthCallback:invalidRequest', {});
1723
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Invalid request' }) : { error: 'Invalid request' });
1792
+ res.status(400).send(tracer ? tracer.wrapResponse('Invalid request') : 'Invalid request');
1724
1793
  });
1725
1794
  router.get('/debug/report/url', async function (req, res) {
1726
1795
  const requestStartTime = new Date().getTime();
@@ -1738,7 +1807,7 @@ function createCoreRouter() {
1738
1807
  }
1739
1808
  else {
1740
1809
  tracer?.trace('getErrorLogReportURL:invalidRequest', {});
1741
- res.status(400).send(tracer ? tracer.wrapResponse({ error: 'Invalid request' }) : { error: 'Invalid request' });
1810
+ res.status(400).send(tracer ? tracer.wrapResponse('Invalid request') : 'Invalid request');
1742
1811
  success = false;
1743
1812
  }
1744
1813
  const requestEndTime = new Date().getTime();
@@ -1809,6 +1878,116 @@ function createCoreRouter() {
1809
1878
  }
1810
1879
  });
1811
1880
  }
1881
+ // --- METADATA ENDPOINT 1: Resource Metadata ---
1882
+ // Tells the client "I am protected" and "Here is who protects me"
1883
+ router.get('/.well-known/oauth-protected-resource', (req, res) => {
1884
+ res.json({
1885
+ resource: process.env.APP_SERVER,
1886
+ // CHANGE THIS: Point to your own server so the client fetches YOUR metadata next
1887
+ authorization_servers: [process.env.APP_SERVER],
1888
+ scopes_supported: ["ReadAccounts"]
1889
+ });
1890
+ });
1891
+
1892
+ // --- METADATA ENDPOINT 2: Auth Server Metadata ---
1893
+ // Usually, you can redirect to your provider's configuration.
1894
+ // If your provider supports OIDC discovery, this is often sufficient.
1895
+ router.get('/.well-known/oauth-authorization-server', (req, res) => {
1896
+ res.json({
1897
+ issuer: process.env.APP_SERVER,
1898
+ registration_endpoint: `${process.env.APP_SERVER}/oauth/register`,
1899
+
1900
+ // CHANGE THIS: Don't point to RingCentral. Point to your own Shim.
1901
+ authorization_endpoint: `${process.env.APP_SERVER}/oauth/authorize_shim`,
1902
+
1903
+ // Keep the token endpoint pointing to RingCentral (that usually works fine)
1904
+ token_endpoint: `${process.env.RINGCENTRAL_SERVER}/restapi/oauth/token`,
1905
+ token_endpoint_auth_methods_supported: ["client_secret_basic"],
1906
+ response_types_supported: ["code"]
1907
+ });
1908
+ });
1909
+
1910
+ router.get('/oauth/authorize_shim', (req, res) => {
1911
+ // 1. Get the parameters ChatGPT sent us
1912
+ const { response_type, client_id, redirect_uri, state, scope } = req.query;
1913
+
1914
+ // 2. Rebuild the query string for RingCentral
1915
+ // We explicitly LEAVE OUT 'resource' and any other junk
1916
+ const params = new URLSearchParams({
1917
+ response_type,
1918
+ client_id, // This will be your RC Client ID (since ChatGPT got it from /register)
1919
+ redirect_uri,
1920
+ state,
1921
+ scope
1922
+ });
1923
+
1924
+ // 3. Redirect the user's browser to the REAL RingCentral URL
1925
+ const rcUrl = `${process.env.RINGCENTRAL_SERVER}/restapi/oauth/authorize?${params.toString()}`;
1926
+
1927
+ console.log("Proxying OAuth request to:", rcUrl); // Helpful for debugging
1928
+ res.redirect(rcUrl);
1929
+ });
1930
+
1931
+ router.post('/oauth/register', (req, res) => {
1932
+ // The MCP client calls this to get credentials.
1933
+ // We simply return our hardcoded RingCentral app credentials.
1934
+ res.json({
1935
+ client_id: process.env.RINGCENTRAL_CLIENT_ID,
1936
+ client_secret: process.env.RINGCENTRAL_CLIENT_SECRET
1937
+ });
1938
+ });
1939
+
1940
+ router.use('/mcp', (req, res, next) => {// LOG EVERYTHING
1941
+ console.log(`[${req.method}] /mcp`);
1942
+ console.log("Headers:", JSON.stringify(req.headers['authorization'] ? "Auth Token Present" : "No Auth"));
1943
+ console.log("Body:", JSON.stringify(req.body));
1944
+ // return next();
1945
+ // Capture the response finish to see the status code
1946
+ res.on('finish', () => {
1947
+ console.log(`[Response] Status: ${res.statusCode}`);
1948
+ console.log(`[Response] data: ${JSON.stringify(res.data)}`);
1949
+ });
1950
+
1951
+ const authHeader = req.headers.authorization;
1952
+ const token = authHeader?.split(' ')[1]; // Remove "Bearer "
1953
+ // Allow the initial connection (GET) and CORS checks (OPTIONS) to pass freely.
1954
+ // We only want to block the actual commands (POST).
1955
+ if (req.method === 'GET' || req.method === 'OPTIONS') {
1956
+ return next();
1957
+ }
1958
+ // SCENARIO 1: No Token provided. Kick off the OAuth flow.
1959
+ if (!token) {
1960
+ res.setHeader('WWW-Authenticate', `Bearer realm="mcp", resource_metadata="${process.env.APP_SERVER}/.well-known/oauth-protected-resource"`);
1961
+ return res.status(401).send();
1962
+ }
1963
+
1964
+ // SCENARIO 2: Token provided. Verify it.
1965
+ try {
1966
+ next();
1967
+ } catch (error) {
1968
+ console.error("Token validation failed:", error.message);
1969
+ // Token is invalid or expired
1970
+ res.setHeader('WWW-Authenticate', `Bearer realm="mcp", resource_metadata="${process.env.APP_SERVER}/.well-known/oauth-protected-resource"`);
1971
+ return res.status(401).send();
1972
+ }
1973
+ });
1974
+ // Handle OPTIONS for CORS preflight
1975
+ router.options('/mcp', (req, res) => {
1976
+ res.setHeader('Access-Control-Allow-Origin', '*');
1977
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
1978
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Accept');
1979
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
1980
+ res.status(200).end();
1981
+ });
1982
+
1983
+ // Dedicated endpoint for all MCP traffic
1984
+ router.post('/mcp', async (req, res) => {
1985
+ // Set CORS headers
1986
+ res.setHeader('Access-Control-Allow-Origin', '*');
1987
+ res.setHeader('Content-Type', 'application/json');
1988
+
1989
+ await mcpHandler.handleMcpRequest(req, res);
1990
+ });
1812
1991
 
1813
1992
  return router;
1814
1993
  }
@@ -1817,6 +1996,15 @@ function createCoreRouter() {
1817
1996
  function createCoreMiddleware() {
1818
1997
  return [
1819
1998
  bodyParser.json(),
1999
+ bodyParser.xml({
2000
+ limit: '50mb',
2001
+ xmlParseOptions: {
2002
+ explicitArray: false,
2003
+ normalize: true,
2004
+ normalizeTags: false,
2005
+ trim: true
2006
+ }
2007
+ }),
1820
2008
  cors({
1821
2009
  methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE']
1822
2010
  })