@tiledesk/tiledesk-server 2.28.1 → 2.29.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.
package/routes/faq_kb.js CHANGED
@@ -19,6 +19,10 @@ var roleChecker = require('../middleware/has-role');
19
19
  const roleConstants = require('../models/roleConstants');
20
20
  const errorCodes = require('../errorCodes');
21
21
  const faq_kb = require('../models/faq_kb');
22
+ const chatbotController = require('../controllers/chatbot.controller');
23
+ const intentsController = require('../controllers/intents.controller');
24
+ const AppError = require('../utils/AppError');
25
+ const asyncHandler = require('../utils/asyncHandler');
22
26
 
23
27
  let chatbot_templates_api_url = process.env.CHATBOT_TEMPLATES_API_URL
24
28
 
@@ -261,57 +265,100 @@ router.put('/:faq_kbid/publish', roleChecker.hasRole('admin'), async (req, res)
261
265
  }
262
266
 
263
267
  const api_url = process.env.API_URL || configGlobal.apiUrl;
264
- winston.debug("fork --> base_url: " + api_url); // check if correct
268
+ winston.debug("fork --> base_url: " + api_url);
265
269
 
266
- let current_project_id = req.projectid;
267
- winston.debug("current project id: " + current_project_id);
268
-
269
- let token = req.headers.authorization;
270
-
271
- let cs = req.app.get('chatbot_service')
270
+ const cs = req.app.get('chatbot_service');
272
271
 
273
272
  try {
274
- // fork(id_faq_kb, api_url, token, project_id)
275
- let forked = await cs.fork(chatbot_id, api_url, token, current_project_id, { forPublish: true });
276
- // winston.debug("forked: ", forked)
277
-
278
- let forkedChatBotId = forked.bot_id;
279
- winston.debug("forkedChatBotId: " + forkedChatBotId);
273
+ const published = await cs.publish({
274
+ id_faq_kb,
275
+ chatbot_id,
276
+ api_url,
277
+ token: req.headers.authorization,
278
+ project_id: req.projectid,
279
+ user_id: req.user.id,
280
+ release_note,
281
+ req
282
+ });
280
283
 
281
- let updatedForkedChabot = await Faq_kb.findByIdAndUpdate(forkedChatBotId, { $unset: { slug: 1 }, trashed: true, root_id: id_faq_kb, release_note: release_note, publishedBy: req.user.id, publishedAt: new Date().getTime() }, { new: true, upsert: true }).exec();
282
- //let updatedForkedChabot = await Faq_kb.findByIdAndUpdate(forkedChatBotId, { $unset: { slug: 1 }, trashed: true, root_id: id_faq_kb, publishedBy: req.user.id, publishedAt: new Date().getTime() }, { new: true }).exec();
283
- winston.debug("updatedForkedChabot: ", updatedForkedChabot);
284
- botEvent.emit('faqbot.update', updatedForkedChabot);
284
+ return res.status(200).send({ message: "Chatbot published successfully", bot_id: published.bot_id });
285
+ } catch (e) {
286
+ winston.error("Error Unable publish chatbot: ", e);
287
+ return res.status(500).send({ success: false, message: "Unable publish chatbot" });
288
+ }
289
+ });
285
290
 
286
- const port = process.env.PORT || '3000';
287
- let TILEBOT_ENDPOINT = process.env.API_ENDPOINT ? process.env.API_ENDPOINT + "/modules/tilebot/ext/" : ("http://localhost:" + port + "/modules/tilebot/ext/");
291
+ router.put('/:faq_kbid/publish/multi', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
288
292
 
289
- if (process.env.TILEBOT_ENDPOINT) {
290
- TILEBOT_ENDPOINT = process.env.TILEBOT_ENDPOINT + "/ext/";
291
- }
292
- winston.info("[RequestService] TILEBOT_ENDPOINT: " + TILEBOT_ENDPOINT);
293
+ const id_project = req.projectid;
294
+ const parent_id = req.params.faq_kbid;
295
+ const chatbots = req.body.chatbots;
296
+ const isValidChatbot = (chatbot) => chatbot && typeof chatbot === 'object' && !Array.isArray(chatbot) && typeof chatbot.id === 'string' && chatbot.id.trim();
293
297
 
294
- let updatedOriginalChabot = await Faq_kb.findByIdAndUpdate(id_faq_kb, { url: TILEBOT_ENDPOINT + forkedChatBotId }, { new: true, upsert: true }).exec();
295
- winston.debug("updatedOriginalChabot: ", updatedOriginalChabot);
298
+ if (!Array.isArray(chatbots) || chatbots.length === 0 || chatbots.some((chatbot) => !isValidChatbot(chatbot))) {
299
+ return res.status(400).send({ success: false, message: "chatbots must be a non-empty array of objects with an id" });
300
+ }
296
301
 
297
- cs.setModified(id_faq_kb, false);
302
+ await chatbotController.getChatbot(parent_id, id_project);
303
+ await chatbotController.assertAreSubagents(
304
+ id_project,
305
+ parent_id,
306
+ chatbots.map((chatbot) => chatbot.id)
307
+ );
298
308
 
299
- botEvent.emit('faqbot.update', updatedOriginalChabot);
309
+ const api_url = process.env.API_URL || configGlobal.apiUrl;
310
+ const token = req.headers.authorization;
311
+ const user_id = req.user.id;
312
+ const release_note = req.body.release_note || "No comment";
313
+ const cs = req.app.get('chatbot_service');
314
+ const results = [];
315
+
316
+ for (const chatbot of chatbots) {
317
+ const id_faq_kb = chatbot.id;
318
+ try {
319
+ const published = await cs.publish({
320
+ id_faq_kb,
321
+ chatbot_id: id_faq_kb,
322
+ api_url,
323
+ token,
324
+ project_id: id_project,
325
+ user_id,
326
+ release_note: chatbot.release_note || release_note,
327
+ req
328
+ });
329
+ results.push({ id: id_faq_kb, success: true, bot_id: published.bot_id });
330
+ } catch (err) {
331
+ const error = err?.response?.data?.message || err?.response?.data?.error || err?.message || "Publish failed";
332
+ winston.error("Error publishing chatbot " + id_faq_kb + ": ", err);
333
+ results.push({ id: id_faq_kb, success: false, error });
334
+ }
335
+ }
300
336
 
301
- botEvent.emit('faqbot.publish', {
302
- req: req,
303
- chatbot: updatedOriginalChabot,
304
- publishedBotId: forkedChatBotId,
305
- release_note: release_note
337
+ const failed = results.filter((result) => !result.success);
338
+ if (failed.length > 0) {
339
+ return res.status(500).send({
340
+ success: false,
341
+ message: "Some chatbots failed to publish",
342
+ results
306
343
  });
307
-
308
- return res.status(200).send({ message: "Chatbot published successfully", bot_id: forkedChatBotId });
309
-
310
- } catch (e) {
311
- winston.error("Error Unable publish chatbot: ", e);
312
- return res.status(500).send({ success: false, message: "Unable publish chatbot" });
313
344
  }
314
- });
345
+
346
+ return res.status(200).send({
347
+ success: true,
348
+ message: "Chatbots published successfully",
349
+ results
350
+ });
351
+ }));
352
+
353
+ router.put('/:faq_kbid/restore/:publish_id', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
354
+ const chatbot = await chatbotController.restoreFromPublished(
355
+ req.params.faq_kbid,
356
+ req.params.publish_id,
357
+ req.projectid,
358
+ req.user.id
359
+ );
360
+ return res.status(200).send(chatbot);
361
+ }));
315
362
 
316
363
  router.put('/:faq_kbid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) {
317
364
 
@@ -459,29 +506,69 @@ router.patch('/:faq_kbid/attributes', roleChecker.hasRoleOrTypes('admin', ['bot'
459
506
 
460
507
  });
461
508
 
462
- router.delete('/:faq_kbid', roleChecker.hasRole('admin'), function (req, res) {
509
+ router.delete('/:faq_kbid', roleChecker.hasRole('admin'), asyncHandler(async function (req, res) {
463
510
 
464
511
  winston.debug(req.body);
465
512
 
466
513
  const chatbot_id = req.params.faq_kbid;
467
514
  const id_project = req.projectid;
468
515
 
469
- Faq_kb.findOneAndDelete({ _id: chatbot_id, id_project: id_project }, function (err, faq_kb) {
470
- if (err) {
471
- return res.status(500).send({ success: false, msg: 'Error deleting object.' });
516
+ let chatbot;
517
+ try {
518
+ chatbot = await Faq_kb.findOne({ _id: chatbot_id, id_project: id_project });
519
+ } catch (err) {
520
+ winston.error("Error getting chatbot: ", err);
521
+ return res.status(500).send({ success: false, msg: 'Error getting chatbot.' });
522
+ }
523
+
524
+ if (!chatbot) {
525
+ return res.status(404).send({
526
+ success: false,
527
+ msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project
528
+ });
529
+ }
530
+
531
+ if (chatbot.subtype === 'subagent') {
532
+ let referencingFaq;
533
+ try {
534
+ referencingFaq = await chatbotController.findSubagentCaller(chatbot, id_project);
535
+ } catch (err) {
536
+ winston.error("Error checking subagent references: ", err);
537
+ return res.status(500).send({ success: false, msg: 'Error checking subagent references.' });
472
538
  }
473
539
 
474
- if (!faq_kb) {
475
- return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
540
+ if (referencingFaq) {
541
+ return res.status(409).send({
542
+ success: false,
543
+ msg: "Cannot delete subagent because it is still referenced by another chatbot",
544
+ error_code: errorCodes.CHATBOT.ERRORS.SUBAGENT_IN_USE,
545
+ referenced_by: {
546
+ id_faq_kb: referencingFaq.id_faq_kb,
547
+ intent_display_name: referencingFaq.intent_display_name
548
+ }
549
+ });
476
550
  }
477
- /**
478
- * WARNING: faq_kb is the operation result, not the faq_kb object. The event subscriber will not receive the object as expected.
479
- */
480
- botEvent.emit('faqbot.delete', faq_kb);
481
- botEvent.emit('faqbot.deleted', { req, chatbot: faq_kb });
482
- res.status(200).send({ success: true, message: "Chatbot with id " + req.params.faq_kbid + " deleted successfully" })
483
- });
484
- });
551
+ }
552
+
553
+ let deletedFaqKb;
554
+ try {
555
+ deletedFaqKb = await Faq_kb.findOneAndDelete({ _id: chatbot_id, id_project: id_project });
556
+ } catch (err) {
557
+ winston.error("Error deleting chatbot: ", err);
558
+ return res.status(500).send({ success: false, msg: 'Error deleting object.' });
559
+ }
560
+
561
+ if (!deletedFaqKb) {
562
+ return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
563
+ }
564
+
565
+ /**
566
+ * WARNING: faq_kb is the operation result, not the faq_kb object. The event subscriber will not receive the object as expected.
567
+ */
568
+ botEvent.emit('faqbot.delete', deletedFaqKb);
569
+ botEvent.emit('faqbot.deleted', { req, chatbot: deletedFaqKb });
570
+ return res.status(200).send({ success: true, message: "Chatbot with id " + req.params.faq_kbid + " deleted successfully" });
571
+ }));
485
572
 
486
573
  router.get('/:faq_kbid', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) {
487
574
 
@@ -549,6 +636,26 @@ router.get('/:faq_kbid/published', roleChecker.hasRoleOrTypes('admin', ['bot', '
549
636
 
550
637
  })
551
638
 
639
+ router.get('/:faq_kbid/subagents', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), async function (req, res) {
640
+
641
+ const id_project = req.projectid;
642
+ const chatbot_id = req.params.faq_kbid;
643
+
644
+ let subagents;
645
+ try {
646
+ subagents = await Faq_kb.find({
647
+ id_project: id_project,
648
+ parent_id: chatbot_id,
649
+ trashed: { $in: [null, false] }
650
+ }).sort({ createdAt: -1 }).lean().exec();
651
+ return res.status(200).send(subagents);
652
+ } catch (err) {
653
+ winston.error("Error finding subagents: ", err);
654
+ return res.status(500).send({ success: false, error: "Error finding subagents from root " + chatbot_id });
655
+ }
656
+
657
+ })
658
+
552
659
  router.get('/:faq_kbid/jwt', roleChecker.hasRoleOrTypes('admin', ['bot', 'subscription']), function (req, res) {
553
660
 
554
661
  winston.debug(req.query);
@@ -613,7 +720,7 @@ router.get('/', roleChecker.hasRoleOrTypes('agent', ['bot', 'subscription']), fu
613
720
  * if filter only for 'trashed = false',
614
721
  * the bots created before the implementation of the 'trashed' property are not returned
615
722
  */
616
- let query = { id_project: id_project, trashed: { $in: [null, false] } };
723
+ let query = { id_project: id_project, subtype: { $ne: "subagent" }, trashed: { $in: [null, false] } };
617
724
 
618
725
  if (restricted_mode === true) {
619
726
  query.agents_available = {
@@ -656,97 +763,151 @@ router.get('/', roleChecker.hasRoleOrTypes('agent', ['bot', 'subscription']), fu
656
763
  })
657
764
  });
658
765
 
659
- router.post('/fork/:id_faq_kb', roleChecker.hasRole('admin'), async (req, res) => {
660
-
661
- let id_faq_kb = req.params.id_faq_kb;
662
- winston.debug('id_faq_kb: ' + id_faq_kb);
663
-
766
+ // DEPRECATED: create shell via createBot, then import intents/subagents into it
767
+ // router.post('/fork/:id_faq_kb', roleChecker.hasRole('admin'), async (req, res) => {
768
+ //
769
+ // let id_faq_kb = req.params.id_faq_kb;
770
+ // winston.debug('id_faq_kb: ' + id_faq_kb);
771
+ //
772
+ // const api_url = process.env.API_URL || configGlobal.apiUrl;
773
+ // winston.debug("fork --> base_url: " + api_url); // check if correct
774
+ //
775
+ // let current_project_id = req.projectid;
776
+ // winston.debug("current project id: " + current_project_id);
777
+ //
778
+ // let landing_project_id = req.query.projectid;
779
+ // winston.debug("landing project id " + landing_project_id)
780
+ //
781
+ // let public = req.query.public;
782
+ // winston.debug("public " + public);
783
+ //
784
+ // let globals = req.query.globals;
785
+ // winston.debug("export globals " + globals);
786
+ //
787
+ // let token = req.headers.authorization;
788
+ //
789
+ // let cs = req.app.get('chatbot_service')
790
+ // let chatbot;
791
+ // try {
792
+ // chatbot = await cs.getBotById(id_faq_kb, public, api_url, chatbot_templates_api_url, token, current_project_id, globals);
793
+ // } catch (err) {
794
+ // return res.status(500).send({ success: false, error: "Unable to get chatbot to be forked"});
795
+ // }
796
+ //
797
+ // if (!chatbot) {
798
+ // return res.status(500).send({ success: false, message: "Unable to get chatbot to be forked" });
799
+ // }
800
+ //
801
+ // if (!globals) {
802
+ // if (chatbot.attributes) {
803
+ // delete chatbot.attributes.globals
804
+ // }
805
+ // }
806
+ //
807
+ // delete chatbot.modified;
808
+ //
809
+ // chatbot.template = "empty";
810
+ //
811
+ // let savedChatbot
812
+ // try {
813
+ // savedChatbot = await cs.createBot(api_url, token, chatbot, landing_project_id);
814
+ // winston.debug("savedChatbot: ", savedChatbot)
815
+ // } catch(err) {
816
+ // winston.error("Error creating new chatbot: ", err);
817
+ // return res.status(500).send({ success: false, message: "An error occurred during chatbot creation"});
818
+ // }
819
+ //
820
+ // if (!savedChatbot) {
821
+ // return res.status(500).send({ success: false, message: "Unable to create new chatbot" });
822
+ // }
823
+ //
824
+ // let import_result
825
+ // try {
826
+ // // Imports parent intents and linked subagents (with their intents) into the new parent.
827
+ // import_result = await cs.importFaqs(api_url, savedChatbot._id, token, chatbot, landing_project_id);
828
+ // winston.debug("imported: ", import_result);
829
+ // } catch(err) {
830
+ // winston.error("Error importing chatbot content on fork: ", err);
831
+ // return res.status(500).send({ success: false, message: "Error importing chatbot content (intents/subagents)"});
832
+ // }
833
+ //
834
+ // if (import_result?.success == false) {
835
+ // return res.status(500).send({ success: false, message: "Unable to import chatbot content in the new chatbot" });
836
+ // }
837
+ //
838
+ // return res.status(200).send({ message: "Chatbot forked successfully", bot_id: savedChatbot._id });
839
+ //
840
+ // })
841
+
842
+ // Export source chatbot, then import with create=true (parent + intents + linked subagents)
843
+ router.post('/fork/:id_faq_kb', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
844
+
845
+ const id_faq_kb = req.params.id_faq_kb;
664
846
  const api_url = process.env.API_URL || configGlobal.apiUrl;
665
- winston.debug("fork --> base_url: " + api_url); // check if correct
666
-
667
- let current_project_id = req.projectid;
847
+ const current_project_id = req.projectid;
848
+ const landing_project_id = req.query.projectid;
849
+ const public = req.query.public;
850
+ const globals = req.query.globals;
851
+ const token = req.headers.authorization;
852
+ const user_id = req.user.id;
853
+
854
+ winston.debug('fork id_faq_kb: ' + id_faq_kb);
855
+ winston.debug("fork --> base_url: " + api_url);
668
856
  winston.debug("current project id: " + current_project_id);
669
-
670
- let landing_project_id = req.query.projectid;
671
- winston.debug("landing project id " + landing_project_id)
672
-
673
- let public = req.query.public;
857
+ winston.debug("landing project id " + landing_project_id);
674
858
  winston.debug("public " + public);
675
-
676
- let globals = req.query.globals;
677
859
  winston.debug("export globals " + globals);
678
860
 
679
- let token = req.headers.authorization;
680
-
681
- let cs = req.app.get('chatbot_service')
861
+ const cs = req.app.get('chatbot_service');
682
862
  let chatbot;
683
863
  try {
684
864
  chatbot = await cs.getBotById(id_faq_kb, public, api_url, chatbot_templates_api_url, token, current_project_id, globals);
685
865
  } catch (err) {
686
- return res.status(500).send({ success: false, error: "Unable to get chatbot to be forked"});
866
+ return res.status(500).send({ success: false, error: "Unable to get chatbot to be forked" });
687
867
  }
688
868
 
689
869
  if (!chatbot) {
690
870
  return res.status(500).send({ success: false, message: "Unable to get chatbot to be forked" });
691
871
  }
692
872
 
693
- if (!globals) {
694
- if (chatbot.attributes) {
695
- delete chatbot.attributes.globals
696
- }
873
+ if (!globals && chatbot.attributes) {
874
+ delete chatbot.attributes.globals;
697
875
  }
698
-
699
- delete chatbot.modified;
700
876
 
701
- chatbot.template = "empty";
702
-
703
- let savedChatbot
704
- try {
705
- savedChatbot = await cs.createBot(api_url, token, chatbot, landing_project_id);
706
- winston.debug("savedChatbot: ", savedChatbot)
707
- } catch(err) {
708
- winston.error("Error creating new chatbot: ", err);
709
- return res.status(500).send({ success: false, message: "An error occurred during chatbot creation"});
710
- }
711
-
712
- if (!savedChatbot) {
713
- return res.status(500).send({ success: false, message: "Unable to create new chatbot" });
714
- }
715
-
716
- let import_result
717
- try {
718
- import_result = await cs.importFaqs(api_url, savedChatbot._id, token, chatbot, landing_project_id);
719
- winston.debug("imported: ", import_result);
720
- } catch(err) {
721
- winston.error("Error importing intents on new chatbot: ", err);
722
- return res.status(500).send({ success: false, message: "Error importing intents in the chatbot"});
723
- }
877
+ delete chatbot.modified;
724
878
 
725
- if (import_result?.success == false) {
726
- return res.status(500).send({ success: false, message: "Unable to import intents in the new chatbot" });
727
- }
879
+ // Publish passes skipSubagents so actions keep the development subagent id.
880
+ // A plain fork still clones subagents into the copy.
881
+ const skipSubagents = req.query.skipSubagents === 'true';
882
+
883
+ const savedChatbot = await chatbotController.import(
884
+ chatbot,
885
+ landing_project_id,
886
+ null,
887
+ true,
888
+ false,
889
+ false,
890
+ user_id,
891
+ skipSubagents
892
+ );
728
893
 
729
894
  if (req.query.for_publish !== 'true') {
730
895
  botEvent.emit('faqbot.created', { req, chatbot: savedChatbot, id_project: landing_project_id });
731
896
  }
732
897
 
733
898
  return res.status(200).send({ message: "Chatbot forked successfully", bot_id: savedChatbot._id });
899
+ }))
734
900
 
735
- })
901
+ router.post('/importjson/:id_faq_kb', roleChecker.hasRole('admin'), upload.single('uploadFile'), asyncHandler(async (req, res) => {
736
902
 
737
- router.post('/importjson/:id_faq_kb', roleChecker.hasRole('admin'), upload.single('uploadFile'), async (req, res) => {
738
-
739
- let chatbot_id = req.params.id_faq_kb;
740
- let id_project = req.projectid;
741
-
742
- winston.debug('import on id_faq_kb: ' + chatbot_id);
743
-
744
- winston.debug('import with option create: ' + req.query.create);
745
- winston.debug('import with option replace: ' + req.query.replace);
746
- winston.debug('import with option overwrite: ' + req.query.overwrite);
903
+ const chatbot_id = req.params.id_faq_kb;
904
+ const id_project = req.projectid;
905
+ const create = req.query.create === 'true';
906
+ const replace = req.query.replace === 'true';
907
+ const overwrite = req.query.overwrite === 'true';
908
+ const user_id = req.user.id;
747
909
 
748
- let json_string;
749
- let json;
910
+ let json, json_string;
750
911
  if (req.file) {
751
912
  json_string = req.file.buffer.toString('utf-8');
752
913
  json = JSON.parse(json_string);
@@ -754,313 +915,372 @@ router.post('/importjson/:id_faq_kb', roleChecker.hasRole('admin'), upload.singl
754
915
  json = req.body;
755
916
  }
756
917
 
757
- winston.debug("json source " + json_string)
758
-
759
- // ****************************
760
- // **** CREATE TRUE option ****
761
- // ****************************
762
- if (req.query.create === 'true') {
763
- // if (json.subtype && (json.subtype === 'webhook' || json.subtype === 'copilot')) {
764
- // json.template = 'empty';
765
- // }
766
- json.template = 'empty';
767
- let savedChatbot = await faqService.create(req.projectid, req.user.id, json)
768
- .catch((err) => {
769
- winston.error("Error creating new chatbot")
770
- return res.status(400).send({ succes: false, message: "Error creatings new chatbot", error: err })
771
- })
772
-
773
- // Edit attributes.rules
774
- let attributes = json.attributes;
775
- if (attributes &&
776
- attributes.rules &&
777
- attributes.rules.length > 0) {
778
-
779
- await attributes.rules.forEach((rule) => {
780
- if (rule.do &&
781
- rule.do[0] &&
782
- rule.do[0].message &&
783
- rule.do[0].message.participants &&
784
- rule.do[0].message.participants[0]) {
785
- rule.do[0].message.participants[0] = "bot_" + savedChatbot._id
786
- winston.debug("attributes rule new participant: ", rule.do[0].message.participants[0])
787
- }
788
- })
789
- }
790
-
791
- let chatbot_edited = { attributes: attributes };
792
-
793
- let updatedChatbot = await Faq_kb.findByIdAndUpdate(savedChatbot._id, chatbot_edited, { new: true }).catch((err) => {
794
- winston.error("Error updating chatbot attributes: ", err);
795
- winston.debug("Skip Error updating chatbot attributes: ", err);
796
- // return res.status(400).send({ success: false, message: "Error updating chatbot attributes", error: err })
797
- })
798
-
799
- botEvent.emit('faqbot.create', savedChatbot);
800
- botEvent.emit('faqbot.created', { req, chatbot: savedChatbot });
801
-
802
- if (json.intents) {
803
-
804
- json.intents.forEach(async (intent) => {
805
-
806
- let new_faq = {
807
- id_faq_kb: savedChatbot._id,
808
- id_project: req.projectid,
809
- createdBy: req.user.id,
810
- intent_display_name: intent.intent_display_name,
811
- intent_id: intent.intent_id,
812
- question: intent.question,
813
- answer: intent.answer,
814
- reply: intent.reply,
815
- form: intent.form,
816
- enabled: intent.enabled,
817
- webhook_enabled: intent.webhook_enabled,
818
- language: intent.language,
819
- actions: intent.actions,
820
- attributes: intent.attributes
821
- }
822
-
823
- let faq = await Faq.create(new_faq).catch((err) => {
824
- if (err.code == 11000) { // should never happen
825
- winston.error("Duplicate intent_display_name.");
826
- winston.debug("Skip duplicated intent_display_name"); // Don't stop the flow
827
- } else {
828
- winston.error("Error saving new faq: ", err)
829
- }
830
- })
831
-
832
- if (faq) {
833
- winston.debug("new intent created")
834
- faqBotEvent.emit('faq.create', faq);
835
- }
836
- })
837
- }
838
-
839
- if (updatedChatbot) {
840
- return res.status(200).send(updatedChatbot)
841
- } else {
842
- return res.status(200).send(savedChatbot)
843
- }
844
- }
845
- // *****************************
846
- // **** CREATE FALSE option ****
847
- // *****************************
848
- else {
849
-
850
- if (!chatbot_id) {
851
- return res.status(400).send({ success: false, message: "With replace or overwrite option a id_faq_kb must be provided" })
852
- }
853
-
854
- let chatbot = await Faq_kb.findOne({ _id: chatbot_id, id_project: id_project }).catch((err) => {
855
- winston.error("Error finding chatbot with id " + chatbot_id);
856
- return res.status(404).send({ success: false, message: "Error finding chatbot with id " + chatbot_id, error: err });
857
- })
858
-
859
- if (!chatbot) {
860
- winston.error("Chatbot not found with id " + chatbot_id + " for project " + id_project);
861
- return res.status(404).send({ success: false, message: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
862
- }
863
-
864
- if (json.webhook_enabled) {
865
- chatbot.webhook_enabled = json.webhook_enabled;
866
- }
867
- if (json.webhook_url) {
868
- chatbot.webhook_url = json.webhook_url;
869
- }
870
- if (json.language) {
871
- chatbot.language = json.language;
872
- }
873
- if (json.name) {
874
- chatbot.name = json.name;
875
- }
876
- if (json.description) {
877
- chatbot.description = json.description;
878
- }
879
-
880
- if (json.attributes) {
881
- let attributes = json.attributes;
882
- if (attributes.rules &&
883
- attributes.rules.length > 0) {
884
- await attributes.rules.forEach((rule) => {
885
- if (rule.do &&
886
- rule.do[0] &&
887
- rule.do[0].message &&
888
- rule.do[0].message.participants &&
889
- rule.do[0].message.participants[0]) {
890
- rule.do[0].message.participants[0] = "bot_" + chatbot._id
891
- winston.debug("attributes rule new participant: " + rule.do[0].message.participants[0])
892
- }
893
- })
894
- chatbot.attributes = json.attributes;
895
- }
896
- }
897
-
898
- let updatedChatbot = await Faq_kb.findByIdAndUpdate(chatbot._id, chatbot, { new: true }).catch((err) => {
899
- winston.error("Error updating chatbot");
900
- return res.status(400).send({ success: false, message: "Error updating chatbot", error: err })
901
- })
902
-
903
- botEvent.emit('faqbot.update', updatedChatbot);
904
-
905
- // *****************************
906
- // **** REPLACE TRUE option ****
907
- // *****************************
908
- if (req.query.replace === 'true') {
909
- let result = await Faq.deleteMany({ id_faq_kb: chatbot._id }).catch((err) => {
910
- winston.error("Unable to delete all existing faqs with id_faq_kb " + chatbot._id);
911
- })
912
-
913
- winston.verbose("All faq for chatbot " + chatbot._id + " deleted successfully")
914
- winston.debug("DeleteMany faqs result ", result);
915
- }
916
-
917
- if (json.intents) {
918
- await json.intents.forEach(async (intent) => {
919
-
920
- let new_faq = {
921
- id_faq_kb: updatedChatbot._id,
922
- id_project: req.projectid,
923
- createdBy: req.user.id,
924
- intent_display_name: intent.intent_display_name,
925
- intent_id: intent.intent_id,
926
- question: intent.question,
927
- answer: intent.answer,
928
- reply: intent.reply,
929
- form: intent.form,
930
- enabled: intent.enabled,
931
- webhook_enabled: intent.webhook_enabled,
932
- language: intent.language,
933
- actions: intent.actions,
934
- attributes: intent.attributes
935
- }
936
-
937
- // *******************************
938
- // **** OVERWRITE TRUE option ****
939
- // *******************************
940
- if (req.query.overwrite == "true") {
941
-
942
- let savingResult = await Faq.findOneAndUpdate({ id_faq_kb: chatbot._id, intent_display_name: intent.intent_display_name }, new_faq, { new: true, upsert: true, rawResult: true }).catch((err) => {
943
- winston.error("Unable to create faq: ", err);
944
- })
945
-
946
- if (savingResult) {
947
- if (savingResult.lastErrorObject.updatedExisting === true) {
948
- winston.verbose("updated existing intent")
949
- faqBotEvent.emit('faq.update', savingResult.value);
950
- } else {
951
- winston.verbose("new intent created")
952
- faqBotEvent.emit('faq.create', savingResult.value);
953
- }
954
- }
955
-
956
- // ********************************
957
- // **** OVERWRITE FALSE option ****
958
- // ********************************
959
- } else {
960
-
961
- let faq = await Faq.create(new_faq).catch((err) => {
962
- if (err.code == 11000) {
963
- winston.error("Duplicate intent_display_name.");
964
- winston.verbose("Skip duplicated intent_display_name");
965
- } else {
966
- winston.error("Error creating new intent: ", err);
967
- }
968
- })
969
-
970
- if (faq) {
971
- winston.debug("new intent created: ", faq)
972
- faqBotEvent.emit('faq.create', faq);
973
- }
974
- }
975
- })
976
- }
977
-
978
- if (updatedChatbot) {
979
- return res.send(updatedChatbot);
980
- } else {
981
- return res.send(chatbot);
982
- }
918
+ const chatbot = await chatbotController.import(json, id_project, chatbot_id, create, replace, overwrite, user_id);
919
+ if (create) {
920
+ botEvent.emit('faqbot.created', { req, chatbot });
983
921
  }
984
- })
985
-
986
- router.get('/exportjson/:id_faq_kb', roleChecker.hasRole('admin'), (req, res) => {
987
-
988
- winston.debug("exporting bot...")
922
+ return res.status(200).send(chatbot);
923
+ }))
924
+
925
+ // router.post('/importjson/:id_faq_kb', roleChecker.hasRole('admin'), upload.single('uploadFile'), async (req, res) => {
926
+
927
+ // let chatbot_id = req.params.id_faq_kb;
928
+ // let id_project = req.projectid;
929
+
930
+ // winston.debug('import on id_faq_kb: ' + chatbot_id);
931
+
932
+ // winston.debug('import with option create: ' + req.query.create);
933
+ // winston.debug('import with option replace: ' + req.query.replace);
934
+ // winston.debug('import with option overwrite: ' + req.query.overwrite);
935
+
936
+ // let json_string;
937
+ // let json;
938
+ // if (req.file) {
939
+ // json_string = req.file.buffer.toString('utf-8');
940
+ // json = JSON.parse(json_string);
941
+ // } else {
942
+ // json = req.body;
943
+ // }
944
+
945
+ // winston.debug("json source " + json_string)
946
+
947
+ // // ****************************
948
+ // // **** CREATE TRUE option ****
949
+ // // ****************************
950
+ // if (req.query.create === 'true') {
951
+ // // if (json.subtype && (json.subtype === 'webhook' || json.subtype === 'copilot')) {
952
+ // // json.template = 'empty';
953
+ // // }
954
+ // json.template = 'empty';
955
+ // let savedChatbot = await faqService.create(req.projectid, req.user.id, json)
956
+ // .catch((err) => {
957
+ // winston.error("Error creating new chatbot")
958
+ // return res.status(400).send({ succes: false, message: "Error creatings new chatbot", error: err })
959
+ // })
960
+
961
+ // // Edit attributes.rules
962
+ // let attributes = json.attributes;
963
+ // if (attributes &&
964
+ // attributes.rules &&
965
+ // attributes.rules.length > 0) {
966
+
967
+ // await attributes.rules.forEach((rule) => {
968
+ // if (rule.do &&
969
+ // rule.do[0] &&
970
+ // rule.do[0].message &&
971
+ // rule.do[0].message.participants &&
972
+ // rule.do[0].message.participants[0]) {
973
+ // rule.do[0].message.participants[0] = "bot_" + savedChatbot._id
974
+ // winston.debug("attributes rule new participant: ", rule.do[0].message.participants[0])
975
+ // }
976
+ // })
977
+ // }
978
+
979
+ // let chatbot_edited = { attributes: attributes };
980
+
981
+ // let updatedChatbot = await Faq_kb.findByIdAndUpdate(savedChatbot._id, chatbot_edited, { new: true }).catch((err) => {
982
+ // winston.error("Error updating chatbot attributes: ", err);
983
+ // winston.debug("Skip Error updating chatbot attributes: ", err);
984
+ // // return res.status(400).send({ success: false, message: "Error updating chatbot attributes", error: err })
985
+ // })
986
+
987
+ // botEvent.emit('faqbot.create', savedChatbot);
988
+
989
+ // if (json.intents) {
990
+
991
+ // json.intents.forEach(async (intent) => {
992
+
993
+ // let new_faq = {
994
+ // id_faq_kb: savedChatbot._id,
995
+ // id_project: req.projectid,
996
+ // createdBy: req.user.id,
997
+ // intent_display_name: intent.intent_display_name,
998
+ // intent_id: intent.intent_id,
999
+ // question: intent.question,
1000
+ // answer: intent.answer,
1001
+ // reply: intent.reply,
1002
+ // form: intent.form,
1003
+ // enabled: intent.enabled,
1004
+ // webhook_enabled: intent.webhook_enabled,
1005
+ // language: intent.language,
1006
+ // actions: intent.actions,
1007
+ // attributes: intent.attributes
1008
+ // }
1009
+
1010
+ // let faq = await Faq.create(new_faq).catch((err) => {
1011
+ // if (err.code == 11000) { // should never happen
1012
+ // winston.error("Duplicate intent_display_name.");
1013
+ // winston.debug("Skip duplicated intent_display_name"); // Don't stop the flow
1014
+ // } else {
1015
+ // winston.error("Error saving new faq: ", err)
1016
+ // }
1017
+ // })
1018
+
1019
+ // if (faq) {
1020
+ // winston.debug("new intent created")
1021
+ // faqBotEvent.emit('faq.create', faq);
1022
+ // }
1023
+ // })
1024
+ // }
1025
+
1026
+ // if (updatedChatbot) {
1027
+ // return res.status(200).send(updatedChatbot)
1028
+ // } else {
1029
+ // return res.status(200).send(savedChatbot)
1030
+ // }
1031
+ // }
1032
+ // // *****************************
1033
+ // // **** CREATE FALSE option ****
1034
+ // // *****************************
1035
+ // else {
1036
+
1037
+ // if (!chatbot_id) {
1038
+ // return res.status(400).send({ success: false, message: "With replace or overwrite option a id_faq_kb must be provided" })
1039
+ // }
1040
+
1041
+ // let chatbot = await Faq_kb.findOne({ _id: chatbot_id, id_project: id_project }).catch((err) => {
1042
+ // winston.error("Error finding chatbot with id " + chatbot_id);
1043
+ // return res.status(404).send({ success: false, message: "Error finding chatbot with id " + chatbot_id, error: err });
1044
+ // })
1045
+
1046
+ // if (!chatbot) {
1047
+ // winston.error("Chatbot not found with id " + chatbot_id + " for project " + id_project);
1048
+ // return res.status(404).send({ success: false, message: "Chatbot not found with id " + chatbot_id + " for project " + id_project });
1049
+ // }
1050
+
1051
+ // if (json.webhook_enabled) {
1052
+ // chatbot.webhook_enabled = json.webhook_enabled;
1053
+ // }
1054
+ // if (json.webhook_url) {
1055
+ // chatbot.webhook_url = json.webhook_url;
1056
+ // }
1057
+ // if (json.language) {
1058
+ // chatbot.language = json.language;
1059
+ // }
1060
+ // if (json.name) {
1061
+ // chatbot.name = json.name;
1062
+ // }
1063
+ // if (json.description) {
1064
+ // chatbot.description = json.description;
1065
+ // }
1066
+
1067
+ // if (json.attributes) {
1068
+ // let attributes = json.attributes;
1069
+ // if (attributes.rules &&
1070
+ // attributes.rules.length > 0) {
1071
+ // await attributes.rules.forEach((rule) => {
1072
+ // if (rule.do &&
1073
+ // rule.do[0] &&
1074
+ // rule.do[0].message &&
1075
+ // rule.do[0].message.participants &&
1076
+ // rule.do[0].message.participants[0]) {
1077
+ // rule.do[0].message.participants[0] = "bot_" + chatbot._id
1078
+ // winston.debug("attributes rule new participant: " + rule.do[0].message.participants[0])
1079
+ // }
1080
+ // })
1081
+ // chatbot.attributes = json.attributes;
1082
+ // }
1083
+ // }
1084
+
1085
+ // let updatedChatbot = await Faq_kb.findByIdAndUpdate(chatbot._id, chatbot, { new: true }).catch((err) => {
1086
+ // winston.error("Error updating chatbot");
1087
+ // return res.status(400).send({ success: false, message: "Error updating chatbot", error: err })
1088
+ // })
1089
+
1090
+ // botEvent.emit('faqbot.update', updatedChatbot);
1091
+
1092
+ // // *****************************
1093
+ // // **** REPLACE TRUE option ****
1094
+ // // *****************************
1095
+ // if (req.query.replace === 'true') {
1096
+ // let result = await Faq.deleteMany({ id_faq_kb: chatbot._id }).catch((err) => {
1097
+ // winston.error("Unable to delete all existing faqs with id_faq_kb " + chatbot._id);
1098
+ // })
1099
+
1100
+ // winston.verbose("All faq for chatbot " + chatbot._id + " deleted successfully")
1101
+ // winston.debug("DeleteMany faqs result ", result);
1102
+ // }
1103
+
1104
+ // if (json.intents) {
1105
+ // await json.intents.forEach(async (intent) => {
1106
+
1107
+ // let new_faq = {
1108
+ // id_faq_kb: updatedChatbot._id,
1109
+ // id_project: req.projectid,
1110
+ // createdBy: req.user.id,
1111
+ // intent_display_name: intent.intent_display_name,
1112
+ // intent_id: intent.intent_id,
1113
+ // question: intent.question,
1114
+ // answer: intent.answer,
1115
+ // reply: intent.reply,
1116
+ // form: intent.form,
1117
+ // enabled: intent.enabled,
1118
+ // webhook_enabled: intent.webhook_enabled,
1119
+ // language: intent.language,
1120
+ // actions: intent.actions,
1121
+ // attributes: intent.attributes
1122
+ // }
1123
+
1124
+ // // *******************************
1125
+ // // **** OVERWRITE TRUE option ****
1126
+ // // *******************************
1127
+ // if (req.query.overwrite == "true") {
1128
+
1129
+ // let savingResult = await Faq.findOneAndUpdate({ id_faq_kb: chatbot._id, intent_display_name: intent.intent_display_name }, new_faq, { new: true, upsert: true, rawResult: true }).catch((err) => {
1130
+ // winston.error("Unable to create faq: ", err);
1131
+ // })
1132
+
1133
+ // if (savingResult) {
1134
+ // if (savingResult.lastErrorObject.updatedExisting === true) {
1135
+ // winston.verbose("updated existing intent")
1136
+ // faqBotEvent.emit('faq.update', savingResult.value);
1137
+ // } else {
1138
+ // winston.verbose("new intent created")
1139
+ // faqBotEvent.emit('faq.create', savingResult.value);
1140
+ // }
1141
+ // }
1142
+
1143
+ // // ********************************
1144
+ // // **** OVERWRITE FALSE option ****
1145
+ // // ********************************
1146
+ // } else {
1147
+
1148
+ // let faq = await Faq.create(new_faq).catch((err) => {
1149
+ // if (err.code == 11000) {
1150
+ // winston.error("Duplicate intent_display_name.");
1151
+ // winston.verbose("Skip duplicated intent_display_name");
1152
+ // } else {
1153
+ // winston.error("Error creating new intent: ", err);
1154
+ // }
1155
+ // })
1156
+
1157
+ // if (faq) {
1158
+ // winston.debug("new intent created: ", faq)
1159
+ // faqBotEvent.emit('faq.create', faq);
1160
+ // }
1161
+ // }
1162
+ // })
1163
+ // }
1164
+
1165
+ // if (updatedChatbot) {
1166
+ // return res.send(updatedChatbot);
1167
+ // } else {
1168
+ // return res.send(chatbot);
1169
+ // }
1170
+ // }
1171
+ // })
1172
+
1173
+ router.get('/exportjson/:id_faq_kb', roleChecker.hasRole('admin'), asyncHandler(async (req, res) => {
989
1174
 
990
1175
  const chatbot_id = req.params.id_faq_kb;
991
1176
  const id_project = req.projectid;
1177
+ const exportGlobals = req.query.globals === 'true';
1178
+ const intentsOnly = req.query.intentsOnly === 'true';
992
1179
 
993
- Faq_kb.findOne({ _id: chatbot_id }, (err, faq_kb) => {
994
- if (err) {
995
- winston.error('GET FAQ-KB ERROR ', err)
996
- return res.status(500).send({ success: false, msg: 'Error getting bot.' });
997
- }
998
-
999
- if (!faq_kb) {
1000
- return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id });
1001
- }
1002
-
1003
- const isPublic = faq_kb.public === true;
1004
- const isOwner = faq_kb.id_project === id_project;
1180
+ const chatbot = await chatbotController.getChatbotById(chatbot_id);
1005
1181
 
1006
- if (!isPublic && !isOwner) {
1007
- return res.status(403).send({
1008
- success: false,
1009
- msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project
1010
- });
1011
- }
1182
+ if (!chatbotController.canAccess(chatbot, id_project)) {
1183
+ throw new AppError(403, `Unauthorized to access chatbot with id ${chatbot_id} for project ${id_project}`);
1184
+ }
1012
1185
 
1013
- winston.debug('FAQ-KB: ', faq_kb)
1186
+ let intents = await intentsController.getIntents(chatbot_id);
1014
1187
 
1015
- faqService.getAll(chatbot_id).then((faqs) => {
1188
+ if (!exportGlobals) {
1189
+ winston.verbose("Delete globals from attributes")
1190
+ delete chatbot.attributes?.globals;
1191
+ }
1016
1192
 
1017
- // delete from exclude map intent_id
1018
- const intents = faqs.map(({ _id, id_project, topic, status, chatbot_id, createdBy, createdAt, updatedAt, __v, ...keepAttrs }) => keepAttrs)
1193
+ let subagents = await chatbotController.findSubagents(chatbot_id, id_project);
1194
+ await Promise.all(subagents.map(async (subagent) => {
1195
+ subagent.intents = await intentsController.getIntents(subagent._id);
1196
+ }));
1019
1197
 
1020
- if (!req.query.globals) {
1021
- winston.verbose("Delete globals from attributes!")
1022
- if (faq_kb.attributes) {
1023
- delete faq_kb.attributes.globals;
1024
- }
1025
- }
1198
+ let exportData = chatbotController.export(chatbot, intents, intentsOnly, subagents);
1199
+ let filename = intentsOnly ? "intents.json" : "bot.json";
1200
+ res.set({ "Content-Disposition": `attachment; filename="${filename}"` });
1201
+ return res.send(exportData);
1026
1202
 
1027
- let json = {
1028
- webhook_enabled: faq_kb.webhook_enabled,
1029
- webhook_url: faq_kb.webhook_url,
1030
- language: faq_kb.language,
1031
- name: faq_kb.name,
1032
- slug: faq_kb.slug,
1033
- type: faq_kb.type,
1034
- subtype: faq_kb.subtype,
1035
- description: faq_kb.description,
1036
- attributes: faq_kb.attributes,
1037
- intents: intents
1038
- }
1203
+ }))
1039
1204
 
1040
- if (req.query.intentsOnly == 'true') {
1041
- let intents_obj = {
1042
- intents: intents
1043
- }
1044
- let intents_string = JSON.stringify(intents_obj);
1045
- res.set({ "Content-Disposition": "attachment; filename=\"intents.json\"" });
1046
- return res.send(intents_string);
1205
+ // DEPRECATED
1206
+ // router.get('/exportjson/:id_faq_kb', roleChecker.hasRole('admin'), (req, res) => {
1047
1207
 
1048
- } else {
1208
+ // winston.debug("exporting bot...")
1049
1209
 
1050
- // if (req.query.file == "false") {
1051
- // return res.status(200).send(json);
1052
- // }
1053
- let json_string = JSON.stringify(json);
1054
- res.set({ "Content-Disposition": "attachment; filename=\"bot.json\"" });
1055
- return res.send(json_string);
1056
- }
1210
+ // const chatbot_id = req.params.id_faq_kb;
1211
+ // const id_project = req.projectid;
1057
1212
 
1058
- }).catch((err) => {
1059
- winston.error('GET FAQ ERROR: ', err)
1060
- return res.status(500).send({ success: false, msg: 'Error getting faqs.' });
1061
- })
1062
- })
1063
- })
1213
+ // Faq_kb.findOne({ _id: chatbot_id }, (err, faq_kb) => {
1214
+ // if (err) {
1215
+ // winston.error('GET FAQ-KB ERROR ', err)
1216
+ // return res.status(500).send({ success: false, msg: 'Error getting bot.' });
1217
+ // }
1218
+
1219
+ // if (!faq_kb) {
1220
+ // return res.status(404).send({ success: false, msg: "Chatbot not found with id " + chatbot_id });
1221
+ // }
1222
+
1223
+ // const isPublic = faq_kb.public === true;
1224
+ // const isOwner = faq_kb.id_project === id_project;
1225
+
1226
+ // if (!isPublic && !isOwner) {
1227
+ // return res.status(403).send({
1228
+ // success: false,
1229
+ // msg: "Chatbot not found with id " + chatbot_id + " for project " + id_project
1230
+ // });
1231
+ // }
1232
+
1233
+ // winston.debug('FAQ-KB: ', faq_kb)
1234
+
1235
+ // faqService.getAll(chatbot_id).then((faqs) => {
1236
+
1237
+ // // delete from exclude map intent_id
1238
+ // const intents = faqs.map(({ _id, id_project, topic, status, chatbot_id, createdBy, createdAt, updatedAt, __v, ...keepAttrs }) => keepAttrs)
1239
+
1240
+ // if (!req.query.globals) {
1241
+ // winston.verbose("Delete globals from attributes!")
1242
+ // if (faq_kb.attributes) {
1243
+ // delete faq_kb.attributes.globals;
1244
+ // }
1245
+ // }
1246
+
1247
+ // let json = {
1248
+ // webhook_enabled: faq_kb.webhook_enabled,
1249
+ // webhook_url: faq_kb.webhook_url,
1250
+ // language: faq_kb.language,
1251
+ // name: faq_kb.name,
1252
+ // slug: faq_kb.slug,
1253
+ // type: faq_kb.type,
1254
+ // subtype: faq_kb.subtype,
1255
+ // description: faq_kb.description,
1256
+ // attributes: faq_kb.attributes,
1257
+ // intents: intents
1258
+ // }
1259
+
1260
+ // if (req.query.intentsOnly == 'true') {
1261
+ // let intents_obj = {
1262
+ // intents: intents
1263
+ // }
1264
+ // let intents_string = JSON.stringify(intents_obj);
1265
+ // res.set({ "Content-Disposition": "attachment; filename=\"intents.json\"" });
1266
+ // return res.send(intents_string);
1267
+
1268
+ // } else {
1269
+
1270
+ // // if (req.query.file == "false") {
1271
+ // // return res.status(200).send(json);
1272
+ // // }
1273
+ // let json_string = JSON.stringify(json);
1274
+ // res.set({ "Content-Disposition": "attachment; filename=\"bot.json\"" });
1275
+ // return res.send(json_string);
1276
+ // }
1277
+
1278
+ // }).catch((err) => {
1279
+ // winston.error('GET FAQ ERROR: ', err)
1280
+ // return res.status(500).send({ success: false, msg: 'Error getting faqs.' });
1281
+ // })
1282
+ // })
1283
+ // })
1064
1284
 
1065
1285
  router.post('/:faq_kbid/training', roleChecker.hasRole('admin'), function (req, res) {
1066
1286