@tiledesk/tiledesk-server 2.19.3 → 2.19.5
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 +10 -0
- package/models/kb_setting.js +101 -1
- package/package.json +1 -1
- package/routes/answered.js +243 -264
- package/routes/kb.js +154 -102
- package/routes/unanswered.js +277 -296
- package/routes/webhook.js +1 -1
- package/services/aiManager.js +29 -20
- package/services/kbQuestionService.js +98 -0
package/routes/unanswered.js
CHANGED
|
@@ -3,338 +3,319 @@ const router = express.Router();
|
|
|
3
3
|
const { Namespace, UnansweredQuestion } = require('../models/kb_setting');
|
|
4
4
|
var winston = require('../config/winston');
|
|
5
5
|
var fastCsv = require('fast-csv');
|
|
6
|
+
const { KbQuestionService } = require('../services/kbQuestionService');
|
|
6
7
|
|
|
7
8
|
// Add a new unanswered question
|
|
8
9
|
router.post('/', async (req, res) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const unansweredQuestion = new UnansweredQuestion({
|
|
30
|
-
id_project,
|
|
31
|
-
namespace,
|
|
32
|
-
question,
|
|
33
|
-
request_id,
|
|
34
|
-
sender
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const savedQuestion = await unansweredQuestion.save();
|
|
38
|
-
res.status(200).json(savedQuestion);
|
|
39
|
-
|
|
40
|
-
} catch (error) {
|
|
41
|
-
winston.error('Error adding unanswered question:', error);
|
|
42
|
-
res.status(500).json({
|
|
43
|
-
success: false,
|
|
44
|
-
error: "Error adding unanswered question"
|
|
45
|
-
});
|
|
46
|
-
}
|
|
10
|
+
try {
|
|
11
|
+
const data = req.body;
|
|
12
|
+
const id_project = req.projectid;
|
|
13
|
+
|
|
14
|
+
const savedQuestion = await KbQuestionService.createUnansweredQuestion(
|
|
15
|
+
id_project,
|
|
16
|
+
data
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
res.status(200).json(savedQuestion);
|
|
20
|
+
|
|
21
|
+
} catch (error) {
|
|
22
|
+
winston.error('Error adding unanswered question:', error);
|
|
23
|
+
res.status(error.status || 500).json({
|
|
24
|
+
success: false,
|
|
25
|
+
error: error.error || "Error adding unanswered question"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
47
28
|
});
|
|
48
29
|
|
|
49
30
|
// Get all unanswered questions for a namespace
|
|
50
31
|
router.get('/:namespace', async (req, res) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
let sortObj;
|
|
87
|
-
if (projection && projection.score) {
|
|
88
|
-
sortObj = { score: { $meta: "textScore" } };
|
|
89
|
-
} else {
|
|
90
|
-
sortObj = { [sortField]: direction };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const questions = await UnansweredQuestion.find(filter, projection)
|
|
94
|
-
.sort(sortObj)
|
|
95
|
-
.skip(page * limit)
|
|
96
|
-
.limit(limit);
|
|
97
|
-
|
|
98
|
-
const count = await UnansweredQuestion.countDocuments(filter);
|
|
99
|
-
|
|
100
|
-
res.status(200).json({
|
|
101
|
-
count,
|
|
102
|
-
questions,
|
|
103
|
-
query: {
|
|
104
|
-
page,
|
|
105
|
-
limit,
|
|
106
|
-
sortField,
|
|
107
|
-
direction,
|
|
108
|
-
search: req.query.search || undefined
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
} catch (error) {
|
|
113
|
-
winston.error('Error getting unanswered questions:', error);
|
|
114
|
-
res.status(500).json({
|
|
115
|
-
success: false,
|
|
116
|
-
error: "Error getting unanswered questions"
|
|
117
|
-
});
|
|
32
|
+
try {
|
|
33
|
+
const { namespace } = req.params;
|
|
34
|
+
const id_project = req.projectid;
|
|
35
|
+
|
|
36
|
+
if (!namespace) {
|
|
37
|
+
return res.status(400).json({
|
|
38
|
+
success: false,
|
|
39
|
+
error: "Missing required parameter: namespace"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check if namespace belongs to project
|
|
44
|
+
const isValidNamespace = await validateNamespace(id_project, namespace);
|
|
45
|
+
if (!isValidNamespace) {
|
|
46
|
+
return res.status(403).json({
|
|
47
|
+
success: false,
|
|
48
|
+
error: "Not allowed. The namespace does not belong to the current project."
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const page = parseInt(req.query.page) || 0;
|
|
53
|
+
const limit = parseInt(req.query.limit) || 20;
|
|
54
|
+
const sortField = req.query.sortField || 'createdAt';
|
|
55
|
+
const direction = parseInt(req.query.direction) || -1;
|
|
56
|
+
|
|
57
|
+
const filter = { id_project, namespace };
|
|
58
|
+
|
|
59
|
+
let projection = undefined;
|
|
60
|
+
|
|
61
|
+
if (req.query.search) {
|
|
62
|
+
filter.$text = { $search: req.query.search };
|
|
63
|
+
// Add score to projection if it's a text search
|
|
64
|
+
projection = { score: { $meta: "textScore" } };
|
|
118
65
|
}
|
|
66
|
+
|
|
67
|
+
let sortObj;
|
|
68
|
+
if (projection && projection.score) {
|
|
69
|
+
sortObj = { score: { $meta: "textScore" } };
|
|
70
|
+
} else {
|
|
71
|
+
sortObj = { [sortField]: direction };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const questions = await UnansweredQuestion.find(filter, projection)
|
|
75
|
+
.sort(sortObj)
|
|
76
|
+
.skip(page * limit)
|
|
77
|
+
.limit(limit);
|
|
78
|
+
|
|
79
|
+
const count = await UnansweredQuestion.countDocuments(filter);
|
|
80
|
+
|
|
81
|
+
res.status(200).json({
|
|
82
|
+
count,
|
|
83
|
+
questions,
|
|
84
|
+
query: {
|
|
85
|
+
page,
|
|
86
|
+
limit,
|
|
87
|
+
sortField,
|
|
88
|
+
direction,
|
|
89
|
+
search: req.query.search || undefined
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
} catch (error) {
|
|
94
|
+
winston.error('Error getting unanswered questions:', error);
|
|
95
|
+
res.status(500).json({
|
|
96
|
+
success: false,
|
|
97
|
+
error: "Error getting unanswered questions"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
119
100
|
});
|
|
120
101
|
|
|
121
102
|
// Delete a specific unanswered question
|
|
122
103
|
router.delete('/:id', async (req, res) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
res.status(200).json({
|
|
136
|
-
success: true,
|
|
137
|
-
message: "Question deleted successfully"
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
} catch (error) {
|
|
141
|
-
winston.error('Error deleting unanswered question:', error);
|
|
142
|
-
res.status(500).json({
|
|
143
|
-
success: false,
|
|
144
|
-
error: "Error deleting unanswered question"
|
|
145
|
-
});
|
|
104
|
+
try {
|
|
105
|
+
const { id } = req.params;
|
|
106
|
+
const id_project = req.projectid;
|
|
107
|
+
|
|
108
|
+
const deleted = await UnansweredQuestion.findOneAndDelete({ _id: id, id_project });
|
|
109
|
+
if (!deleted) {
|
|
110
|
+
return res.status(404).json({
|
|
111
|
+
success: false,
|
|
112
|
+
error: "Question not found"
|
|
113
|
+
});
|
|
146
114
|
}
|
|
115
|
+
|
|
116
|
+
res.status(200).json({
|
|
117
|
+
success: true,
|
|
118
|
+
message: "Question deleted successfully"
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
} catch (error) {
|
|
122
|
+
winston.error('Error deleting unanswered question:', error);
|
|
123
|
+
res.status(500).json({
|
|
124
|
+
success: false,
|
|
125
|
+
error: "Error deleting unanswered question"
|
|
126
|
+
});
|
|
127
|
+
}
|
|
147
128
|
});
|
|
148
129
|
|
|
149
130
|
// Delete all unanswered questions for a namespace
|
|
150
131
|
router.delete('/namespace/:namespace', async (req, res) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const result = await UnansweredQuestion.deleteMany({ id_project, namespace });
|
|
165
|
-
res.status(200).json({
|
|
166
|
-
success: true,
|
|
167
|
-
count: result.deletedCount,
|
|
168
|
-
message: "All questions deleted successfully"
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
} catch (error) {
|
|
172
|
-
winston.error('Error deleting unanswered questions:', error);
|
|
173
|
-
res.status(500).json({
|
|
174
|
-
success: false,
|
|
175
|
-
error: "Error deleting unanswered questions"
|
|
176
|
-
});
|
|
132
|
+
try {
|
|
133
|
+
const { namespace } = req.params;
|
|
134
|
+
const id_project = req.projectid;
|
|
135
|
+
|
|
136
|
+
// Check if namespace belongs to project
|
|
137
|
+
const isValidNamespace = await validateNamespace(id_project, namespace);
|
|
138
|
+
if (!isValidNamespace) {
|
|
139
|
+
return res.status(403).json({
|
|
140
|
+
success: false,
|
|
141
|
+
error: "Not allowed. The namespace does not belong to the current project."
|
|
142
|
+
});
|
|
177
143
|
}
|
|
144
|
+
|
|
145
|
+
const result = await UnansweredQuestion.deleteMany({ id_project, namespace });
|
|
146
|
+
res.status(200).json({
|
|
147
|
+
success: true,
|
|
148
|
+
count: result.deletedCount,
|
|
149
|
+
message: "All questions deleted successfully"
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
} catch (error) {
|
|
153
|
+
winston.error('Error deleting unanswered questions:', error);
|
|
154
|
+
res.status(500).json({
|
|
155
|
+
success: false,
|
|
156
|
+
error: "Error deleting unanswered questions"
|
|
157
|
+
});
|
|
158
|
+
}
|
|
178
159
|
});
|
|
179
160
|
|
|
180
161
|
// Update an unanswered question
|
|
181
162
|
router.put('/:id', async (req, res) => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
res.status(200).json(updatedQuestion);
|
|
208
|
-
|
|
209
|
-
} catch (error) {
|
|
210
|
-
winston.error('Error updating unanswered question:', error);
|
|
211
|
-
res.status(500).json({
|
|
212
|
-
success: false,
|
|
213
|
-
error: "Error updating unanswered question"
|
|
214
|
-
});
|
|
163
|
+
try {
|
|
164
|
+
const { id } = req.params;
|
|
165
|
+
const { question } = req.body;
|
|
166
|
+
const id_project = req.projectid;
|
|
167
|
+
|
|
168
|
+
if (!question) {
|
|
169
|
+
return res.status(400).json({
|
|
170
|
+
success: false,
|
|
171
|
+
error: "Missing required parameter: question"
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const updatedQuestion = await UnansweredQuestion.findOneAndUpdate(
|
|
176
|
+
{ _id: id, id_project },
|
|
177
|
+
{ question },
|
|
178
|
+
{ new: true }
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
if (!updatedQuestion) {
|
|
182
|
+
return res.status(404).json({
|
|
183
|
+
success: false,
|
|
184
|
+
error: "Question not found"
|
|
185
|
+
});
|
|
215
186
|
}
|
|
187
|
+
|
|
188
|
+
res.status(200).json(updatedQuestion);
|
|
189
|
+
|
|
190
|
+
} catch (error) {
|
|
191
|
+
winston.error('Error updating unanswered question:', error);
|
|
192
|
+
res.status(500).json({
|
|
193
|
+
success: false,
|
|
194
|
+
error: "Error updating unanswered question"
|
|
195
|
+
});
|
|
196
|
+
}
|
|
216
197
|
});
|
|
217
198
|
|
|
218
199
|
// Count unanswered questions for a namespace
|
|
219
200
|
router.get('/count/:namespace', async (req, res) => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const count = await UnansweredQuestion.countDocuments({
|
|
241
|
-
id_project,
|
|
242
|
-
namespace
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
res.status(200).json({ count });
|
|
246
|
-
|
|
247
|
-
} catch (error) {
|
|
248
|
-
winston.error('Error counting unanswered questions:', error);
|
|
249
|
-
res.status(500).json({
|
|
250
|
-
success: false,
|
|
251
|
-
error: "Error counting unanswered questions"
|
|
252
|
-
});
|
|
201
|
+
try {
|
|
202
|
+
const { namespace } = req.params;
|
|
203
|
+
const id_project = req.projectid;
|
|
204
|
+
|
|
205
|
+
if (!namespace) {
|
|
206
|
+
return res.status(400).json({
|
|
207
|
+
success: false,
|
|
208
|
+
error: "Missing required parameter: namespace"
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check if namespace belongs to project
|
|
213
|
+
const isValidNamespace = await validateNamespace(id_project, namespace);
|
|
214
|
+
if (!isValidNamespace) {
|
|
215
|
+
return res.status(403).json({
|
|
216
|
+
success: false,
|
|
217
|
+
error: "Not allowed. The namespace does not belong to the current project."
|
|
218
|
+
});
|
|
253
219
|
}
|
|
220
|
+
|
|
221
|
+
const count = await UnansweredQuestion.countDocuments({
|
|
222
|
+
id_project,
|
|
223
|
+
namespace
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
res.status(200).json({ count });
|
|
227
|
+
|
|
228
|
+
} catch (error) {
|
|
229
|
+
winston.error('Error counting unanswered questions:', error);
|
|
230
|
+
res.status(500).json({
|
|
231
|
+
success: false,
|
|
232
|
+
error: "Error counting unanswered questions"
|
|
233
|
+
});
|
|
234
|
+
}
|
|
254
235
|
});
|
|
255
236
|
|
|
256
237
|
router.get('/:namespace/export', async (req, res) => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const questions = await UnansweredQuestion.find({ id_project, namespace })
|
|
278
|
-
.sort({ createdAt: -1 })
|
|
279
|
-
.lean();
|
|
280
|
-
|
|
281
|
-
const safeFilename = String(namespace).replace(/[^\w.-]+/g, '_') || 'export';
|
|
282
|
-
|
|
283
|
-
if (mode === 'json') {
|
|
284
|
-
const questionsJson = questions.map((q) => {
|
|
285
|
-
const { __v, updatedAt, ...doc } = q;
|
|
286
|
-
return doc;
|
|
287
|
-
});
|
|
288
|
-
const payload = {
|
|
289
|
-
namespace,
|
|
290
|
-
exportedAt: new Date().toISOString(),
|
|
291
|
-
count: questionsJson.length,
|
|
292
|
-
questions: questionsJson
|
|
293
|
-
};
|
|
294
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
295
|
-
res.setHeader('Content-Disposition', `attachment; filename="unanswered-${safeFilename}.json"`);
|
|
296
|
-
return res.send(JSON.stringify(payload, null, 2));
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
300
|
-
res.setHeader('Content-Disposition', `attachment; filename="unanswered-${safeFilename}.csv"`);
|
|
301
|
-
|
|
302
|
-
const csvStream = fastCsv.format({ headers: true });
|
|
303
|
-
csvStream.pipe(res);
|
|
304
|
-
|
|
305
|
-
for (const q of questions) {
|
|
306
|
-
csvStream.write({
|
|
307
|
-
id: String(q._id),
|
|
308
|
-
namespace: q.namespace,
|
|
309
|
-
question: q.question,
|
|
310
|
-
request_id: q.request_id || '',
|
|
311
|
-
createdAt: q.createdAt ? new Date(q.createdAt).toISOString() : ''
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
csvStream.end();
|
|
315
|
-
} catch (error) {
|
|
316
|
-
winston.error('Error exporting unanswered questions:', error);
|
|
317
|
-
if (!res.headersSent) {
|
|
318
|
-
res.status(500).json({
|
|
319
|
-
success: false,
|
|
320
|
-
error: "Error exporting unanswered questions"
|
|
321
|
-
});
|
|
322
|
-
}
|
|
238
|
+
try {
|
|
239
|
+
const { namespace } = req.params;
|
|
240
|
+
const id_project = req.projectid;
|
|
241
|
+
const mode = String(req.query.mode || 'csv').toLowerCase();
|
|
242
|
+
|
|
243
|
+
if (mode !== 'csv' && mode !== 'json') {
|
|
244
|
+
return res.status(400).json({
|
|
245
|
+
success: false,
|
|
246
|
+
error: 'Invalid format. Use mode=json or mode=csv'
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const isValidNamespace = await validateNamespace(id_project, namespace);
|
|
251
|
+
if (!isValidNamespace) {
|
|
252
|
+
return res.status(403).json({
|
|
253
|
+
success: false,
|
|
254
|
+
error: "Not allowed. The namespace does not belong to the current project."
|
|
255
|
+
});
|
|
323
256
|
}
|
|
257
|
+
|
|
258
|
+
const questions = await UnansweredQuestion.find({ id_project, namespace })
|
|
259
|
+
.sort({ createdAt: -1 })
|
|
260
|
+
.lean();
|
|
261
|
+
|
|
262
|
+
const safeFilename = String(namespace).replace(/[^\w.-]+/g, '_') || 'export';
|
|
263
|
+
|
|
264
|
+
if (mode === 'json') {
|
|
265
|
+
const questionsJson = questions.map((q) => {
|
|
266
|
+
const { __v, updatedAt, ...doc } = q;
|
|
267
|
+
return doc;
|
|
268
|
+
});
|
|
269
|
+
const payload = {
|
|
270
|
+
namespace,
|
|
271
|
+
exportedAt: new Date().toISOString(),
|
|
272
|
+
count: questionsJson.length,
|
|
273
|
+
questions: questionsJson
|
|
274
|
+
};
|
|
275
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
276
|
+
res.setHeader('Content-Disposition', `attachment; filename="unanswered-${safeFilename}.json"`);
|
|
277
|
+
return res.send(JSON.stringify(payload, null, 2));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
281
|
+
res.setHeader('Content-Disposition', `attachment; filename="unanswered-${safeFilename}.csv"`);
|
|
282
|
+
|
|
283
|
+
const csvStream = fastCsv.format({ headers: true });
|
|
284
|
+
csvStream.pipe(res);
|
|
285
|
+
|
|
286
|
+
for (const q of questions) {
|
|
287
|
+
csvStream.write({
|
|
288
|
+
id: String(q._id),
|
|
289
|
+
namespace: q.namespace,
|
|
290
|
+
question: q.question,
|
|
291
|
+
request_id: q.request_id || '',
|
|
292
|
+
createdAt: q.createdAt ? new Date(q.createdAt).toISOString() : ''
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
csvStream.end();
|
|
296
|
+
} catch (error) {
|
|
297
|
+
winston.error('Error exporting unanswered questions:', error);
|
|
298
|
+
if (!res.headersSent) {
|
|
299
|
+
res.status(500).json({
|
|
300
|
+
success: false,
|
|
301
|
+
error: "Error exporting unanswered questions"
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
324
305
|
});
|
|
325
306
|
|
|
326
307
|
// Helper function to validate namespace
|
|
327
308
|
async function validateNamespace(id_project, namespace_id) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
309
|
+
try {
|
|
310
|
+
const namespace = await Namespace.findOne({
|
|
311
|
+
id_project: id_project,
|
|
312
|
+
id: namespace_id
|
|
313
|
+
});
|
|
314
|
+
return !!namespace; // return true if namespace exists, false otherwise
|
|
315
|
+
} catch (err) {
|
|
316
|
+
winston.error("validate namespace error: ", err);
|
|
317
|
+
throw err;
|
|
318
|
+
}
|
|
338
319
|
}
|
|
339
320
|
|
|
340
321
|
module.exports = router;
|
package/routes/webhook.js
CHANGED
|
@@ -199,7 +199,7 @@ router.post('/kb/reindex', async (req, res) => {
|
|
|
199
199
|
json.engine = namespace.engine || (namespace.hybrid ? default_engine_hybrid : default_engine);
|
|
200
200
|
json.hybrid = namespace.hybrid;
|
|
201
201
|
|
|
202
|
-
let embedding = namespace.embedding || default_embedding;
|
|
202
|
+
let embedding = namespace.embedding || Object.assign({}, default_embedding);
|
|
203
203
|
embedding.api_key = process.env.EMBEDDING_API_KEY || process.env.GPTKEY;
|
|
204
204
|
json.embedding = embedding;
|
|
205
205
|
|