nodebb-plugin-integration 0.3.0 → 0.4.1
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/library.js +40 -96
- package/package.json +1 -1
- package/plugin.json +10 -0
- package/src/config/index.js +133 -322
- package/src/routes/api.js +65 -208
- package/src/routes/index.js +48 -109
package/library.js
CHANGED
|
@@ -6,166 +6,110 @@
|
|
|
6
6
|
* Main plugin entry point
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
require("dotenv").config();
|
|
10
|
-
|
|
11
9
|
const Config = require("./src/config");
|
|
12
10
|
const Routes = require("./src/routes");
|
|
13
11
|
const Events = require("./src/events");
|
|
14
12
|
const Indexer = require("./src/ai/indexer");
|
|
15
13
|
const MCP = require("./src/mcp/server");
|
|
16
14
|
|
|
17
|
-
|
|
18
15
|
const Plugin = {};
|
|
19
16
|
|
|
20
|
-
|
|
21
17
|
/**
|
|
22
|
-
* NodeBB hook:
|
|
23
|
-
*
|
|
24
18
|
* static:app.load
|
|
25
19
|
*/
|
|
26
|
-
Plugin.init = async function (
|
|
20
|
+
Plugin.init = async function (params = {}) {
|
|
27
21
|
|
|
28
|
-
console.log(
|
|
29
|
-
"[NodeBB Integration] initializing"
|
|
30
|
-
);
|
|
22
|
+
console.log("[NodeBB Integration] initializing");
|
|
31
23
|
|
|
24
|
+
const { router, middleware } = params;
|
|
32
25
|
|
|
33
26
|
if (router) {
|
|
34
|
-
|
|
35
|
-
Routes.init(
|
|
36
|
-
router
|
|
37
|
-
);
|
|
38
|
-
|
|
27
|
+
Routes.init(router, middleware);
|
|
39
28
|
} else {
|
|
40
|
-
|
|
41
|
-
console.log(
|
|
42
|
-
"[NodeBB Integration] router unavailable (standalone mode)"
|
|
43
|
-
);
|
|
44
|
-
|
|
29
|
+
console.log("[NodeBB Integration] router unavailable (standalone mode)");
|
|
45
30
|
}
|
|
46
31
|
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
Events &&
|
|
50
|
-
typeof Events.init === "function"
|
|
51
|
-
) {
|
|
52
|
-
|
|
32
|
+
if (Events && typeof Events.init === "function") {
|
|
53
33
|
await Events.init();
|
|
54
|
-
|
|
55
34
|
}
|
|
56
35
|
|
|
36
|
+
const settings = await Config.get();
|
|
57
37
|
|
|
58
38
|
if (
|
|
59
|
-
|
|
60
|
-
process.env.MCP_AUTOSTART === "true" &&
|
|
39
|
+
settings.mcpEnabled &&
|
|
61
40
|
MCP &&
|
|
62
41
|
typeof MCP.start === "function"
|
|
63
42
|
) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"[NodeBB Integration] MCP
|
|
69
|
-
|
|
70
|
-
|
|
43
|
+
try {
|
|
44
|
+
await MCP.start();
|
|
45
|
+
console.log("[NodeBB Integration] MCP server started");
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error("[NodeBB Integration] MCP start failed:", err.message);
|
|
48
|
+
}
|
|
71
49
|
}
|
|
72
50
|
|
|
73
|
-
|
|
74
|
-
console.log(
|
|
75
|
-
"[NodeBB Integration] ready"
|
|
76
|
-
);
|
|
77
|
-
|
|
51
|
+
console.log("[NodeBB Integration] ready");
|
|
78
52
|
};
|
|
79
53
|
|
|
80
|
-
|
|
81
54
|
/**
|
|
82
|
-
* NodeBB hook:
|
|
83
|
-
*
|
|
84
55
|
* action:post.save
|
|
85
56
|
*/
|
|
86
57
|
Plugin.onPostSave = async function (post) {
|
|
87
58
|
|
|
88
59
|
try {
|
|
89
60
|
|
|
90
|
-
if (
|
|
91
|
-
Events
|
|
92
|
-
typeof Events.postSave === "function"
|
|
93
|
-
) {
|
|
94
|
-
|
|
95
|
-
await Events.postSave(
|
|
96
|
-
post
|
|
97
|
-
);
|
|
98
|
-
|
|
61
|
+
if (Events && typeof Events.postSave === "function") {
|
|
62
|
+
await Events.postSave(post);
|
|
99
63
|
}
|
|
100
64
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Indexer &&
|
|
104
|
-
typeof Indexer.indexPost === "function"
|
|
105
|
-
) {
|
|
106
|
-
|
|
107
|
-
await Indexer.indexPost(
|
|
108
|
-
post
|
|
109
|
-
);
|
|
110
|
-
|
|
65
|
+
if (Indexer && typeof Indexer.indexPost === "function") {
|
|
66
|
+
await Indexer.indexPost(post);
|
|
111
67
|
}
|
|
112
68
|
|
|
69
|
+
} catch (err) {
|
|
113
70
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.error(
|
|
117
|
-
"[Plugin] post save error",
|
|
118
|
-
error.message
|
|
119
|
-
);
|
|
71
|
+
console.error("[Plugin] post save error:", err.message);
|
|
120
72
|
|
|
121
73
|
}
|
|
122
74
|
|
|
123
75
|
};
|
|
124
76
|
|
|
125
|
-
|
|
126
77
|
/**
|
|
127
|
-
* NodeBB hook:
|
|
128
|
-
*
|
|
129
78
|
* action:topic.save
|
|
130
79
|
*/
|
|
131
80
|
Plugin.onTopicSave = async function (topic) {
|
|
132
81
|
|
|
133
82
|
try {
|
|
134
83
|
|
|
135
|
-
if (
|
|
136
|
-
Events
|
|
137
|
-
typeof Events.topicCreate === "function"
|
|
138
|
-
) {
|
|
139
|
-
|
|
140
|
-
await Events.topicCreate(
|
|
141
|
-
topic
|
|
142
|
-
);
|
|
143
|
-
|
|
84
|
+
if (Events && typeof Events.topicCreate === "function") {
|
|
85
|
+
await Events.topicCreate(topic);
|
|
144
86
|
}
|
|
145
87
|
|
|
88
|
+
if (Indexer && typeof Indexer.indexTopic === "function") {
|
|
89
|
+
await Indexer.indexTopic(topic);
|
|
90
|
+
}
|
|
146
91
|
|
|
147
|
-
|
|
148
|
-
Indexer &&
|
|
149
|
-
typeof Indexer.indexTopic === "function"
|
|
150
|
-
) {
|
|
92
|
+
} catch (err) {
|
|
151
93
|
|
|
152
|
-
|
|
153
|
-
topic
|
|
154
|
-
);
|
|
94
|
+
console.error("[Plugin] topic save error:", err.message);
|
|
155
95
|
|
|
156
|
-
|
|
96
|
+
}
|
|
157
97
|
|
|
98
|
+
};
|
|
158
99
|
|
|
159
|
-
|
|
100
|
+
/**
|
|
101
|
+
* ACP menu item
|
|
102
|
+
*/
|
|
103
|
+
Plugin.addAdminNavigation = async function (header) {
|
|
160
104
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
105
|
+
header.plugins.push({
|
|
106
|
+
route: "/plugins/integration",
|
|
107
|
+
icon: "fa-robot",
|
|
108
|
+
name: "NodeBB Integration"
|
|
109
|
+
});
|
|
165
110
|
|
|
166
|
-
|
|
111
|
+
return header;
|
|
167
112
|
|
|
168
113
|
};
|
|
169
114
|
|
|
170
|
-
|
|
171
115
|
module.exports = Plugin;
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
"name": "NodeBB Integration Layer",
|
|
4
4
|
"description": "AI integration layer for NodeBB with MCP, semantic search, RAG and Qdrant knowledge pipeline",
|
|
5
5
|
"library": "./library.js",
|
|
6
|
+
|
|
6
7
|
"staticDirs": {
|
|
7
8
|
"public": "public"
|
|
8
9
|
},
|
|
10
|
+
|
|
11
|
+
"modules": {
|
|
12
|
+
"admin/plugins/integration.js": "public/js/admin.js"
|
|
13
|
+
},
|
|
14
|
+
|
|
9
15
|
"hooks": [
|
|
10
16
|
{
|
|
11
17
|
"hook": "static:app.load",
|
|
@@ -18,6 +24,10 @@
|
|
|
18
24
|
{
|
|
19
25
|
"hook": "action:topic.save",
|
|
20
26
|
"method": "onTopicSave"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"hook": "filter:admin.header.build",
|
|
30
|
+
"method": "addAdminNavigation"
|
|
21
31
|
}
|
|
22
32
|
]
|
|
23
33
|
}
|
package/src/config/index.js
CHANGED
|
@@ -1,322 +1,133 @@
|
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
openai: {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
apiKey:
|
|
142
|
-
|
|
143
|
-
process.env.OPENAI_API_KEY ||
|
|
144
|
-
|
|
145
|
-
"",
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
model:
|
|
150
|
-
|
|
151
|
-
process.env.OPENAI_MODEL ||
|
|
152
|
-
|
|
153
|
-
"gpt-4.1-mini",
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
embeddingModel:
|
|
158
|
-
|
|
159
|
-
process.env.OPENAI_EMBEDDING_MODEL ||
|
|
160
|
-
|
|
161
|
-
"text-embedding-3-small"
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
embeddings: {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
provider:
|
|
178
|
-
|
|
179
|
-
process.env.AI_PROVIDER ||
|
|
180
|
-
|
|
181
|
-
"fake",
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
size:
|
|
186
|
-
|
|
187
|
-
Number(
|
|
188
|
-
|
|
189
|
-
process.env.EMBEDDING_SIZE ||
|
|
190
|
-
|
|
191
|
-
1536
|
|
192
|
-
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
mcp: {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
enabled:
|
|
210
|
-
|
|
211
|
-
process.env.MCP_ENABLED !== "false"
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
/*
|
|
244
|
-
|
|
245
|
-
Safe debug output
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Does not expose API keys
|
|
249
|
-
*/
|
|
250
|
-
|
|
251
|
-
Config.info = function() {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
environment:
|
|
260
|
-
|
|
261
|
-
Config.environment,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
nodebb:
|
|
266
|
-
|
|
267
|
-
{
|
|
268
|
-
|
|
269
|
-
url:
|
|
270
|
-
|
|
271
|
-
Config.nodebb.url
|
|
272
|
-
|
|
273
|
-
},
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
qdrant:
|
|
278
|
-
|
|
279
|
-
{
|
|
280
|
-
|
|
281
|
-
url:
|
|
282
|
-
|
|
283
|
-
Config.qdrant.url,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
collection:
|
|
287
|
-
|
|
288
|
-
Config.qdrant.collection
|
|
289
|
-
|
|
290
|
-
},
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
embeddings:
|
|
295
|
-
|
|
296
|
-
Config.embeddings,
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
mcp:
|
|
301
|
-
|
|
302
|
-
Config.mcp
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
module.exports = Config;
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NodeBB Integration Layer
|
|
5
|
+
*
|
|
6
|
+
* Configuration provider
|
|
7
|
+
*
|
|
8
|
+
* Settings are stored in the NodeBB database
|
|
9
|
+
* using meta.settings.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
let meta = null;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
|
|
16
|
+
meta = require.main.require("./src/meta");
|
|
17
|
+
|
|
18
|
+
} catch (err) {
|
|
19
|
+
|
|
20
|
+
// Standalone mode (tests, npm, CLI)
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULTS = {
|
|
25
|
+
|
|
26
|
+
aiProvider: "fake",
|
|
27
|
+
|
|
28
|
+
openaiApiKey: "",
|
|
29
|
+
|
|
30
|
+
openaiModel: "text-embedding-3-small",
|
|
31
|
+
|
|
32
|
+
qdrantUrl: "http://localhost:6333",
|
|
33
|
+
|
|
34
|
+
collection: "nodebb_knowledge",
|
|
35
|
+
|
|
36
|
+
mcpEnabled: false,
|
|
37
|
+
|
|
38
|
+
mcpPort: 3001
|
|
39
|
+
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
async function get() {
|
|
43
|
+
|
|
44
|
+
if (!meta) {
|
|
45
|
+
return { ...DEFAULTS };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
|
|
50
|
+
return await meta.settings.get(
|
|
51
|
+
"nodebb-plugin-integration",
|
|
52
|
+
DEFAULTS
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
} catch (err) {
|
|
56
|
+
|
|
57
|
+
console.error(
|
|
58
|
+
"[Config] unable to load settings:",
|
|
59
|
+
err.message
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return { ...DEFAULTS };
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function set(values) {
|
|
69
|
+
|
|
70
|
+
if (!meta) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
|
|
76
|
+
await meta.settings.set(
|
|
77
|
+
"nodebb-plugin-integration",
|
|
78
|
+
values
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
|
|
83
|
+
} catch (err) {
|
|
84
|
+
|
|
85
|
+
console.error(
|
|
86
|
+
"[Config] unable to save settings:",
|
|
87
|
+
err.message
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return false;
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function info() {
|
|
97
|
+
|
|
98
|
+
const cfg = await get();
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
|
|
102
|
+
aiProvider: cfg.aiProvider,
|
|
103
|
+
|
|
104
|
+
openaiModel: cfg.openaiModel,
|
|
105
|
+
|
|
106
|
+
qdrantUrl: cfg.qdrantUrl,
|
|
107
|
+
|
|
108
|
+
collection: cfg.collection,
|
|
109
|
+
|
|
110
|
+
mcpEnabled: cfg.mcpEnabled,
|
|
111
|
+
|
|
112
|
+
mcpPort: cfg.mcpPort,
|
|
113
|
+
|
|
114
|
+
openaiApiKey:
|
|
115
|
+
cfg.openaiApiKey
|
|
116
|
+
? "***configured***"
|
|
117
|
+
: ""
|
|
118
|
+
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
|
|
125
|
+
get,
|
|
126
|
+
|
|
127
|
+
set,
|
|
128
|
+
|
|
129
|
+
info,
|
|
130
|
+
|
|
131
|
+
defaults: DEFAULTS
|
|
132
|
+
|
|
133
|
+
};
|
package/src/routes/api.js
CHANGED
|
@@ -1,240 +1,97 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* NodeBB Integration Layer
|
|
5
|
+
*
|
|
6
|
+
* API controllers
|
|
7
|
+
*/
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
const Config = require("../config");
|
|
10
|
+
const Search = require("../ai/search");
|
|
11
|
+
const Meta = require("../nodebb/meta");
|
|
6
12
|
|
|
13
|
+
/**
|
|
14
|
+
* GET /api/v3/integration/info
|
|
15
|
+
*/
|
|
16
|
+
exports.apiInfo = async function (req, res) {
|
|
7
17
|
|
|
8
|
-
|
|
18
|
+
try {
|
|
9
19
|
|
|
20
|
+
const forum = await Meta.getInfo();
|
|
21
|
+
const settings = await Config.info();
|
|
10
22
|
|
|
23
|
+
res.json({
|
|
24
|
+
success: true,
|
|
25
|
+
forum,
|
|
26
|
+
settings
|
|
27
|
+
});
|
|
11
28
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const Meta =
|
|
18
|
-
require("../nodebb/meta");
|
|
19
|
-
|
|
20
|
-
const Search =
|
|
21
|
-
require("../ai/search");
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
function init(router) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if(!router) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return false;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
router.get(
|
|
56
|
-
|
|
57
|
-
"/api/v3/integration/info",
|
|
58
|
-
|
|
59
|
-
async function(
|
|
60
|
-
|
|
61
|
-
req,
|
|
62
|
-
|
|
63
|
-
res
|
|
64
|
-
|
|
65
|
-
) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const info =
|
|
74
|
-
|
|
75
|
-
await Meta.getInfo();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
res.json(info);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
catch(error) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
res.status(500)
|
|
95
|
-
|
|
96
|
-
.json({
|
|
97
|
-
|
|
98
|
-
error:
|
|
99
|
-
|
|
100
|
-
error.message
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
29
|
+
} catch (err) {
|
|
108
30
|
|
|
31
|
+
res.status(500).json({
|
|
32
|
+
success: false,
|
|
33
|
+
error: err.message
|
|
34
|
+
});
|
|
109
35
|
|
|
110
36
|
}
|
|
111
37
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
38
|
+
};
|
|
118
39
|
|
|
40
|
+
/**
|
|
41
|
+
* POST /api/v3/integration/search
|
|
42
|
+
*/
|
|
43
|
+
exports.apiSearch = async function (req, res) {
|
|
119
44
|
|
|
45
|
+
try {
|
|
120
46
|
|
|
47
|
+
const query = req.body?.query || "";
|
|
48
|
+
const limit = Number(req.body?.limit || 5);
|
|
121
49
|
|
|
122
|
-
|
|
50
|
+
const result = await Search.searchText(
|
|
51
|
+
query,
|
|
52
|
+
limit
|
|
53
|
+
);
|
|
123
54
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
req,
|
|
129
|
-
|
|
130
|
-
res
|
|
131
|
-
|
|
132
|
-
) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const query =
|
|
141
|
-
|
|
142
|
-
req.body.query;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const limit =
|
|
151
|
-
|
|
152
|
-
req.body.limit || 5;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const result =
|
|
161
|
-
|
|
162
|
-
await Search.searchText(
|
|
163
|
-
|
|
164
|
-
query,
|
|
165
|
-
|
|
166
|
-
limit
|
|
167
|
-
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
res.json(result);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
catch(error) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
res.status(500)
|
|
188
|
-
|
|
189
|
-
.json({
|
|
190
|
-
|
|
191
|
-
error:
|
|
192
|
-
|
|
193
|
-
error.message
|
|
194
|
-
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
55
|
+
res.json({
|
|
56
|
+
success: true,
|
|
57
|
+
result
|
|
58
|
+
});
|
|
200
59
|
|
|
60
|
+
} catch (err) {
|
|
201
61
|
|
|
62
|
+
res.status(500).json({
|
|
63
|
+
success: false,
|
|
64
|
+
error: err.message
|
|
65
|
+
});
|
|
202
66
|
|
|
203
67
|
}
|
|
204
68
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
69
|
+
};
|
|
210
70
|
|
|
71
|
+
/**
|
|
72
|
+
* ACP page
|
|
73
|
+
*/
|
|
74
|
+
exports.renderAdmin = async function (req, res) {
|
|
211
75
|
|
|
76
|
+
try {
|
|
212
77
|
|
|
78
|
+
const settings = await Config.get();
|
|
213
79
|
|
|
214
|
-
|
|
80
|
+
res.render(
|
|
81
|
+
"admin/plugins/integration",
|
|
82
|
+
{
|
|
83
|
+
title: "NodeBB Integration",
|
|
215
84
|
|
|
85
|
+
settings
|
|
86
|
+
}
|
|
87
|
+
);
|
|
216
88
|
|
|
89
|
+
} catch (err) {
|
|
217
90
|
|
|
91
|
+
res.status(500).render("500", {
|
|
92
|
+
error: err.message
|
|
93
|
+
});
|
|
218
94
|
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
module.exports = {
|
|
234
|
-
|
|
235
|
-
init
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
95
|
+
}
|
|
239
96
|
|
|
240
97
|
};
|
package/src/routes/index.js
CHANGED
|
@@ -1,110 +1,49 @@
|
|
|
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
|
-
API.init(
|
|
60
|
-
|
|
61
|
-
router
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
console.log(
|
|
73
|
-
|
|
74
|
-
"[Routes] initialized"
|
|
75
|
-
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return true;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
module.exports = {
|
|
104
|
-
|
|
105
|
-
init
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NodeBB Integration Layer
|
|
5
|
+
*
|
|
6
|
+
* Routes
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const API = require("./api");
|
|
10
|
+
|
|
11
|
+
exports.init = function (router, middleware) {
|
|
12
|
+
|
|
13
|
+
if (!router) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* REST API
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
router.get(
|
|
22
|
+
"/api/v3/integration/info",
|
|
23
|
+
API.apiInfo
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
router.post(
|
|
27
|
+
"/api/v3/integration/search",
|
|
28
|
+
API.apiSearch
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* ACP page
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
if (
|
|
36
|
+
middleware &&
|
|
37
|
+
middleware.admin &&
|
|
38
|
+
typeof middleware.admin.buildHeader === "function"
|
|
39
|
+
) {
|
|
40
|
+
|
|
41
|
+
router.get(
|
|
42
|
+
"/admin/plugins/integration",
|
|
43
|
+
middleware.admin.buildHeader,
|
|
44
|
+
API.renderAdmin
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
110
49
|
};
|