@tiledesk/tiledesk-server 2.9.31 → 2.9.33

Sign up to get free protection for your applications and to get access to all the features.
package/routes/kb.js CHANGED
@@ -1,6 +1,7 @@
1
1
  var express = require('express');
2
- var { Namespace } = require('../models/kb_setting');
3
- var { KB } = require('../models/kb_setting');
2
+ var { Namespace, KB, Engine } = require('../models/kb_setting');
3
+ // var { KB } = require('../models/kb_setting');
4
+ // var { Engine } = require('../models/kb_setting')
4
5
  var router = express.Router();
5
6
  var winston = require('../config/winston');
6
7
  var multer = require('multer')
@@ -17,6 +18,7 @@ let Integration = require('../models/integrations');
17
18
  var parsecsv = require("fast-csv");
18
19
 
19
20
  const { MODELS_MULTIPLIER } = require('../utils/aiUtils');
21
+ const { body } = require('express-validator');
20
22
 
21
23
  const AMQP_MANAGER_URL = process.env.AMQP_MANAGER_URL;
22
24
  const JOB_TOPIC_EXCHANGE = process.env.JOB_TOPIC_EXCHANGE_TRAIN || 'tiledesk-trainer';
@@ -45,6 +47,13 @@ let default_preview_settings = {
45
47
  //context: "You are an awesome AI Assistant."
46
48
  context: null
47
49
  }
50
+ let default_engine = {
51
+ name: "pinecone",
52
+ type: "pod",
53
+ apikey: "",
54
+ vector_size: 1536,
55
+ index_name: process.env.PINECONE_INDEX
56
+ }
48
57
 
49
58
  //let default_context = "Answer if and ONLY if the answer is contained in the context provided. If the answer is not contained in the context provided ALWAYS answer with <NOANS>\n{context}"
50
59
  //let default_context = "You are an helpful assistant for question-answering tasks.\nUse ONLY the following pieces of retrieved context to answer the question.\nIf you don't know the answer, just say that you don't know.\nIf none of the retrieved context answer the question, add this word to the end <NOANS>\n\n{context}";
@@ -68,9 +77,9 @@ router.post('/scrape/single', async (req, res) => {
68
77
  let data = req.body;
69
78
  winston.debug("/scrape/single data: ", data);
70
79
 
71
- if (!data.scrape_type) {
72
- data.scrape_type = 1;
73
- }
80
+ // if (!data.scrape_type) {
81
+ // data.scrape_type = 1;
82
+ // }
74
83
 
75
84
  let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
76
85
  winston.error("find namespaces error: ", err)
@@ -112,11 +121,28 @@ router.post('/scrape/single', async (req, res) => {
112
121
  json.content = kb.content;
113
122
  }
114
123
 
115
- json.scrape_type = 1;
116
- if (data.scrape_type) {
117
- json.scrape_type = data.scrape_type;
124
+ // json.scrape_type = 1;
125
+ // if (data.scrape_type) {
126
+ // json.scrape_type = data.scrape_type;
127
+ // }
128
+
129
+ if (kb.scrape_type) {
130
+ json.scrape_type = kb.scrape_type
118
131
  }
119
132
 
133
+ if (kb.scrape_options) {
134
+ json.parameters_scrape_type_4 = {
135
+ tags_to_extract: kb.scrape_options.tags_to_extract,
136
+ unwanted_tags: kb.scrape_options.unwanted_tags,
137
+ unwanted_classnames: kb.scrape_options.unwanted_classnames
138
+ }
139
+ }
140
+
141
+ let ns = namespaces.find(n => n.id === kb.namespace);
142
+ json.engine = ns.engine || default_engine;
143
+
144
+ winston.verbose("/scrape/single json: ", json);
145
+
120
146
  startScrape(json).then((response) => {
121
147
  winston.verbose("startScrape response: ", response);
122
148
  res.status(200).send(response);
@@ -132,6 +158,8 @@ router.post('/scrape/single', async (req, res) => {
132
158
 
133
159
  router.post('/scrape/status', async (req, res) => {
134
160
 
161
+ let project_id = req.projectid;
162
+ // (EXAMPLE) body: { id, namespace }
135
163
  let data = req.body;
136
164
  winston.debug("/scrapeStatus req.body: ", req.body);
137
165
 
@@ -143,6 +171,25 @@ router.post('/scrape/status', async (req, res) => {
143
171
  returnObject = true;
144
172
  }
145
173
 
174
+ let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
175
+ winston.error("find namespaces error: ", err)
176
+ res.status(500).send({ success: false, error: err })
177
+ })
178
+
179
+ if (!namespaces || namespaces.length == 0) {
180
+ let alert = "No namespace found for the selected project " + project_id + ". Cannot add content to a non-existent namespace."
181
+ winston.warn(alert);
182
+ res.status(403).send(alert);
183
+ }
184
+
185
+ let namespaceIds = namespaces.map(namespace => namespace.id);
186
+ if (!namespaceIds.includes(data.namespace)) {
187
+ return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
188
+ }
189
+
190
+ let ns = namespaces.find(n => n.id === data.namespace);
191
+ data.engine = ns.engine || default_engine;
192
+
146
193
  openaiService.scrapeStatus(data).then(async (response) => {
147
194
 
148
195
  winston.debug("scrapeStatus response.data: ", response.data);
@@ -229,7 +276,7 @@ router.post('/qa', async (req, res) => {
229
276
  }
230
277
 
231
278
  // Check if "Advanced Mode" is active. In such case the default_context must be not appended
232
- if (!data.advanced_context) {
279
+ if (!data.advancedPrompt) {
233
280
  if (data.system_context) {
234
281
  data.system_context = data.system_context + " \n" + contexts[data.model];
235
282
  } else {
@@ -237,9 +284,15 @@ router.post('/qa', async (req, res) => {
237
284
  }
238
285
  }
239
286
 
240
- // if (process.env.NODE_ENV === 'test') {
241
- // return res.status(200).send({ success: true, message: "Question skipped in test environment"});
242
- // }
287
+ let ns = namespaces.find(n => n.id === data.namespace);
288
+ data.engine = ns.engine || default_engine;
289
+
290
+ delete data.advancedPrompt;
291
+ winston.verbose("ask data: ", data);
292
+
293
+ if (process.env.NODE_ENV === 'test') {
294
+ return res.status(200).send({ success: true, message: "Question skipped in test environment"});
295
+ }
243
296
 
244
297
  openaiService.askNamespace(data).then((resp) => {
245
298
  winston.debug("qa resp: ", resp.data);
@@ -317,6 +370,9 @@ router.delete('/delete', async (req, res) => {
317
370
  if (!namespaceIds.includes(data.namespace)) {
318
371
  return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
319
372
  }
373
+
374
+ let ns = namespaces.find(n => n.id === data.namespace);
375
+ data.engine = ns.engine || default_engine;
320
376
 
321
377
  openaiService.deleteIndex(data).then((resp) => {
322
378
  winston.debug("delete resp: ", resp.data);
@@ -352,6 +408,11 @@ router.delete('/deleteall', async (req, res) => {
352
408
  return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
353
409
  }
354
410
 
411
+ let ns = namespaces.find(n => n.id === data.namespace);
412
+ data.engine = ns.engine || default_engine;
413
+
414
+ winston.verbose("/deleteall data: ", data);
415
+
355
416
  openaiService.deleteNamespace(data).then((resp) => {
356
417
  winston.debug("delete namespace resp: ", resp.data);
357
418
  res.status(200).send(resp.data);
@@ -394,7 +455,8 @@ router.get('/namespace/all', async (req, res) => {
394
455
  id: project_id,
395
456
  name: "Default",
396
457
  preview_settings: default_preview_settings,
397
- default: true
458
+ default: true,
459
+ engine: default_engine
398
460
  })
399
461
 
400
462
  new_namespace.save((err, savedNamespace) => {
@@ -448,7 +510,11 @@ router.get('/namespace/:id/chunks/:content_id', async (req, res) => {
448
510
  return res.status(403).send({ success: false, error: "Not allowed. The conten does not belong to the current namespace." })
449
511
  }
450
512
 
451
- openaiService.getContentChunks(namespace_id, content_id).then((resp) => {
513
+ let ns = namespaces.find(n => n.id === namespace_id);
514
+ let engine = ns.engine || default_engine;
515
+ delete engine._id;
516
+
517
+ openaiService.getContentChunks(namespace_id, content_id, engine).then((resp) => {
452
518
  let chunks = resp.data;
453
519
  winston.debug("chunks for content " + content_id);
454
520
  winston.debug("chunks found ", chunks);
@@ -492,7 +558,6 @@ router.get('/namespace/:id/chatbots', async (req, res) => {
492
558
  let chatbots = intents.map(i => i.id_faq_kb);
493
559
  let uniqueChatbots = [...new Set(chatbots)];
494
560
 
495
-
496
561
  let chatbotPromises = uniqueChatbots.map(async (c_id) => {
497
562
  try {
498
563
  let chatbot = await faq_kb.findOne({ _id: c_id, trashed: false });
@@ -527,6 +592,7 @@ router.post('/namespace', async (req, res) => {
527
592
  id: namespace_id,
528
593
  name: body.name,
529
594
  preview_settings: default_preview_settings,
595
+ engine: default_engine
530
596
  })
531
597
 
532
598
  let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
@@ -589,11 +655,23 @@ router.put('/namespace/:id', async (req, res) => {
589
655
 
590
656
  router.delete('/namespace/:id', async (req, res) => {
591
657
 
592
- let id_project = req.projectid;
658
+ let project_id = req.projectid;
593
659
  let namespace_id = req.params.id;
594
660
 
661
+ let namespace = await Namespace.findOne({ id_project: project_id, id: namespace_id }).catch((err) => {
662
+ winston.error("find namespace error: ", err);
663
+ res.status(500).send({ success: false, error: err });
664
+ })
665
+
666
+ if (!namespace) {
667
+ let alert = "No namespace found for id " + namespace_id;
668
+ winston.warn(alert);
669
+ res.status(404).send({ success: false, error: alert })
670
+ }
671
+
595
672
  let data = {
596
- namespace: namespace_id
673
+ namespace: namespace_id,
674
+ engine: namespace.engine || default_engine
597
675
  }
598
676
 
599
677
  if (req.query.contents_only && (req.query.contents_only === true || req.query.contents_only === 'true')) {
@@ -601,7 +679,7 @@ router.delete('/namespace/:id', async (req, res) => {
601
679
  openaiService.deleteNamespace(data).then(async (resp) => {
602
680
  winston.debug("delete namespace resp: ", resp.data);
603
681
 
604
- let deleteResponse = await KB.deleteMany({ id_project: id_project, namespace: namespace_id }).catch((err) => {
682
+ let deleteResponse = await KB.deleteMany({ id_project: project_id, namespace: namespace_id }).catch((err) => {
605
683
  winston.error("deleteMany error: ", err);
606
684
  return res.status(500).send({ success: false, error: err });
607
685
  })
@@ -635,7 +713,7 @@ router.delete('/namespace/:id', async (req, res) => {
635
713
  openaiService.deleteNamespace(data).then(async (resp) => {
636
714
  winston.debug("delete namespace resp: ", resp.data);
637
715
 
638
- let deleteResponse = await KB.deleteMany({ id_project: id_project, namespace: namespace_id }).catch((err) => {
716
+ let deleteResponse = await KB.deleteMany({ id_project: project_id, namespace: namespace_id }).catch((err) => {
639
717
  winston.error("deleteMany error: ", err);
640
718
  return res.status(500).send({ success: false, error: err });
641
719
  })
@@ -831,7 +909,7 @@ router.post('/', async (req, res) => {
831
909
  if (!namespaceIds.includes(body.namespace)) {
832
910
  return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
833
911
  }
834
-
912
+
835
913
  let quoteManager = req.app.get('quote_manager');
836
914
  let limits = await quoteManager.getPlanLimits(req.project);
837
915
  let kbs_limit = limits.kbs;
@@ -848,6 +926,7 @@ router.post('/', async (req, res) => {
848
926
  if (total_count > kbs_limit) {
849
927
  return res.status(403).send({ success: false, error: "Cannot exceed the number of resources in the current plan", plan_limit: kbs_limit })
850
928
  }
929
+
851
930
  let new_kb = {
852
931
  id_project: project_id,
853
932
  name: body.name,
@@ -860,6 +939,19 @@ router.post('/', async (req, res) => {
860
939
  if (!new_kb.namespace) {
861
940
  new_kb.namespace = project_id;
862
941
  }
942
+ if (new_kb.type === 'txt') {
943
+ new_kb.scrape_type = 1;
944
+ }
945
+ if (new_kb.type === 'url') {
946
+ if (!body.scrape_type || body.scrape_type === 2) {
947
+ new_kb.scrape_type = 2;
948
+ new_kb.scrape_options = await setDefaultScrapeOptions();
949
+ } else {
950
+ new_kb.scrape_type = body.scrape_type;
951
+ new_kb.scrape_options = body.scrape_options;
952
+ }
953
+ }
954
+
863
955
  winston.debug("adding kb: ", new_kb);
864
956
 
865
957
  KB.findOneAndUpdate({ id_project: project_id, type: 'url', source: new_kb.source }, new_kb, { upsert: true, new: true, rawResult: true }, async (err, raw) => {
@@ -871,26 +963,37 @@ router.post('/', async (req, res) => {
871
963
 
872
964
  res.status(200).send(raw);
873
965
 
966
+ let saved_kb = raw.value;
874
967
  let webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
875
968
 
876
969
  let json = {
877
- id: raw.value._id,
878
- type: raw.value.type,
879
- source: raw.value.source,
970
+ id: saved_kb._id,
971
+ type: saved_kb.type,
972
+ source: saved_kb.source,
880
973
  content: "",
881
- namespace: raw.value.namespace,
974
+ namespace: saved_kb.namespace,
882
975
  webhook: webhook
883
976
  }
884
977
  winston.debug("json: ", json);
885
978
 
886
- if (raw.value.content) {
887
- json.content = raw.value.content;
979
+ if (saved_kb.content) {
980
+ json.content = saved_kb.content;
981
+ }
982
+ if (saved_kb.scrape_type) {
983
+ json.scrape_type = saved_kb.scrape_type;
984
+ }
985
+ if (saved_kb.scrape_options) {
986
+ json.parameters_scrape_type_4 = saved_kb.scrape_options;
888
987
  }
988
+ let ns = namespaces.find(n => n.id === body.namespace);
989
+ json.engine = ns.engine || default_engine;
889
990
 
890
991
  let resources = [];
891
992
 
892
993
  resources.push(json);
893
- scheduleScrape(resources);
994
+ if (!process.env.NODE_ENV) {
995
+ scheduleScrape(resources);
996
+ }
894
997
 
895
998
  }
896
999
  })
@@ -908,6 +1011,8 @@ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
908
1011
  }
909
1012
 
910
1013
  let project_id = req.projectid;
1014
+ let scrape_type = req.body.scrape_type;
1015
+ let scrape_options = req.body.scrape_options;
911
1016
 
912
1017
  let namespace_id = req.query.namespace;
913
1018
  if (!namespace_id) {
@@ -956,16 +1061,37 @@ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
956
1061
  let webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
957
1062
 
958
1063
  let kbs = [];
959
- list.forEach(url => {
960
- kbs.push({
1064
+ list.forEach( async (url) => {
1065
+ let kb = {
961
1066
  id_project: project_id,
962
1067
  name: url,
963
1068
  source: url,
964
1069
  type: 'url',
965
1070
  content: "",
966
1071
  namespace: namespace_id,
967
- status: -1
968
- })
1072
+ status: -1,
1073
+ scrape_type: scrape_type
1074
+ }
1075
+
1076
+ if (!kb.scrape_type) {
1077
+ scrape_type = 2;
1078
+ }
1079
+
1080
+ if (scrape_type == 2) {
1081
+ kb.scrape_options = {
1082
+ tags_to_extract: ["body"],
1083
+ unwanted_tags: [],
1084
+ unwanted_classnames: []
1085
+ }
1086
+ } else {
1087
+ kb.scrape_options = scrape_options;
1088
+ }
1089
+ // if (scrape_type === 2) {
1090
+ // kb.scrape_options = await setDefaultScrapeOptions();
1091
+ // } else {
1092
+ // kb.scrape_options = await setCustomScrapeOptions(scrape_options);
1093
+ // }
1094
+ kbs.push(kb)
969
1095
  })
970
1096
 
971
1097
  let operations = kbs.map(doc => {
@@ -979,13 +1105,20 @@ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
979
1105
  }
980
1106
  })
981
1107
 
1108
+ console.log("kbs: ", kbs);
1109
+
982
1110
  saveBulk(operations, kbs, project_id).then((result) => {
983
1111
 
1112
+ let ns = namespaces.find(n => n.id === namespace_id);
1113
+ let engine = ns.engine || default_engine;
1114
+
984
1115
  let resources = result.map(({ name, status, __v, createdAt, updatedAt, id_project, ...keepAttrs }) => keepAttrs)
985
- resources = resources.map(({ _id, ...rest }) => {
986
- return { id: _id, webhook: webhook, ...rest };
1116
+ resources = resources.map(({ _id, scrape_options, ...rest }) => {
1117
+ return { id: _id, webhook: webhook, parameters_scrape_type_4: scrape_options, engine: engine, ...rest}
987
1118
  });
1119
+ console.log("resources to be sent to worker: ", resources);
988
1120
  winston.verbose("resources to be sent to worker: ", resources);
1121
+
989
1122
  scheduleScrape(resources);
990
1123
  res.status(200).send(result);
991
1124
 
@@ -1085,9 +1218,13 @@ router.post('/csv', upload.single('uploadFile'), async (req, res) => {
1085
1218
  })
1086
1219
 
1087
1220
  saveBulk(operations, kbs, project_id).then((result) => {
1221
+
1222
+ let ns = namespaces.find(n => n.id === namespace_id);
1223
+ let engine = ns.engine || default_engine;
1224
+
1088
1225
  let resources = result.map(({ name, status, __v, createdAt, updatedAt, id_project, ...keepAttrs }) => keepAttrs)
1089
1226
  resources = resources.map(({ _id, ...rest}) => {
1090
- return { id: _id, webhooh: webhook, ...rest };
1227
+ return { id: _id, webhooh: webhook, engine: engine, ...rest };
1091
1228
  })
1092
1229
  winston.verbose("resources to be sent to worker: ", resources);
1093
1230
  if (!process.env.NODE_ENV) {
@@ -1166,7 +1303,6 @@ router.delete('/:kb_id', async (req, res) => {
1166
1303
  let kb_id = req.params.kb_id;
1167
1304
  winston.verbose("delete kb_id " + kb_id);
1168
1305
 
1169
-
1170
1306
  let kb = await KB.findOne({ id_project: project_id, _id: kb_id}).catch((err) => {
1171
1307
  winston.warn("Unable to find kb. Error: ", err);
1172
1308
  return res.status(500).send({ success: false, error: err })
@@ -1186,6 +1322,16 @@ router.delete('/:kb_id', async (req, res) => {
1186
1322
  data.namespace = project_id;
1187
1323
  }
1188
1324
 
1325
+ let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
1326
+ winston.error("find namespaces error: ", err)
1327
+ res.status(500).send({ success: false, error: err })
1328
+ })
1329
+
1330
+ let ns = namespaces.find(n => n.id === data.namespace);
1331
+ data.engine = ns.engine || default_engine;
1332
+
1333
+ winston.verbose("/:delete_id data: ", data);
1334
+
1189
1335
  openaiService.deleteIndex(data).then((resp) => {
1190
1336
  winston.debug("delete resp: ", resp.data);
1191
1337
  if (resp.data.success === true) {
@@ -1369,6 +1515,30 @@ async function getKeyFromIntegrations(project_id) {
1369
1515
  }
1370
1516
  })
1371
1517
  }
1518
+
1519
+ async function setDefaultScrapeOptions() {
1520
+ return {
1521
+ tags_to_extract: ["body"],
1522
+ unwanted_tags: [],
1523
+ unwanted_classnames: []
1524
+ }
1525
+ }
1526
+
1527
+ async function setCustomScrapeOptions(options) {
1528
+ if (!options) {
1529
+ options = await setDefaultScrapeOptions();
1530
+ } else {
1531
+ if (!options.tags_to_extract || options.tags_to_extract.length == 0) {
1532
+ options.tags_to_extract = ["body"];
1533
+ }
1534
+ if (!options.unwanted_tags) {
1535
+ options.unwanted_tags = [];
1536
+ }
1537
+ if (!options.unwanted_classnames) {
1538
+ options.unwanted_classnames = [];
1539
+ }
1540
+ }
1541
+ }
1372
1542
  /**
1373
1543
  * ****************************************
1374
1544
  * Utils Methods Section - End
package/routes/project.js CHANGED
@@ -13,7 +13,7 @@ var Group = require('../models/group');
13
13
 
14
14
  var winston = require('../config/winston');
15
15
  var roleChecker = require('../middleware/has-role');
16
-
16
+ var config = require('../config/database');
17
17
 
18
18
  // THE THREE FOLLOWS IMPORTS ARE USED FOR AUTHENTICATION IN THE ROUTE
19
19
  var passport = require('passport');
@@ -25,6 +25,24 @@ var orgUtil = require("../utils/orgUtil");
25
25
  var cacheEnabler = require("../services/cacheEnabler");
26
26
  var mongoose = require('mongoose');
27
27
 
28
+ var jwt = require('jsonwebtoken');
29
+ // CHECK IT ASAP!!!!
30
+ let configSecret = process.env.GLOBAL_SECRET || config.secret;
31
+ var pKey = process.env.GLOBAL_SECRET_OR_PRIVATE_KEY;
32
+ // console.log("pKey",pKey);
33
+
34
+ if (pKey) {
35
+ configSecret = pKey.replace(/\\n/g, '\n');
36
+ }
37
+
38
+ let pubConfigSecret = process.env.GLOBAL_SECRET || config.secret;
39
+ var pubKey = process.env.GLOBAL_SECRET_OR_PUB_KEY;
40
+ if (pubKey) {
41
+ pubConfigSecret = pubKey.replace(/\\n/g, '\n');
42
+ }
43
+ // CHECK IT ASAP!!!!
44
+
45
+
28
46
  router.post('/', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], async (req, res) => {
29
47
 
30
48
  // create(name, createdBy, settings)
@@ -735,6 +753,95 @@ Project.findByIdAndUpdate(req.params.projectid, { $pull: { bannedUsers: { "_id":
735
753
 
736
754
  });
737
755
 
756
+ router.get('/all', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], function (req, res) {
757
+
758
+ if (req.headers.authorization) {
759
+
760
+ let token = req.headers.authorization.split(" ")[1];
761
+ let decode = jwt.verify(token, pubConfigSecret)
762
+ if (decode && (decode.email === process.env.ADMIN_EMAIL)) {
763
+
764
+ Project.aggregate([
765
+ // {
766
+ // $match: {
767
+ // status: 100,
768
+ // //createdAt: { $gte: startDate}
769
+ // },
770
+ // },
771
+ {
772
+ $sort: {
773
+ createdAt: -1
774
+ },
775
+ },
776
+ {
777
+ $lookup: {
778
+ from: 'project_users',
779
+ localField: '_id',
780
+ foreignField: 'id_project',
781
+ as: 'project_user',
782
+ pipeline: [
783
+ { $match: { role: 'owner' } }
784
+ ]
785
+ }
786
+ },
787
+ {
788
+ $addFields: {
789
+ project_user: { $arrayElemAt: ['$project_user', 0] }
790
+ }
791
+ },
792
+ {
793
+ $lookup: {
794
+ from: 'users',
795
+ localField: 'project_user.id_user',
796
+ foreignField: '_id',
797
+ as: 'user'
798
+ },
799
+ },
800
+ {
801
+ $addFields: {
802
+ user: { $arrayElemAt: ['$user', 0] }
803
+ }
804
+ }
805
+ ])
806
+ .then((projects) => {
807
+ winston.verbose("Projects found " + projects.length)
808
+ // const fieldsToKeep = ['_id', 'name', 'createdBy', 'createdAt', 'user.email' ];
809
+
810
+ const filteredProjects = projects.map(project => {
811
+ const filteredProject = {};
812
+ filteredProject._id = project._id;
813
+ filteredProject.name = project.name;
814
+ filteredProject.owner = project.user?.email;
815
+ filteredProject.createdAt = project.createdAt;
816
+ filteredProject.profile_name = project.profile?.name;
817
+ // ... add other fields here
818
+
819
+ // fieldsToKeep.forEach(field => {
820
+ // if (project[field] !== undefined) {
821
+ // filteredProject[field] = project[field];
822
+ // }
823
+ // });
824
+ return filteredProject;
825
+ });
826
+
827
+ return res.status(200).send(filteredProjects);
828
+ })
829
+ .catch((err) => {
830
+ console.error(err);
831
+ return res.status(500).send({ success: false, error: err});
832
+ });
833
+
834
+ // let updatedUser = await User.findByIdAndUpdate(savedUser._id, { emailverified: true }, { new: true }).exec();
835
+ // winston.debug("updatedUser: ", updatedUser);
836
+ // skipVerificationEmail = true;
837
+ // winston.verbose("skip sending verification email")
838
+ } else {
839
+ return res.status(403).send({ success: false, error: "You don't have the permission required to perform the operation"});
840
+ }
841
+
842
+ }
843
+
844
+ });
738
845
 
739
846
 
740
847
  //roleChecker.hasRole('agent') works because req.params.projectid is valid using :projectid of this method