nodebb-plugin-integration 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -320
- package/README.ru.md +199 -347
- package/library.js +93 -115
- package/package.json +60 -57
- package/plugin.json +50 -33
- package/public/js/admin.js +58 -45
- package/public/templates/admin/plugins/integration.tpl +213 -0
- package/src/ai/embeddings.js +46 -253
- package/src/ai/indexer.js +156 -343
- package/src/ai/openai.js +74 -330
- package/src/ai/qdrant.js +114 -517
- package/src/ai/search.js +138 -251
- package/src/config/index.js +176 -133
- package/src/config/validation.js +250 -0
- package/src/events/index.js +174 -324
- package/src/mcp/manager.js +396 -0
- package/src/mcp/server.js +347 -177
- package/src/mcp/tools.js +158 -617
- package/src/nodebb/categories.js +39 -0
- package/src/nodebb/index.js +72 -305
- package/src/nodebb/meta.js +24 -273
- package/src/nodebb/posts.js +93 -348
- package/src/nodebb/privileges.js +68 -0
- package/src/nodebb/topics.js +91 -355
- package/src/nodebb/users.js +62 -345
- package/src/routes/api.js +255 -97
- package/src/routes/index.js +64 -49
- package/templates/admin/plugins/integration.tpl +0 -130
package/src/nodebb/posts.js
CHANGED
|
@@ -1,348 +1,93 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
catch(error) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
nativePosts = null;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return nativePosts;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
async function getPost(pid) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const posts =
|
|
135
|
-
|
|
136
|
-
loadNative();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
/*
|
|
146
|
-
* Native NodeBB mode
|
|
147
|
-
*/
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if(
|
|
151
|
-
|
|
152
|
-
posts &&
|
|
153
|
-
|
|
154
|
-
typeof posts.getPostFields === "function"
|
|
155
|
-
|
|
156
|
-
) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return await posts.getPostFields(
|
|
161
|
-
|
|
162
|
-
pid,
|
|
163
|
-
|
|
164
|
-
[
|
|
165
|
-
|
|
166
|
-
"pid",
|
|
167
|
-
|
|
168
|
-
"tid",
|
|
169
|
-
|
|
170
|
-
"uid",
|
|
171
|
-
|
|
172
|
-
"content",
|
|
173
|
-
|
|
174
|
-
"timestamp"
|
|
175
|
-
|
|
176
|
-
]
|
|
177
|
-
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
/*
|
|
191
|
-
* Standalone mode
|
|
192
|
-
*/
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
pid,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
tid:
|
|
202
|
-
|
|
203
|
-
null,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
uid:
|
|
208
|
-
|
|
209
|
-
null,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
content:
|
|
214
|
-
|
|
215
|
-
"NodeBB unavailable",
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
timestamp:
|
|
220
|
-
|
|
221
|
-
Date.now()
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
async function getPosts(pids = []) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const result = [];
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
for(const pid of pids) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
result.push(
|
|
263
|
-
|
|
264
|
-
await getPost(pid)
|
|
265
|
-
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
return result;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
function info() {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
nodebb:
|
|
305
|
-
|
|
306
|
-
NodeBB.info(),
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
adapter:
|
|
311
|
-
|
|
312
|
-
"posts"
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
module.exports = {
|
|
336
|
-
|
|
337
|
-
getPost,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
getPosts,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
info
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const NodeBB = require("./index");
|
|
4
|
+
|
|
5
|
+
const META_FIELDS = [
|
|
6
|
+
"pid",
|
|
7
|
+
"tid",
|
|
8
|
+
"uid",
|
|
9
|
+
"timestamp",
|
|
10
|
+
"deleted",
|
|
11
|
+
"cid"
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const CONTENT_FIELDS = [
|
|
15
|
+
"pid",
|
|
16
|
+
"content"
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function validatePid(pid) {
|
|
20
|
+
pid = Number(pid);
|
|
21
|
+
|
|
22
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) {
|
|
23
|
+
throw new TypeError("Invalid post ID");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return pid;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function getPostMeta(pid) {
|
|
30
|
+
pid = validatePid(pid);
|
|
31
|
+
|
|
32
|
+
const posts = NodeBB.get("posts");
|
|
33
|
+
|
|
34
|
+
if (typeof posts.getPostFields !== "function") {
|
|
35
|
+
throw new Error("NodeBB posts.getPostFields is unavailable");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return posts.getPostFields(pid, META_FIELDS);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function getPostContent(pid) {
|
|
42
|
+
pid = validatePid(pid);
|
|
43
|
+
|
|
44
|
+
const posts = NodeBB.get("posts");
|
|
45
|
+
|
|
46
|
+
if (typeof posts.getPostFields !== "function") {
|
|
47
|
+
throw new Error("NodeBB posts.getPostFields is unavailable");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return posts.getPostFields(pid, CONTENT_FIELDS);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function getPosts(pids = [], uid) {
|
|
54
|
+
if (!Array.isArray(pids)) {
|
|
55
|
+
throw new TypeError("Post IDs must be an array");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
uid = Number(uid);
|
|
59
|
+
|
|
60
|
+
if (!Number.isSafeInteger(uid) || uid < 0) {
|
|
61
|
+
throw new TypeError("Invalid user ID");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const unique = [
|
|
65
|
+
...new Set(
|
|
66
|
+
pids
|
|
67
|
+
.map(Number)
|
|
68
|
+
.filter(pid => Number.isSafeInteger(pid) && pid > 0)
|
|
69
|
+
)
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
if (!unique.length) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const posts = NodeBB.get("posts");
|
|
77
|
+
|
|
78
|
+
if (typeof posts.getPostsByPids !== "function") {
|
|
79
|
+
throw new Error("NodeBB posts.getPostsByPids is unavailable");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const result = await posts.getPostsByPids(unique, uid);
|
|
83
|
+
|
|
84
|
+
return result.posts || result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
getPostMeta,
|
|
89
|
+
getPostContent,
|
|
90
|
+
getPosts,
|
|
91
|
+
META_FIELDS,
|
|
92
|
+
CONTENT_FIELDS
|
|
93
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const NodeBB = require("./index");
|
|
4
|
+
|
|
5
|
+
function validId(value) {
|
|
6
|
+
const id = Number(value);
|
|
7
|
+
return Number.isSafeInteger(id) && id > 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function validUid(value) {
|
|
11
|
+
const uid = Number(value);
|
|
12
|
+
return Number.isSafeInteger(uid) && uid >= 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function canReadPost(pid, uid) {
|
|
16
|
+
pid = Number(pid);
|
|
17
|
+
uid = Number(uid);
|
|
18
|
+
|
|
19
|
+
if (!validId(pid) || !validUid(uid)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const privileges = NodeBB.get("privileges");
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
!privileges.posts ||
|
|
28
|
+
typeof privileges.posts.canRead !== "function"
|
|
29
|
+
) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return Boolean(await privileges.posts.canRead(pid, uid));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error("[Integration] post access check failed:", error.message);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function canReadTopic(tid, uid) {
|
|
41
|
+
tid = Number(tid);
|
|
42
|
+
uid = Number(uid);
|
|
43
|
+
|
|
44
|
+
if (!validId(tid) || !validUid(uid)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const privileges = NodeBB.get("privileges");
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
!privileges.topics ||
|
|
53
|
+
typeof privileges.topics.canRead !== "function"
|
|
54
|
+
) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return Boolean(await privileges.topics.canRead(tid, uid));
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("[Integration] topic access check failed:", error.message);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
canReadPost,
|
|
67
|
+
canReadTopic
|
|
68
|
+
};
|