@tiledesk/tiledesk-server 2.7.26 → 2.7.27
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/CHANGELOG.md +5 -0
- package/app.js +1 -1
- package/config/labels/widget.json +31 -0
- package/event/emailEvent.js +58 -2
- package/models/faq.js +5 -0
- package/models/kb_setting.js +38 -3
- package/package.json +4 -3
- package/pubmodules/s/index.js +2 -2
- package/pubmodules/s/models/subscription-payment.js +2 -2
- package/pubmodules/s/stripe/index.js +2 -2
- package/routes/kb.js +952 -542
- package/routes/openai.js +3 -3
- package/routes/project.js +21 -6
- package/routes/quotes.js +13 -4
- package/services/QuoteManager.js +163 -16
- package/services/emailService.js +53 -1
- package/services/requestService.js +16 -5
- package/template/email/beenInvitedNewUser.html +221 -216
- package/template/email/checkpointReachedEmail.html +92 -0
- package/test/kbRoute.js +1186 -311
- package/test/mock/projectMock.js +2 -3
- package/test/openaiRoute.js +3 -0
- package/test/requestService.js +3 -0
- package/utils/aiUtils.js +41 -5
package/routes/kb.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
var express = require('express');
|
2
|
+
var { Namespace } = require('../models/kb_setting');
|
2
3
|
var { KB } = require('../models/kb_setting');
|
3
4
|
var router = express.Router();
|
4
5
|
var winston = require('../config/winston');
|
@@ -9,6 +10,11 @@ const JobManager = require('../utils/jobs-worker-queue-manager/JobManagerV2');
|
|
9
10
|
const { Scheduler } = require('../services/Scheduler');
|
10
11
|
var configGlobal = require('../config/global');
|
11
12
|
const Sitemapper = require('sitemapper');
|
13
|
+
var mongoose = require('mongoose');
|
14
|
+
const faq = require('../models/faq');
|
15
|
+
const faq_kb = require('../models/faq_kb');
|
16
|
+
|
17
|
+
const { MODELS_MULTIPLIER } = require('../utils/aiUtils');
|
12
18
|
|
13
19
|
const AMQP_MANAGER_URL = process.env.AMQP_MANAGER_URL;
|
14
20
|
const JOB_TOPIC_EXCHANGE = process.env.JOB_TOPIC_EXCHANGE_TRAIN || 'tiledesk-trainer';
|
@@ -16,719 +22,1123 @@ const KB_WEBHOOK_TOKEN = process.env.KB_WEBHOOK_TOKEN || 'kbcustomtoken';
|
|
16
22
|
const apiUrl = process.env.API_URL || configGlobal.apiUrl;
|
17
23
|
|
18
24
|
let jobManager = new JobManager(AMQP_MANAGER_URL, {
|
19
|
-
|
20
|
-
|
21
|
-
|
25
|
+
debug: false,
|
26
|
+
topic: JOB_TOPIC_EXCHANGE,
|
27
|
+
exchange: JOB_TOPIC_EXCHANGE
|
22
28
|
})
|
23
29
|
|
24
30
|
jobManager.connectAndStartPublisher(() => {
|
25
|
-
|
31
|
+
winston.info("ConnectPublisher done");
|
26
32
|
})
|
27
33
|
|
28
|
-
|
34
|
+
let default_preview_settings = {
|
35
|
+
model: 'gpt-3.5-turbo',
|
36
|
+
max_tokens: 128,
|
37
|
+
temperature: 0.7,
|
38
|
+
top_k: 4,
|
39
|
+
context: "You are an awesome AI Assistant."
|
40
|
+
}
|
29
41
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
let sortField = "updatedAt";
|
37
|
-
let text;
|
38
|
-
|
39
|
-
let query = {};
|
40
|
-
query["id_project"] = project_id;
|
41
|
-
|
42
|
-
if (req.query.status) {
|
43
|
-
status = parseInt(req.query.status);
|
44
|
-
query["status"] = status;
|
45
|
-
winston.debug("Get kb status: " + status)
|
46
|
-
}
|
42
|
+
/**
|
43
|
+
* ****************************************
|
44
|
+
* Proxy Section - Start
|
45
|
+
* ****************************************
|
46
|
+
*/
|
47
|
+
router.post('/scrape/single', async (req, res) => {
|
47
48
|
|
48
|
-
|
49
|
-
type = req.query.type;
|
50
|
-
query["type"] = type;
|
51
|
-
winston.debug("Get kb type: " + type);
|
52
|
-
}
|
49
|
+
let project_id = req.projectid;
|
53
50
|
|
54
|
-
|
55
|
-
|
56
|
-
winston.debug("Get kb limit: " + limit)
|
57
|
-
}
|
51
|
+
let data = req.body;
|
52
|
+
winston.debug("/scrape/single data: ", data);
|
58
53
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
54
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
55
|
+
winston.error("find namespaces error: ", err)
|
56
|
+
res.status(500).send({ success: false, error: err })
|
57
|
+
})
|
58
|
+
|
59
|
+
if (!namespaces || namespaces.length == 0) {
|
60
|
+
let alert = "No namespace found for the selected project " + project_id + ". Cannot add content to a non-existent namespace."
|
61
|
+
winston.warn(alert);
|
62
|
+
res.status(403).send(alert);
|
63
|
+
}
|
63
64
|
|
64
|
-
|
65
|
-
winston.debug("Get kb skip page: " + skip);
|
65
|
+
let namespaceIds = namespaces.map(namespace => namespace.id);
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
KB.findById(data.id, (err, kb) => {
|
68
|
+
if (err) {
|
69
|
+
winston.error("findById error: ", err);
|
70
|
+
return res.status(500).send({ success: false, error: err });
|
70
71
|
}
|
71
72
|
|
72
|
-
if (
|
73
|
-
|
74
|
-
winston.debug("Get kb sortField: " + sortField)
|
73
|
+
else if (!kb) {
|
74
|
+
return res.status(404).send({ success: false, error: "Unable to find the kb requested" })
|
75
75
|
}
|
76
|
+
else {
|
77
|
+
|
78
|
+
if (!namespaceIds.includes(kb.namespace)) {
|
79
|
+
return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
|
80
|
+
}
|
81
|
+
|
82
|
+
let json = {
|
83
|
+
id: kb._id,
|
84
|
+
type: kb.type,
|
85
|
+
source: kb.source,
|
86
|
+
content: "",
|
87
|
+
namespace: kb.namespace
|
88
|
+
}
|
89
|
+
|
90
|
+
if (kb.content) {
|
91
|
+
json.content = kb.content;
|
92
|
+
}
|
93
|
+
|
94
|
+
startScrape(json).then((response) => {
|
95
|
+
winston.verbose("startScrape response: ", response);
|
96
|
+
res.status(200).send(response);
|
97
|
+
}).catch((err) => {
|
98
|
+
winston.error("startScrape err: ", err);
|
99
|
+
res.status(500).send({ success: false, error: err });
|
100
|
+
})
|
76
101
|
|
77
|
-
if (req.query.search) {
|
78
|
-
text = req.query.search;
|
79
|
-
query['source'] = new RegExp(text);
|
80
|
-
winston.debug("Get kb text: " + text);
|
81
102
|
}
|
103
|
+
})
|
82
104
|
|
83
|
-
|
84
|
-
sortQuery[sortField] = direction;
|
85
|
-
winston.debug("Get kb sortQuery: " + sortQuery);
|
105
|
+
})
|
86
106
|
|
87
|
-
|
88
|
-
if (err) {
|
89
|
-
winston.error("Find all kbs error: ", err);
|
90
|
-
}
|
91
|
-
winston.debug("KBs count: ", kbs_count);
|
92
|
-
|
93
|
-
KB.find(query)
|
94
|
-
.skip(skip)
|
95
|
-
.limit(limit)
|
96
|
-
.sort(sortQuery)
|
97
|
-
.exec((err, kbs) => {
|
98
|
-
if (err) {
|
99
|
-
winston.error("Find all kbs error: ", err);
|
100
|
-
return res.status(500).send({ success: false, error: err });
|
101
|
-
}
|
102
|
-
|
103
|
-
winston.debug("KBs found: ", kbs);
|
104
|
-
|
105
|
-
let response = {
|
106
|
-
count: kbs_count,
|
107
|
-
query: {},
|
108
|
-
kbs: kbs
|
109
|
-
}
|
110
|
-
if (status) {
|
111
|
-
response.query.status = status;
|
112
|
-
}
|
113
|
-
if (limit) {
|
114
|
-
response.query.limit = limit;
|
115
|
-
}
|
116
|
-
if (status) {
|
117
|
-
response.query.page = page;
|
118
|
-
}
|
119
|
-
if (sortField) {
|
120
|
-
response.query.sortField = sortField;
|
121
|
-
}
|
122
|
-
if (direction) {
|
123
|
-
response.query.direction = direction;
|
124
|
-
}
|
125
|
-
if (text) {
|
126
|
-
response.query.search = text;
|
127
|
-
}
|
128
|
-
|
129
|
-
|
130
|
-
return res.status(200).send(response);
|
131
|
-
})
|
107
|
+
router.post('/scrape/status', async (req, res) => {
|
132
108
|
|
133
|
-
|
109
|
+
let data = req.body;
|
110
|
+
winston.debug("/scrapeStatus req.body: ", req.body);
|
134
111
|
|
112
|
+
let returnObject = false;
|
135
113
|
|
136
|
-
|
114
|
+
if (req.query &&
|
115
|
+
req.query.returnObject &&
|
116
|
+
(req.query.returnObject === true || req.query.returnObject === 'true')) {
|
117
|
+
returnObject = true;
|
118
|
+
}
|
137
119
|
|
138
|
-
|
120
|
+
openaiService.scrapeStatus(data).then(async (response) => {
|
139
121
|
|
140
|
-
|
122
|
+
winston.debug("scrapeStatus response.data: ", response.data);
|
141
123
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
124
|
+
let update = {};
|
125
|
+
|
126
|
+
if (response.data.status_code) {
|
127
|
+
// update.status = response.data.status_code;
|
128
|
+
update.status = await statusConverter(response.data.status_code)
|
129
|
+
|
130
|
+
}
|
131
|
+
|
132
|
+
KB.findByIdAndUpdate(data.id, update, { new: true }, (err, savedKb) => {
|
133
|
+
|
134
|
+
if (err) {
|
135
|
+
winston.verbose("Status was successfully recovered, but the update on the db failed");
|
136
|
+
winston.error("find kb by id and updated error: ", err);
|
137
|
+
|
138
|
+
if (returnObject) {
|
139
|
+
return res.status(206).send({ warning: "Unable to udpate content on db", message: "The original reply was forwarded", data: response.data });
|
140
|
+
} else {
|
141
|
+
return res.status(200).send(response.data);
|
146
142
|
}
|
143
|
+
}
|
147
144
|
|
148
|
-
|
145
|
+
if (returnObject) {
|
146
|
+
return res.status(200).send(savedKb);
|
147
|
+
} else {
|
148
|
+
return res.status(200).send(response.data);
|
149
|
+
}
|
149
150
|
})
|
151
|
+
|
152
|
+
}).catch((err) => {
|
153
|
+
winston.error("scrapeStatus err: ", err);
|
154
|
+
let status = err.response.status;
|
155
|
+
res.status(status).send({ statusText: err.response.statusText, error: err.response.data.detail });
|
156
|
+
})
|
150
157
|
})
|
151
158
|
|
152
|
-
router.post('/', async (req, res) => {
|
159
|
+
router.post('/qa', async (req, res) => {
|
153
160
|
|
161
|
+
let project_id = req.projectid;
|
162
|
+
let publicKey = false;
|
163
|
+
let data = req.body;
|
154
164
|
|
155
|
-
|
156
|
-
|
165
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
166
|
+
winston.error("find namespaces error: ", err)
|
167
|
+
res.status(500).send({ success: false, error: err })
|
168
|
+
})
|
157
169
|
|
158
|
-
|
159
|
-
|
170
|
+
if (!namespaces || namespaces.length == 0) {
|
171
|
+
let alert = "No namespace found for the selected project " + project_id + ". Cannot add content to a non-existent namespace."
|
172
|
+
winston.warn(alert);
|
173
|
+
res.status(403).send(alert);
|
174
|
+
}
|
160
175
|
|
161
|
-
|
162
|
-
// if (req.body.namespace != req.projectid) {
|
163
|
-
// return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project."})
|
164
|
-
// }
|
165
|
-
// }
|
176
|
+
let namespaceIds = namespaces.map(namespace => namespace.id);
|
166
177
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
winston.verbose("Limit of kbs for current plan: " + kbs_limit);
|
178
|
+
if (!namespaceIds.includes(data.namespace)) {
|
179
|
+
return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
|
180
|
+
}
|
171
181
|
|
172
|
-
|
173
|
-
winston.verbose("Kbs count: " + kbs_count);
|
182
|
+
winston.debug("/qa data: ", data);
|
174
183
|
|
175
|
-
|
176
|
-
|
184
|
+
if (!data.gptkey) {
|
185
|
+
let gptkey = process.env.GPTKEY;
|
186
|
+
if (!gptkey) {
|
187
|
+
return res.status(403).send({ success: false, error: "GPT apikey undefined" })
|
177
188
|
}
|
189
|
+
data.gptkey = gptkey;
|
190
|
+
publicKey = true;
|
191
|
+
}
|
192
|
+
|
193
|
+
let obj = { createdAt: new Date() };
|
178
194
|
|
179
|
-
|
180
|
-
|
181
|
-
|
195
|
+
let quoteManager = req.app.get('quote_manager');
|
196
|
+
if (publicKey === true) {
|
197
|
+
let isAvailable = await quoteManager.checkQuote(req.project, obj, 'tokens');
|
198
|
+
if (isAvailable === false) {
|
199
|
+
return res.status(403).send({ success: false, message: "Tokens quota exceeded", error_code: 13001})
|
182
200
|
}
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
201
|
+
}
|
202
|
+
|
203
|
+
if (data.system_context) {
|
204
|
+
data.system_context = data.system_context + " {context}";
|
205
|
+
}
|
206
|
+
|
207
|
+
openaiService.askNamespace(data).then((resp) => {
|
208
|
+
winston.debug("qa resp: ", resp.data);
|
209
|
+
let answer = resp.data;
|
210
|
+
|
211
|
+
let id = answer.id;
|
212
|
+
let index = id.indexOf("#");
|
213
|
+
if (index != -1) {
|
214
|
+
id = id.substring(index + 1);
|
215
|
+
}
|
216
|
+
|
217
|
+
KB.findById(id, (err, resource) => {
|
218
|
+
|
219
|
+
let multiplier = MODELS_MULTIPLIER[data.model];
|
220
|
+
if (!multiplier) {
|
221
|
+
multiplier = 1;
|
222
|
+
winston.info("No multiplier found for AI model")
|
223
|
+
}
|
224
|
+
obj.multiplier = multiplier;
|
225
|
+
obj.tokens = answer.prompt_token_size;
|
226
|
+
|
227
|
+
let incremented_key = quoteManager.incrementTokenCount(req.project, obj);
|
228
|
+
winston.verbose("incremented_key: ", incremented_key);
|
229
|
+
|
230
|
+
if (err) {
|
231
|
+
winston.error("Unable to find resource with id " + id + " in namespace " + answer.namespace + ". The standard answer is returned.")
|
232
|
+
return res.status(200).send(resp.data);
|
233
|
+
}
|
234
|
+
|
235
|
+
if (!resource) {
|
236
|
+
winston.error("Resource with id " + id + " not found in namespace " + answer.namespace + ". The standard answer is returned.")
|
237
|
+
return res.status(200).send(resp.data);
|
238
|
+
}
|
239
|
+
|
240
|
+
answer.source = resource.name;
|
241
|
+
return res.status(200).send(answer);
|
242
|
+
})
|
243
|
+
|
244
|
+
|
245
|
+
}).catch((err) => {
|
246
|
+
winston.error("qa err: ", err);
|
247
|
+
winston.error("qa err.response: ", err.response);
|
248
|
+
if (err.response && err.response.status) {
|
249
|
+
let status = err.response.status;
|
250
|
+
res.status(status).send({ success: false, statusText: err.response.statusText, error: err.response.data.detail });
|
191
251
|
}
|
192
|
-
|
193
|
-
|
252
|
+
else {
|
253
|
+
res.status(500).send({ success: false, error: err });
|
194
254
|
}
|
195
|
-
winston.debug("adding kb: ", new_kb);
|
196
255
|
|
197
|
-
|
198
|
-
|
199
|
-
winston.error("findOneAndUpdate with upsert error: ", err);
|
200
|
-
res.status(500).send({ success: false, error: err });
|
201
|
-
}
|
202
|
-
else {
|
256
|
+
})
|
257
|
+
})
|
203
258
|
|
204
|
-
|
259
|
+
router.delete('/delete', async (req, res) => {
|
205
260
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
id: raw.value._id,
|
210
|
-
type: raw.value.type,
|
211
|
-
source: raw.value.source,
|
212
|
-
content: "",
|
213
|
-
namespace: raw.value.namespace,
|
214
|
-
webhook: webhook
|
215
|
-
}
|
216
|
-
winston.debug("json: ", json);
|
261
|
+
let project_id = req.projectid;
|
262
|
+
let data = req.body;
|
263
|
+
winston.debug("/delete data: ", data);
|
217
264
|
|
218
|
-
|
219
|
-
|
220
|
-
|
265
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
266
|
+
winston.error("find namespaces error: ", err)
|
267
|
+
res.status(500).send({ success: false, error: err })
|
268
|
+
})
|
221
269
|
|
222
|
-
|
270
|
+
if (!namespaces || namespaces.length == 0) {
|
271
|
+
let alert = "No namespace found for the selected project " + project_id + ". Cannot add content to a non-existent namespace."
|
272
|
+
winston.warn(alert);
|
273
|
+
res.status(403).send(alert);
|
274
|
+
}
|
223
275
|
|
224
|
-
|
225
|
-
scheduleScrape(resources);
|
276
|
+
let namespaceIds = namespaces.map(namespace => namespace.id);
|
226
277
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
// winston.error("startScrape err: ", err);
|
231
|
-
// })
|
278
|
+
if (!namespaceIds.includes(data.namespace)) {
|
279
|
+
return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
|
280
|
+
}
|
232
281
|
|
233
|
-
|
234
|
-
|
282
|
+
openaiService.deleteIndex(data).then((resp) => {
|
283
|
+
winston.debug("delete resp: ", resp.data);
|
284
|
+
res.status(200).send(resp.data);
|
285
|
+
}).catch((err) => {
|
286
|
+
winston.error("delete err: ", err);
|
287
|
+
let status = err.response.status;
|
288
|
+
res.status(status).send({ statusText: err.response.statusText, error: err.response.data.detail });
|
289
|
+
})
|
235
290
|
|
236
291
|
})
|
237
292
|
|
238
|
-
router.
|
293
|
+
router.delete('/deleteall', async (req, res) => {
|
239
294
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
}
|
295
|
+
let project_id = req.projectid;
|
296
|
+
let data = req.body;
|
297
|
+
winston.debug('/delete all data: ', data);
|
298
|
+
|
299
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
300
|
+
winston.error("find namespaces error: ", err)
|
301
|
+
res.status(500).send({ success: false, error: err })
|
302
|
+
})
|
303
|
+
|
304
|
+
if (!namespaces || namespaces.length == 0) {
|
305
|
+
let alert = "No namespace found for the selected project " + project_id + ". Cannot add content to a non-existent namespace."
|
306
|
+
winston.warn(alert);
|
307
|
+
res.status(403).send(alert);
|
308
|
+
}
|
309
|
+
|
310
|
+
let namespaceIds = namespaces.map(namespace => namespace.id);
|
311
|
+
|
312
|
+
if (!namespaceIds.includes(data.namespace)) {
|
313
|
+
return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
|
314
|
+
}
|
315
|
+
|
316
|
+
openaiService.deleteNamespace(data).then((resp) => {
|
317
|
+
winston.debug("delete namespace resp: ", resp.data);
|
318
|
+
res.status(200).send(resp.data);
|
319
|
+
}).catch((err) => {
|
320
|
+
winston.error("delete namespace err: ", err);
|
321
|
+
let status = err.response.status;
|
322
|
+
res.status(status).send({ statusText: err.response.statusText, error: err.response.data.detail });
|
323
|
+
})
|
324
|
+
})
|
325
|
+
/**
|
326
|
+
* ****************************************
|
327
|
+
* Proxy Section - Start
|
328
|
+
* ****************************************
|
329
|
+
*/
|
247
330
|
|
248
|
-
let project_id = req.projectid;
|
249
331
|
|
250
|
-
|
251
|
-
let limits = await quoteManager.getPlanLimits(req.project);
|
252
|
-
let kbs_limit = limits.kbs;
|
253
|
-
winston.verbose("Limit of kbs for current plan: " + kbs_limit);
|
332
|
+
//----------------------------------------
|
254
333
|
|
255
|
-
let kbs_count = await KB.countDocuments({ id_project: project_id }).exec();
|
256
|
-
winston.verbose("Kbs count: " + kbs_count);
|
257
334
|
|
258
|
-
|
259
|
-
|
260
|
-
|
335
|
+
/**
|
336
|
+
* ****************************************
|
337
|
+
* Namespace Section - Start
|
338
|
+
* ****************************************
|
339
|
+
*/
|
340
|
+
router.get('/namespace/all', async (req, res) => {
|
261
341
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
}
|
342
|
+
let project_id = req.projectid;
|
343
|
+
|
344
|
+
Namespace.find({ id_project: project_id }).lean().exec((err, namespaces) => {
|
266
345
|
|
267
|
-
if (
|
268
|
-
|
269
|
-
|
346
|
+
if (err) {
|
347
|
+
winston.error("find namespaces error: ", err);
|
348
|
+
return res.status(500).send({ success: false, error: err });
|
270
349
|
}
|
350
|
+
else if (namespaces.length == 0) {
|
271
351
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
type: 'url',
|
281
|
-
content: "",
|
282
|
-
namespace: project_id,
|
283
|
-
status: -1
|
284
|
-
})
|
285
|
-
})
|
352
|
+
// Create Default Namespace
|
353
|
+
let new_namespace = new Namespace({
|
354
|
+
id_project: project_id,
|
355
|
+
id: project_id,
|
356
|
+
name: "Default",
|
357
|
+
preview_settings: default_preview_settings,
|
358
|
+
default: true
|
359
|
+
})
|
286
360
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
update: doc,
|
292
|
-
upsert: true,
|
293
|
-
returnOriginal: false
|
294
|
-
}
|
361
|
+
new_namespace.save((err, savedNamespace) => {
|
362
|
+
if (err) {
|
363
|
+
winston.error("create default namespace error: ", err);
|
364
|
+
return res.status(500).send({ success: false, error: err });
|
295
365
|
}
|
296
|
-
})
|
297
366
|
|
298
|
-
|
367
|
+
let namespaceObj = savedNamespace.toObject();
|
368
|
+
delete namespaceObj._id;
|
369
|
+
delete namespaceObj.__v;
|
299
370
|
|
300
|
-
let
|
301
|
-
|
302
|
-
return { id: _id, webhook: webhook, ...rest };
|
303
|
-
});
|
304
|
-
winston.verbose("resources to be sent to worker: ", resources);
|
305
|
-
scheduleScrape(resources);
|
306
|
-
res.status(200).send(result);
|
371
|
+
let namespaces = [];
|
372
|
+
namespaces.push(namespaceObj);
|
307
373
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
})
|
374
|
+
return res.status(200).send(namespaces);
|
375
|
+
|
376
|
+
})
|
312
377
|
|
378
|
+
} else {
|
379
|
+
|
380
|
+
const namespaceObjArray = namespaces.map(({ _id, __v, ...keepAttrs }) => keepAttrs)
|
381
|
+
winston.debug("namespaceObjArray: ", namespaceObjArray);
|
382
|
+
return res.status(200).send(namespaceObjArray);
|
383
|
+
}
|
384
|
+
})
|
313
385
|
})
|
314
386
|
|
315
|
-
router.
|
387
|
+
router.get('/namespace/:id/chatbots', async (req, res) => {
|
388
|
+
|
389
|
+
let project_id = req.projectid;
|
390
|
+
let namespace_id = req.params.id;
|
391
|
+
|
392
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
393
|
+
winston.error("find namespaces error: ", err)
|
394
|
+
res.status(500).send({ success: false, error: err })
|
395
|
+
})
|
396
|
+
|
397
|
+
let namespaceIds = namespaces.map(namespace => namespace.id);
|
398
|
+
if (!namespaceIds.includes(namespace_id)) {
|
399
|
+
return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
|
400
|
+
}
|
401
|
+
|
402
|
+
let intents = await faq.find({ id_project: project_id, 'actions.namespace': namespace_id }).catch((err) => {
|
403
|
+
winston.error("Error find intents: ", err);
|
404
|
+
return res.status(500).send({ success: false, message: 'Unable to retrieve intents using the selected namespace', error: err });
|
405
|
+
})
|
406
|
+
|
407
|
+
if (!intents || intents.length == 0) {
|
408
|
+
winston.verbose("No intents found for the selected chatbot")
|
409
|
+
return res.status(200).send({ success: false, message: "No intents found for the selected chatbot" });
|
410
|
+
}
|
411
|
+
|
412
|
+
let chatbots = intents.map(i => i.id_faq_kb);
|
413
|
+
let uniqueChatbots = [...new Set(chatbots)];
|
414
|
+
|
415
|
+
let chatbotsArray = [];
|
416
|
+
let chatbotPromises = uniqueChatbots.map(async (c_id) => {
|
417
|
+
try {
|
418
|
+
let chatbot = await faq_kb.findById(c_id);
|
419
|
+
if (chatbot) {
|
420
|
+
let data = {
|
421
|
+
_id: chatbot._id,
|
422
|
+
name: chatbot.name
|
423
|
+
}
|
424
|
+
chatbotsArray.push(data);
|
425
|
+
}
|
426
|
+
} catch (err) {
|
427
|
+
winston.error("error getting chatbot: ", err);
|
428
|
+
}
|
429
|
+
});
|
316
430
|
|
317
|
-
|
318
|
-
|
319
|
-
const sitemap = new Sitemapper({
|
320
|
-
url: sitemap_url,
|
321
|
-
timeout: 15000
|
322
|
-
});
|
431
|
+
await Promise.all(chatbotPromises);
|
323
432
|
|
324
|
-
|
325
|
-
winston.debug("data: ", data);
|
326
|
-
res.status(200).send(data);
|
327
|
-
}).catch((err) => {
|
328
|
-
console.error("err ", err)
|
329
|
-
res.status(500).send({ success: false, error: err });
|
330
|
-
})
|
433
|
+
winston.debug("chatbotsArray: ", chatbotsArray);
|
331
434
|
|
435
|
+
res.status(200).send(chatbotsArray);
|
332
436
|
})
|
333
437
|
|
334
|
-
router.
|
438
|
+
router.post('/namespace', async (req, res) => {
|
439
|
+
|
440
|
+
let project_id = req.projectid;
|
441
|
+
let body = req.body;
|
442
|
+
winston.debug("add namespace body: ", body);
|
443
|
+
|
444
|
+
var namespace_id = mongoose.Types.ObjectId();
|
445
|
+
let new_namespace = new Namespace({
|
446
|
+
id_project: project_id,
|
447
|
+
id: namespace_id,
|
448
|
+
name: body.name,
|
449
|
+
preview_settings: default_preview_settings,
|
450
|
+
})
|
451
|
+
|
452
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
453
|
+
winston.error("find namespaces error: ", err)
|
454
|
+
//res.status(500).send({ success: false, error: err })
|
455
|
+
})
|
456
|
+
if (!namespaces || namespaces.length == 0) {
|
457
|
+
new_namespace.default = true;
|
458
|
+
}
|
459
|
+
|
460
|
+
new_namespace.save((err, savedNamespace) => {
|
461
|
+
if (err) {
|
462
|
+
winston.error("create namespace error: ", err);
|
463
|
+
return res.status(500).send({ success: false, error: err });
|
464
|
+
}
|
335
465
|
|
336
|
-
let
|
337
|
-
|
466
|
+
let namespaceObj = savedNamespace.toObject();
|
467
|
+
delete namespaceObj._id;
|
468
|
+
delete namespaceObj.__v;
|
469
|
+
return res.status(200).send(namespaceObj);
|
470
|
+
})
|
471
|
+
})
|
338
472
|
|
339
|
-
|
473
|
+
router.put('/namespace/:id', async (req, res) => {
|
474
|
+
|
475
|
+
let namespace_id = req.params.id;
|
476
|
+
let body = req.body;
|
477
|
+
winston.debug("update namespace body: ", body);
|
340
478
|
|
341
|
-
|
342
|
-
|
479
|
+
let update = {};
|
480
|
+
|
481
|
+
if (body.name) {
|
482
|
+
update.name = body.name;
|
483
|
+
}
|
484
|
+
if (body.preview_settings) {
|
485
|
+
update.preview_settings = body.preview_settings;
|
486
|
+
}
|
487
|
+
|
488
|
+
Namespace.findOneAndUpdate({ id: namespace_id }, update, { new: true, upsert: true }, (err, updatedNamespace) => {
|
489
|
+
if (err) {
|
490
|
+
winston.error("update namespace error: ", err);
|
491
|
+
return res.status(500).send({ success: false, error: err });
|
343
492
|
}
|
344
493
|
|
345
|
-
|
346
|
-
|
494
|
+
let namespaceObj = updatedNamespace.toObject();
|
495
|
+
delete namespaceObj._id;
|
496
|
+
delete namespaceObj.__v;
|
497
|
+
res.status(200).send(namespaceObj);
|
498
|
+
})
|
499
|
+
})
|
500
|
+
|
501
|
+
router.delete('/namespace/:id', async (req, res) => {
|
502
|
+
|
503
|
+
let id_project = req.projectid;
|
504
|
+
let namespace_id = req.params.id;
|
505
|
+
|
506
|
+
let data = {
|
507
|
+
namespace: namespace_id
|
508
|
+
}
|
509
|
+
|
510
|
+
if (req.query.contents_only && (req.query.contents_only === true || req.query.contents_only === 'true')) {
|
511
|
+
|
512
|
+
openaiService.deleteNamespace(data).then(async (resp) => {
|
513
|
+
winston.debug("delete namespace resp: ", resp.data);
|
514
|
+
|
515
|
+
let deleteResponse = await KB.deleteMany({ id_project: id_project, namespace: namespace_id }).catch((err) => {
|
516
|
+
winston.error("deleteMany error: ", err);
|
517
|
+
return res.status(500).send({ success: false, error: err });
|
518
|
+
})
|
519
|
+
winston.debug("delete all contents response: ", deleteResponse);
|
520
|
+
|
521
|
+
return res.status(200).send({ success: true, message: "All contents deleted successfully" })
|
522
|
+
|
523
|
+
}).catch((err) => {
|
524
|
+
|
525
|
+
winston.error("deleteNamespace err: ", err);
|
526
|
+
winston.error("deleteNamespace err.response: ", err.response);
|
527
|
+
if (err.response && err.response.status) {
|
528
|
+
let status = err.response.status;
|
529
|
+
res.status(status).send({ success: false, statusText: err.response.statusText, error: err.response.data.detail });
|
530
|
+
} else {
|
531
|
+
res.status(500).send({ success: false, message: "Unable to delete namespace", error: err })
|
532
|
+
}
|
533
|
+
})
|
534
|
+
|
535
|
+
} else {
|
536
|
+
|
537
|
+
let namespace = await Namespace.findOne({ id: namespace_id }).catch((err) => {
|
538
|
+
winston.error("findOne namespace error: ", err);
|
539
|
+
return res.status(500).send({ success: false, error: err });
|
540
|
+
})
|
541
|
+
if (namespace.default === true) {
|
542
|
+
winston.error("Default namespace cannot be deleted");
|
543
|
+
return res.status(403).send({ success: false, error: "Default namespace cannot be deleted" });
|
347
544
|
}
|
348
545
|
|
349
|
-
|
546
|
+
openaiService.deleteNamespace(data).then(async (resp) => {
|
547
|
+
winston.debug("delete namespace resp: ", resp.data);
|
350
548
|
|
351
|
-
|
549
|
+
let deleteResponse = await KB.deleteMany({ id_project: id_project, namespace: namespace_id }).catch((err) => {
|
550
|
+
winston.error("deleteMany error: ", err);
|
551
|
+
return res.status(500).send({ success: false, error: err });
|
552
|
+
})
|
553
|
+
winston.debug("delete all contents response: ", deleteResponse);
|
352
554
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
555
|
+
let deleteNamespaceResponse = await Namespace.findOneAndDelete({ id: namespace_id }).catch((err) => {
|
556
|
+
winston.error("deleteOne namespace error: ", err);
|
557
|
+
return res.status(500).send({ success: false, error: err });
|
558
|
+
})
|
559
|
+
winston.debug("delete namespace response: ", deleteNamespaceResponse);
|
357
560
|
|
358
|
-
|
359
|
-
winston.debug("Try to updating a non-existing kb");
|
360
|
-
return res.status(400).send({ success: false, message: "Content not found" })
|
361
|
-
}
|
561
|
+
return res.status(200).send({ success: true, message: "Namespace deleted succesfully" })
|
362
562
|
|
363
|
-
|
563
|
+
}).catch((err) => {
|
564
|
+
let status = 400;
|
565
|
+
if (err.response && err.response.status) {
|
566
|
+
status = err.response.status;
|
567
|
+
}
|
568
|
+
return res.status(status).send({ success: false, error: "Unable to delete namespace" })
|
364
569
|
})
|
365
570
|
|
571
|
+
}
|
572
|
+
|
573
|
+
|
574
|
+
|
366
575
|
})
|
576
|
+
/**
|
577
|
+
* ****************************************
|
578
|
+
* Namespace Section - End
|
579
|
+
* ****************************************
|
580
|
+
*/
|
367
581
|
|
368
|
-
// PROXY PUGLIA AI V2 - START
|
369
|
-
router.post('/scrape/single', async (req, res) => {
|
370
582
|
|
371
|
-
|
372
|
-
|
583
|
+
//----------------------------------------
|
584
|
+
|
373
585
|
|
374
|
-
|
586
|
+
/**
|
587
|
+
* ****************************************
|
588
|
+
* Content Section - Start
|
589
|
+
* ****************************************
|
590
|
+
*/
|
591
|
+
router.get('/', async (req, res) => {
|
592
|
+
|
593
|
+
let project_id = req.projectid;
|
594
|
+
let namespace = req.query.namespace;
|
595
|
+
if (!namespace) {
|
596
|
+
return res.status(400).send({ success: false, error: "queryParam 'namespace' is not defined" })
|
597
|
+
}
|
598
|
+
let status;
|
599
|
+
let type;
|
600
|
+
let limit = 200;
|
601
|
+
let page = 0;
|
602
|
+
let direction = -1;
|
603
|
+
let sortField = "updatedAt";
|
604
|
+
let text;
|
605
|
+
|
606
|
+
let query = {};
|
607
|
+
query["id_project"] = project_id;
|
608
|
+
query["namespace"] = namespace;
|
609
|
+
|
610
|
+
if (req.query.status) {
|
611
|
+
status = parseInt(req.query.status);
|
612
|
+
query["status"] = status;
|
613
|
+
winston.debug("Get kb status: " + status)
|
614
|
+
}
|
615
|
+
|
616
|
+
if (req.query.type) {
|
617
|
+
type = req.query.type;
|
618
|
+
query["type"] = type;
|
619
|
+
winston.debug("Get kb type: " + type);
|
620
|
+
}
|
621
|
+
|
622
|
+
if (req.query.limit) {
|
623
|
+
limit = parseInt(req.query.limit);
|
624
|
+
winston.debug("Get kb limit: " + limit)
|
625
|
+
}
|
626
|
+
|
627
|
+
if (req.query.page) {
|
628
|
+
page = parseInt(req.query.page);
|
629
|
+
winston.debug("Get kb page: " + page)
|
630
|
+
}
|
631
|
+
|
632
|
+
let skip = page * limit;
|
633
|
+
winston.debug("Get kb skip page: " + skip);
|
634
|
+
|
635
|
+
if (req.query.direction) {
|
636
|
+
direction = parseInt(req.query.direction)
|
637
|
+
winston.debug("Get kb direction: " + direction)
|
638
|
+
}
|
639
|
+
|
640
|
+
if (req.query.sortField) {
|
641
|
+
sortField = req.query.sortField;
|
642
|
+
winston.debug("Get kb sortField: " + sortField)
|
643
|
+
}
|
644
|
+
|
645
|
+
if (req.query.search) {
|
646
|
+
text = req.query.search;
|
647
|
+
query['source'] = new RegExp(text);
|
648
|
+
winston.debug("Get kb text: " + text);
|
649
|
+
}
|
650
|
+
|
651
|
+
let sortQuery = {};
|
652
|
+
sortQuery[sortField] = direction;
|
653
|
+
winston.debug("Get kb sortQuery: " + sortQuery);
|
654
|
+
|
655
|
+
KB.countDocuments(query, (err, kbs_count) => {
|
656
|
+
if (err) {
|
657
|
+
winston.error("Find all kbs error: ", err);
|
658
|
+
}
|
659
|
+
winston.debug("KBs count: ", kbs_count);
|
660
|
+
|
661
|
+
KB.find(query)
|
662
|
+
.skip(skip)
|
663
|
+
.limit(limit)
|
664
|
+
.sort(sortQuery)
|
665
|
+
.exec((err, kbs) => {
|
375
666
|
if (err) {
|
376
|
-
|
377
|
-
|
667
|
+
winston.error("Find all kbs error: ", err);
|
668
|
+
return res.status(500).send({ success: false, error: err });
|
378
669
|
}
|
379
670
|
|
380
|
-
|
381
|
-
return res.status(404).send({ success: false, error: "Unable to find the kb requested" })
|
382
|
-
}
|
383
|
-
else {
|
384
|
-
|
385
|
-
let json = {
|
386
|
-
id: kb._id,
|
387
|
-
type: kb.type,
|
388
|
-
source: kb.source,
|
389
|
-
content: "",
|
390
|
-
namespace: kb.namespace
|
391
|
-
}
|
392
|
-
|
393
|
-
if (kb.content) {
|
394
|
-
json.content = kb.content;
|
395
|
-
}
|
396
|
-
|
397
|
-
startScrape(json).then((response) => {
|
398
|
-
winston.verbose("startScrape response: ", response);
|
399
|
-
res.status(200).send(response);
|
400
|
-
}).catch((err) => {
|
401
|
-
winston.error("startScrape err: ", err);
|
402
|
-
res.status(500).send({ success: false, error: err });
|
403
|
-
})
|
671
|
+
winston.debug("KBs found: ", kbs);
|
404
672
|
|
673
|
+
let response = {
|
674
|
+
count: kbs_count,
|
675
|
+
query: {},
|
676
|
+
kbs: kbs
|
677
|
+
}
|
678
|
+
if (status) {
|
679
|
+
response.query.status = status;
|
680
|
+
}
|
681
|
+
if (limit) {
|
682
|
+
response.query.limit = limit;
|
683
|
+
}
|
684
|
+
if (status) {
|
685
|
+
response.query.page = page;
|
686
|
+
}
|
687
|
+
if (sortField) {
|
688
|
+
response.query.sortField = sortField;
|
689
|
+
}
|
690
|
+
if (direction) {
|
691
|
+
response.query.direction = direction;
|
692
|
+
}
|
693
|
+
if (text) {
|
694
|
+
response.query.search = text;
|
405
695
|
}
|
406
|
-
})
|
407
696
|
|
408
|
-
})
|
409
697
|
|
410
|
-
|
698
|
+
return res.status(200).send(response);
|
699
|
+
})
|
700
|
+
|
701
|
+
})
|
411
702
|
|
412
|
-
let data = req.body;
|
413
|
-
winston.debug("/scrapeStatus req.body: ", req.body);
|
414
703
|
|
415
|
-
|
704
|
+
})
|
705
|
+
|
706
|
+
router.get('/:kb_id', async (req, res) => {
|
416
707
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
708
|
+
let kb_id = req.params.kb_id;
|
709
|
+
|
710
|
+
KB.findById(kb_id, (err, kb) => {
|
711
|
+
if (err) {
|
712
|
+
winston.error("Find kb by id error: ", err);
|
713
|
+
return res.status(500).send({ success: false, error: err });
|
421
714
|
}
|
422
715
|
|
423
|
-
|
716
|
+
return res.status(200).send(kb);
|
717
|
+
})
|
718
|
+
})
|
424
719
|
|
425
|
-
|
720
|
+
router.post('/', async (req, res) => {
|
426
721
|
|
427
|
-
|
722
|
+
let project_id = req.projectid;
|
723
|
+
let body = req.body;
|
724
|
+
|
725
|
+
if (!body.namespace) {
|
726
|
+
return res.status(400).send({ success: false, error: "parameter 'namespace' is not defined" });
|
727
|
+
}
|
728
|
+
|
729
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
730
|
+
winston.error("find namespaces error: ", err)
|
731
|
+
res.status(500).send({ success: false, error: err })
|
732
|
+
})
|
733
|
+
|
734
|
+
if (!namespaces || namespaces.length == 0) {
|
735
|
+
let alert = "No namespace found for the selected project " + project_id + ". Cannot add content to a non-existent namespace."
|
736
|
+
winston.warn(alert);
|
737
|
+
res.status(403).send(alert);
|
738
|
+
}
|
739
|
+
|
740
|
+
let namespaceIds = namespaces.map(namespace => namespace.id);
|
741
|
+
|
742
|
+
if (!namespaceIds.includes(body.namespace)) {
|
743
|
+
return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
|
744
|
+
}
|
745
|
+
|
746
|
+
let quoteManager = req.app.get('quote_manager');
|
747
|
+
let limits = await quoteManager.getPlanLimits(req.project);
|
748
|
+
let kbs_limit = limits.kbs;
|
749
|
+
winston.verbose("Limit of kbs for current plan: " + kbs_limit);
|
750
|
+
|
751
|
+
let kbs_count = await KB.countDocuments({ id_project: project_id }).exec();
|
752
|
+
winston.verbose("Kbs count: " + kbs_count);
|
753
|
+
|
754
|
+
if (kbs_count >= kbs_limit) {
|
755
|
+
return res.status(403).send({ success: false, error: "Maximum number of resources reached for the current plan", plan_limit: kbs_limit })
|
756
|
+
}
|
757
|
+
|
758
|
+
let total_count = kbs_count + 1;
|
759
|
+
if (total_count > kbs_limit) {
|
760
|
+
return res.status(403).send({ success: false, error: "Cannot exceed the number of resources in the current plan", plan_limit: kbs_limit })
|
761
|
+
}
|
762
|
+
let new_kb = {
|
763
|
+
id_project: project_id,
|
764
|
+
name: body.name,
|
765
|
+
type: body.type,
|
766
|
+
source: body.source,
|
767
|
+
content: body.content,
|
768
|
+
namespace: body.namespace,
|
769
|
+
status: -1
|
770
|
+
}
|
771
|
+
if (!new_kb.namespace) {
|
772
|
+
new_kb.namespace = project_id;
|
773
|
+
}
|
774
|
+
winston.debug("adding kb: ", new_kb);
|
775
|
+
|
776
|
+
KB.findOneAndUpdate({ id_project: project_id, type: 'url', source: new_kb.source }, new_kb, { upsert: true, new: true, rawResult: true }, async (err, raw) => {
|
777
|
+
if (err) {
|
778
|
+
winston.error("findOneAndUpdate with upsert error: ", err);
|
779
|
+
res.status(500).send({ success: false, error: err });
|
780
|
+
}
|
781
|
+
else {
|
428
782
|
|
429
|
-
|
430
|
-
// update.status = response.data.status_code;
|
431
|
-
update.status = await statusConverter(response.data.status_code)
|
432
|
-
|
433
|
-
}
|
783
|
+
res.status(200).send(raw);
|
434
784
|
|
435
|
-
|
785
|
+
let webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
|
436
786
|
|
437
|
-
|
438
|
-
|
439
|
-
|
787
|
+
let json = {
|
788
|
+
id: raw.value._id,
|
789
|
+
type: raw.value.type,
|
790
|
+
source: raw.value.source,
|
791
|
+
content: "",
|
792
|
+
namespace: raw.value.namespace,
|
793
|
+
webhook: webhook
|
794
|
+
}
|
795
|
+
winston.debug("json: ", json);
|
440
796
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
return res.status(200).send(response.data);
|
445
|
-
}
|
446
|
-
}
|
797
|
+
if (raw.value.content) {
|
798
|
+
json.content = raw.value.content;
|
799
|
+
}
|
447
800
|
|
448
|
-
|
449
|
-
return res.status(200).send(savedKb);
|
450
|
-
} else {
|
451
|
-
return res.status(200).send(response.data);
|
452
|
-
}
|
453
|
-
})
|
801
|
+
let resources = [];
|
454
802
|
|
455
|
-
|
456
|
-
|
457
|
-
let status = err.response.status;
|
458
|
-
res.status(status).send({ statusText: err.response.statusText, error: err.response.data.detail });
|
459
|
-
})
|
460
|
-
})
|
803
|
+
resources.push(json);
|
804
|
+
scheduleScrape(resources);
|
461
805
|
|
462
|
-
router.post('/qa', async (req, res) => {
|
463
|
-
let data = req.body;
|
464
|
-
// add or override namespace value if it is passed for security reason
|
465
|
-
data.namespace = req.projectid;
|
466
|
-
winston.debug("/qa data: ", data);
|
467
|
-
|
468
|
-
// if (req.body.namespace != req.projectid) {
|
469
|
-
// return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project."})
|
470
|
-
// }
|
471
|
-
|
472
|
-
if (!data.gptkey) {
|
473
|
-
let gptkey = process.env.GPTKEY;
|
474
|
-
if (!gptkey) {
|
475
|
-
return res.status(403).send({ success: false, error: "GPT apikey undefined" })
|
476
|
-
}
|
477
|
-
data.gptkey = gptkey;
|
478
806
|
}
|
807
|
+
})
|
808
|
+
|
809
|
+
})
|
479
810
|
|
480
|
-
|
481
|
-
winston.debug("qa resp: ", resp.data);
|
482
|
-
let answer = resp.data;
|
811
|
+
router.post('/multi', upload.single('uploadFile'), async (req, res) => {
|
483
812
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
813
|
+
let list;
|
814
|
+
if (req.file) {
|
815
|
+
file_string = req.file.buffer.toString('utf-8');
|
816
|
+
list = file_string.trim().split('\n');
|
817
|
+
} else {
|
818
|
+
list = req.body.list;
|
819
|
+
}
|
820
|
+
|
821
|
+
let project_id = req.projectid;
|
822
|
+
|
823
|
+
let namespace_id = req.query.namespace;
|
824
|
+
if (!namespace_id) {
|
825
|
+
return res.status(400).send({ success: false, error: "queryParam 'namespace' is not defined" })
|
826
|
+
}
|
827
|
+
|
828
|
+
let namespaces = await Namespace.find({ id_project: project_id }).catch((err) => {
|
829
|
+
winston.error("find namespaces error: ", err)
|
830
|
+
res.status(500).send({ success: false, error: err })
|
831
|
+
})
|
832
|
+
|
833
|
+
if (!namespaces || namespaces.length == 0) {
|
834
|
+
let alert = "No namespace found for the selected project " + project_id + ". Cannot add content to a non-existent namespace."
|
835
|
+
winston.warn(alert);
|
836
|
+
res.status(403).send({ success: false, error: alert });
|
837
|
+
}
|
838
|
+
|
839
|
+
let namespaceIds = namespaces.map(namespace => namespace.id);
|
840
|
+
|
841
|
+
if (!namespaceIds.includes(namespace_id)) {
|
842
|
+
return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
|
843
|
+
}
|
844
|
+
|
845
|
+
let quoteManager = req.app.get('quote_manager');
|
846
|
+
let limits = await quoteManager.getPlanLimits(req.project);
|
847
|
+
let kbs_limit = limits.kbs;
|
848
|
+
winston.verbose("Limit of kbs for current plan: " + kbs_limit);
|
849
|
+
|
850
|
+
let kbs_count = await KB.countDocuments({ id_project: project_id }).exec();
|
851
|
+
winston.verbose("Kbs count: " + kbs_count);
|
852
|
+
|
853
|
+
if (kbs_count >= kbs_limit) {
|
854
|
+
return res.status(403).send({ success: false, error: "Maximum number of resources reached for the current plan", plan_limit: kbs_limit })
|
855
|
+
}
|
856
|
+
|
857
|
+
let total_count = kbs_count + list.length;
|
858
|
+
if (total_count > kbs_limit) {
|
859
|
+
return res.status(403).send({ success: false, error: "Cannot exceed the number of resources in the current plan", plan_limit: kbs_limit })
|
860
|
+
}
|
861
|
+
|
862
|
+
if (list.length > 300) {
|
863
|
+
winston.error("Too many urls. Can't index more than 300 urls at a time.");
|
864
|
+
return res.status(403).send({ success: false, error: "Too many urls. Can't index more than 300 urls at a time." })
|
865
|
+
}
|
866
|
+
|
867
|
+
let webhook = apiUrl + '/webhook/kb/status?token=' + KB_WEBHOOK_TOKEN;
|
868
|
+
|
869
|
+
let kbs = [];
|
870
|
+
list.forEach(url => {
|
871
|
+
kbs.push({
|
872
|
+
id_project: project_id,
|
873
|
+
name: url,
|
874
|
+
source: url,
|
875
|
+
type: 'url',
|
876
|
+
content: "",
|
877
|
+
namespace: namespace_id,
|
878
|
+
status: -1
|
879
|
+
})
|
880
|
+
})
|
881
|
+
|
882
|
+
let operations = kbs.map(doc => {
|
883
|
+
return {
|
884
|
+
updateOne: {
|
885
|
+
filter: { id_project: doc.id_project, type: 'url', source: doc.source },
|
886
|
+
update: doc,
|
887
|
+
upsert: true,
|
888
|
+
returnOriginal: false
|
889
|
+
}
|
890
|
+
}
|
891
|
+
})
|
489
892
|
|
490
|
-
|
893
|
+
saveBulk(operations, kbs, project_id).then((result) => {
|
491
894
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
895
|
+
let resources = result.map(({ name, status, __v, createdAt, updatedAt, id_project, ...keepAttrs }) => keepAttrs)
|
896
|
+
resources = resources.map(({ _id, ...rest }) => {
|
897
|
+
return { id: _id, webhook: webhook, ...rest };
|
898
|
+
});
|
899
|
+
winston.verbose("resources to be sent to worker: ", resources);
|
900
|
+
scheduleScrape(resources);
|
901
|
+
res.status(200).send(result);
|
496
902
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
903
|
+
}).catch((err) => {
|
904
|
+
winston.error("Unable to save kbs in bulk ", err)
|
905
|
+
res.status(500).send(err);
|
906
|
+
})
|
501
907
|
|
502
|
-
}).catch((err) => {
|
503
|
-
winston.error("qa err: ", err);
|
504
|
-
console.log(err.response)
|
505
|
-
if (err.response
|
506
|
-
&& err.response.status) {
|
507
|
-
let status = err.response.status;
|
508
|
-
res.status(status).send({ success: false, statusText: err.response.statusText, error: err.response.data.detail });
|
509
|
-
}
|
510
|
-
else {
|
511
|
-
res.status(500).send({ success: false, error: err });
|
512
|
-
}
|
513
|
-
|
514
|
-
})
|
515
908
|
})
|
516
909
|
|
517
|
-
router.
|
910
|
+
router.post('/sitemap', async (req, res) => {
|
518
911
|
|
519
|
-
|
520
|
-
winston.debug("/delete data: ", data);
|
912
|
+
let sitemap_url = req.body.sitemap;
|
521
913
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
914
|
+
const sitemap = new Sitemapper({
|
915
|
+
url: sitemap_url,
|
916
|
+
timeout: 15000
|
917
|
+
});
|
918
|
+
|
919
|
+
sitemap.fetch().then((data) => {
|
920
|
+
winston.debug("data: ", data);
|
921
|
+
res.status(200).send(data);
|
922
|
+
}).catch((err) => {
|
923
|
+
console.error("err ", err)
|
924
|
+
res.status(500).send({ success: false, error: err });
|
925
|
+
})
|
530
926
|
|
531
927
|
})
|
532
928
|
|
533
|
-
router.
|
929
|
+
router.put('/:kb_id', async (req, res) => {
|
534
930
|
|
535
|
-
|
536
|
-
|
931
|
+
let kb_id = req.params.kb_id;
|
932
|
+
winston.verbose("update kb_id " + kb_id);
|
933
|
+
|
934
|
+
let update = {};
|
935
|
+
|
936
|
+
if (req.body.name != undefined) {
|
937
|
+
update.name = req.body.name;
|
938
|
+
}
|
939
|
+
|
940
|
+
if (req.body.status != undefined) {
|
941
|
+
update.status = req.body.status;
|
942
|
+
}
|
943
|
+
|
944
|
+
winston.debug("kb update: ", update);
|
945
|
+
|
946
|
+
KB.findByIdAndUpdate(kb_id, update, { new: true }, (err, savedKb) => {
|
947
|
+
|
948
|
+
if (err) {
|
949
|
+
winston.error("KB findByIdAndUpdate error: ", err);
|
950
|
+
return res.status(500).send({ success: false, error: err });
|
951
|
+
}
|
952
|
+
|
953
|
+
if (!savedKb) {
|
954
|
+
winston.debug("Try to updating a non-existing kb");
|
955
|
+
return res.status(400).send({ success: false, message: "Content not found" })
|
956
|
+
}
|
957
|
+
|
958
|
+
res.status(200).send(savedKb)
|
959
|
+
})
|
537
960
|
|
538
|
-
openaiService.deleteNamespace(data).then((resp) => {
|
539
|
-
winston.debug("delete namespace resp: ", resp.data);
|
540
|
-
res.status(200).send(resp.data);
|
541
|
-
}).catch((err) => {
|
542
|
-
winston.error("delete namespace err: ", err);
|
543
|
-
let status = err.response.status;
|
544
|
-
res.status(status).send({ statusText: err.response.statusText, error: err.response.data.detail });
|
545
|
-
})
|
546
961
|
})
|
547
|
-
// PROXY PUGLIA AI V2 - END
|
548
962
|
|
549
963
|
router.delete('/:kb_id', async (req, res) => {
|
550
964
|
|
551
|
-
|
552
|
-
|
553
|
-
|
965
|
+
let project_id = req.projectid;
|
966
|
+
let kb_id = req.params.kb_id;
|
967
|
+
winston.verbose("delete kb_id " + kb_id);
|
554
968
|
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
969
|
+
let data = {
|
970
|
+
id: kb_id,
|
971
|
+
namespace: project_id
|
972
|
+
}
|
973
|
+
|
974
|
+
openaiService.deleteIndex(data).then((resp) => {
|
975
|
+
winston.debug("delete resp: ", resp.data);
|
559
976
|
|
560
|
-
|
561
|
-
|
977
|
+
if (resp.data.success === true) {
|
978
|
+
KB.findByIdAndDelete(kb_id, (err, deletedKb) => {
|
562
979
|
|
563
|
-
if (
|
564
|
-
|
980
|
+
if (err) {
|
981
|
+
winston.error("Delete kb error: ", err);
|
982
|
+
return res.status(500).send({ success: false, error: err });
|
983
|
+
}
|
984
|
+
res.status(200).send(deletedKb);
|
985
|
+
})
|
565
986
|
|
566
|
-
|
567
|
-
|
568
|
-
return res.status(500).send({ success: false, error: err });
|
569
|
-
}
|
570
|
-
res.status(200).send(deletedKb);
|
571
|
-
})
|
987
|
+
} else {
|
988
|
+
winston.verbose("resp.data: ", resp.data);
|
572
989
|
|
990
|
+
KB.findOneAndDelete({ _id: kb_id, status: { $in: [-1, 3, 4, 100, 300, 400] } }, (err, deletedKb) => {
|
991
|
+
if (err) {
|
992
|
+
winston.error("findOneAndDelete err: ", err);
|
993
|
+
return res.status(500).send({ success: false, error: "Unable to delete the content due to an error" })
|
994
|
+
}
|
995
|
+
else if (!deletedKb) {
|
996
|
+
winston.verbose("Unable to delete the content in indexing status")
|
997
|
+
return res.status(500).send({ success: false, error: "Unable to delete the content in indexing status" })
|
573
998
|
} else {
|
574
|
-
|
575
|
-
|
576
|
-
KB.findOneAndDelete({ _id: kb_id, status: { $in: [-1, 3, 4, 100, 300, 400] } }, (err, deletedKb) => {
|
577
|
-
if (err) {
|
578
|
-
winston.error("findOneAndDelete err: ", err);
|
579
|
-
return res.status(500).send({ success: false, error: "Unable to delete the content due to an error" })
|
580
|
-
}
|
581
|
-
else if (!deletedKb) {
|
582
|
-
winston.verbose("Unable to delete the content in indexing status")
|
583
|
-
return res.status(500).send({ success: false, error: "Unable to delete the content in indexing status" })
|
584
|
-
} else {
|
585
|
-
res.status(200).send(deletedKb);
|
586
|
-
}
|
587
|
-
})
|
999
|
+
res.status(200).send(deletedKb);
|
588
1000
|
}
|
1001
|
+
})
|
1002
|
+
}
|
589
1003
|
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
1004
|
+
}).catch((err) => {
|
1005
|
+
let status = err.response.status;
|
1006
|
+
res.status(status).send({ success: false, statusText: err.response.statusText, error: err.response.data.detail });
|
1007
|
+
})
|
594
1008
|
|
595
1009
|
})
|
596
1010
|
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
1011
|
+
/**
|
1012
|
+
* ****************************************
|
1013
|
+
* Content Section - End
|
1014
|
+
* ****************************************
|
1015
|
+
*/
|
601
1016
|
|
602
|
-
let data = {
|
603
|
-
namespace: namespace_id
|
604
|
-
}
|
605
1017
|
|
606
|
-
|
607
|
-
winston.debug("delete namespace resp: ", resp.data);
|
1018
|
+
//----------------------------------------
|
608
1019
|
|
609
|
-
KB.deleteMany({ id_project: id_project, namespace: namespace_id }, (err, deleteResponse) => {
|
610
|
-
if (err) {
|
611
|
-
winston.error("deleteMany error: ", err);
|
612
|
-
return res.status(500).send({ success: false, error: "Unable to delete namespace due to an error" })
|
613
|
-
}
|
614
|
-
winston.debug("deleteResponse: ", deleteResponse);
|
615
|
-
res.status(200).send({ success: true, message: "Namespace deleted succesfully" })
|
616
|
-
})
|
617
1020
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
1021
|
+
/**
|
1022
|
+
* ****************************************
|
1023
|
+
* Utils Methods Section - Start
|
1024
|
+
* ****************************************
|
1025
|
+
*/
|
623
1026
|
|
624
1027
|
async function saveBulk(operations, kbs, project_id) {
|
625
1028
|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
})
|
1029
|
+
return new Promise((resolve, reject) => {
|
1030
|
+
KB.bulkWrite(operations, { ordered: false }).then((result) => {
|
1031
|
+
winston.verbose("bulkWrite operations result: ", result);
|
1032
|
+
|
1033
|
+
KB.find({ id_project: project_id, source: { $in: kbs.map(kb => kb.source) } }).lean().then((documents) => {
|
1034
|
+
winston.debug("documents: ", documents);
|
1035
|
+
resolve(documents)
|
1036
|
+
}).catch((err) => {
|
1037
|
+
winston.error("Error finding documents ", err)
|
1038
|
+
reject(err);
|
1039
|
+
})
|
1040
|
+
|
1041
|
+
}).catch((err) => {
|
1042
|
+
reject(err);
|
641
1043
|
})
|
1044
|
+
})
|
642
1045
|
|
643
1046
|
}
|
644
1047
|
|
645
1048
|
async function statusConverter(status) {
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
1049
|
+
return new Promise((resolve) => {
|
1050
|
+
|
1051
|
+
let td_status;
|
1052
|
+
switch (status) {
|
1053
|
+
case 0:
|
1054
|
+
td_status = -1;
|
1055
|
+
break;
|
1056
|
+
case 2:
|
1057
|
+
td_status = 200;
|
1058
|
+
break;
|
1059
|
+
case 3:
|
1060
|
+
td_status = 300;
|
1061
|
+
break;
|
1062
|
+
case 4:
|
1063
|
+
td_status = 400;
|
1064
|
+
break;
|
1065
|
+
default:
|
1066
|
+
td_status = -1
|
1067
|
+
}
|
1068
|
+
resolve(td_status);
|
1069
|
+
})
|
667
1070
|
}
|
668
1071
|
|
669
1072
|
async function updateStatus(id, status) {
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
})
|
1073
|
+
return new Promise((resolve) => {
|
1074
|
+
|
1075
|
+
KB.findByIdAndUpdate(id, { status: status }, { new: true }, (err, updatedKb) => {
|
1076
|
+
if (err) {
|
1077
|
+
resolve(false)
|
1078
|
+
} else if (!updatedKb) {
|
1079
|
+
winston.verbose("Unable to update status. Data source not found.")
|
1080
|
+
resolve(false)
|
1081
|
+
} else {
|
1082
|
+
winston.debug("updatedKb: ", updatedKb)
|
1083
|
+
resolve(true);
|
1084
|
+
}
|
683
1085
|
})
|
1086
|
+
})
|
684
1087
|
}
|
685
1088
|
|
686
1089
|
async function scheduleScrape(resources) {
|
687
1090
|
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
1091
|
+
// let data = {
|
1092
|
+
// resources: resources
|
1093
|
+
// }
|
1094
|
+
let scheduler = new Scheduler({ jobManager: jobManager });
|
1095
|
+
|
1096
|
+
resources.forEach(r => {
|
1097
|
+
winston.debug("Schedule job with following data: ", r);
|
1098
|
+
scheduler.trainSchedule(r, async (err, result) => {
|
1099
|
+
let error_code = 100;
|
1100
|
+
if (err) {
|
1101
|
+
winston.error("Scheduling error: ", err);
|
1102
|
+
error_code = 400;
|
1103
|
+
} else {
|
1104
|
+
winston.info("Scheduling result: ", result);
|
1105
|
+
}
|
1106
|
+
await updateStatus(r.id, error_code);
|
1107
|
+
});
|
1108
|
+
})
|
706
1109
|
|
707
1110
|
|
708
|
-
|
1111
|
+
return true;
|
709
1112
|
}
|
710
1113
|
|
711
1114
|
async function startScrape(data) {
|
712
1115
|
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
}
|
718
|
-
data.gptkey = gptkey;
|
1116
|
+
if (!data.gptkey) {
|
1117
|
+
let gptkey = process.env.GPTKEY;
|
1118
|
+
if (!gptkey) {
|
1119
|
+
return { error: "GPT apikey undefined" }
|
719
1120
|
}
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
1121
|
+
data.gptkey = gptkey;
|
1122
|
+
}
|
1123
|
+
|
1124
|
+
return new Promise((resolve, reject) => {
|
1125
|
+
openaiService.singleScrape(data).then(async (resp) => {
|
1126
|
+
winston.debug("singleScrape resp: ", resp.data);
|
1127
|
+
let status_updated = await updateStatus(data.id, 100);
|
1128
|
+
winston.verbose("status of kb " + data.id + " updated: " + status_updated);
|
1129
|
+
resolve(resp.data);
|
1130
|
+
}).catch((err) => {
|
1131
|
+
winston.error("singleScrape err: ", err);
|
1132
|
+
reject(err);
|
731
1133
|
})
|
1134
|
+
})
|
732
1135
|
}
|
1136
|
+
/**
|
1137
|
+
* ****************************************
|
1138
|
+
* Utils Methods Section - End
|
1139
|
+
* ****************************************
|
1140
|
+
*/
|
1141
|
+
|
1142
|
+
|
733
1143
|
|
734
1144
|
module.exports = router;
|