blockmine 1.18.4 → 1.19.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/.kiro/steering/product.md +27 -0
- package/.kiro/steering/structure.md +89 -0
- package/.kiro/steering/tech.md +94 -0
- package/CHANGELOG.md +147 -112
- package/backend/cli.js +59 -57
- package/backend/prisma/migrations/20250701190321_add/migration.sql +2 -2
- package/backend/prisma/migrations/20250709150611_add_run_on_startup_to_tasks/migration.sql +21 -21
- package/backend/prisma/migrations/20250709151124_make_cron_pattern_optional/migration.sql +21 -21
- package/backend/prisma/migrations/20250718181335_add_plugin_data_store/migration.sql +14 -14
- package/backend/prisma/migrations/20250719115906_add_plugin_owner_to_graphs/migration.sql +45 -45
- package/backend/prisma/migrations/20250723160648_add_bot_sort_order/migration.sql +2 -2
- package/backend/prisma/migrations/20250816083216_add_panel_user_bot_access/migration.sql +30 -0
- package/backend/prisma/migrations/migration_lock.toml +2 -2
- package/backend/prisma/schema.prisma +244 -229
- package/backend/src/api/middleware/botAccess.js +35 -0
- package/backend/src/api/routes/auth.js +633 -595
- package/backend/src/api/routes/bots.js +294 -179
- package/backend/src/api/routes/eventGraphs.js +459 -459
- package/backend/src/api/routes/pluginIde.js +6 -2
- package/backend/src/api/routes/servers.js +27 -0
- package/backend/src/core/BotManager.js +0 -1
- package/backend/src/core/BotProcess.js +1 -2
- package/backend/src/core/GraphExecutionEngine.js +917 -917
- package/backend/src/core/PluginLoader.js +208 -86
- package/backend/src/core/PluginManager.js +465 -427
- package/backend/src/core/commands/dev.js +6 -1
- package/backend/src/real-time/presence.js +74 -0
- package/backend/src/real-time/socketHandler.js +2 -0
- package/backend/src/server.js +193 -186
- package/frontend/dist/assets/index-BFd7YoAj.css +1 -0
- package/frontend/dist/assets/index-DxdxTe6I.js +8352 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-CA3XrPPP.css +0 -1
- package/frontend/dist/assets/index-CM9ljR30.js +0 -8352
|
@@ -13,6 +13,7 @@ const { randomUUID } = require('crypto');
|
|
|
13
13
|
const eventGraphsRouter = require('./eventGraphs');
|
|
14
14
|
const pluginIdeRouter = require('./pluginIde');
|
|
15
15
|
const { deepMergeSettings } = require('../../core/utils/settingsMerger');
|
|
16
|
+
const { checkBotAccess } = require('../middleware/botAccess');
|
|
16
17
|
|
|
17
18
|
const multer = require('multer');
|
|
18
19
|
const archiver = require('archiver');
|
|
@@ -23,6 +24,8 @@ const upload = multer({ storage: multer.memoryStorage() });
|
|
|
23
24
|
|
|
24
25
|
const router = express.Router();
|
|
25
26
|
|
|
27
|
+
router.use('/:botId(\\d+)/*', authenticate, (req, res, next) => checkBotAccess(req, res, next));
|
|
28
|
+
|
|
26
29
|
const conditionalRestartAuth = (req, res, next) => {
|
|
27
30
|
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
|
28
31
|
console.log('[Debug] Роут перезапуска бота доступен без проверки прав');
|
|
@@ -60,18 +63,16 @@ const conditionalStartStopAuth = (req, res, next) => {
|
|
|
60
63
|
};
|
|
61
64
|
|
|
62
65
|
const conditionalListAuth = (req, res, next) => {
|
|
63
|
-
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
|
64
|
-
console.log('[Debug] Роут списка ботов/состояния доступен без проверки прав');
|
|
65
|
-
return next();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
66
|
return authenticate(req, res, (err) => {
|
|
69
67
|
if (err) return next(err);
|
|
68
|
+
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
|
69
|
+
return next();
|
|
70
|
+
}
|
|
70
71
|
return authorize('bot:list')(req, res, next);
|
|
71
72
|
});
|
|
72
73
|
};
|
|
73
74
|
|
|
74
|
-
router.post('/:id/restart', conditionalRestartAuth, async (req, res) => {
|
|
75
|
+
router.post('/:id/restart', conditionalRestartAuth, authenticate, checkBotAccess, async (req, res) => {
|
|
75
76
|
try {
|
|
76
77
|
const botId = parseInt(req.params.id, 10);
|
|
77
78
|
botManager.stopBot(botId);
|
|
@@ -89,7 +90,7 @@ router.post('/:id/restart', conditionalRestartAuth, async (req, res) => {
|
|
|
89
90
|
}
|
|
90
91
|
});
|
|
91
92
|
|
|
92
|
-
router.post('/:id/chat', conditionalChatAuth, (req, res) => {
|
|
93
|
+
router.post('/:id/chat', conditionalChatAuth, authenticate, checkBotAccess, (req, res) => {
|
|
93
94
|
try {
|
|
94
95
|
const botId = parseInt(req.params.id, 10);
|
|
95
96
|
const { message } = req.body;
|
|
@@ -100,7 +101,7 @@ router.post('/:id/chat', conditionalChatAuth, (req, res) => {
|
|
|
100
101
|
} catch (error) { res.status(500).json({ error: 'Внутренняя ошибка сервера: ' + error.message }); }
|
|
101
102
|
});
|
|
102
103
|
|
|
103
|
-
router.post('/:id/start', conditionalStartStopAuth, async (req, res) => {
|
|
104
|
+
router.post('/:id/start', conditionalStartStopAuth, authenticate, checkBotAccess, async (req, res) => {
|
|
104
105
|
try {
|
|
105
106
|
const botId = parseInt(req.params.id, 10);
|
|
106
107
|
const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
|
|
@@ -115,7 +116,7 @@ router.post('/:id/start', conditionalStartStopAuth, async (req, res) => {
|
|
|
115
116
|
}
|
|
116
117
|
});
|
|
117
118
|
|
|
118
|
-
router.post('/:id/stop', conditionalStartStopAuth, (req, res) => {
|
|
119
|
+
router.post('/:id/stop', conditionalStartStopAuth, authenticate, checkBotAccess, (req, res) => {
|
|
119
120
|
try {
|
|
120
121
|
const botId = parseInt(req.params.id, 10);
|
|
121
122
|
botManager.stopBot(botId);
|
|
@@ -135,15 +136,37 @@ router.get('/', conditionalListAuth, async (req, res) => {
|
|
|
135
136
|
|
|
136
137
|
if (botsWithoutSortOrder.length > 0) {
|
|
137
138
|
console.log(`[API] Обновляем sortOrder для ${botsWithoutSortOrder.length} ботов`);
|
|
139
|
+
|
|
140
|
+
const maxSortOrder = await prisma.bot.aggregate({
|
|
141
|
+
_max: { sortOrder: true }
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
let nextSortOrder = (maxSortOrder._max.sortOrder || 0) + 1;
|
|
145
|
+
|
|
138
146
|
for (const bot of botsWithoutSortOrder) {
|
|
139
147
|
await prisma.bot.update({
|
|
140
148
|
where: { id: bot.id },
|
|
141
|
-
data: { sortOrder:
|
|
149
|
+
data: { sortOrder: nextSortOrder }
|
|
142
150
|
});
|
|
151
|
+
console.log(`[API] Установлен sortOrder ${nextSortOrder} для бота ${bot.id}`);
|
|
152
|
+
nextSortOrder++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let whereFilter = {};
|
|
157
|
+
if (req.user && typeof req.user.userId === 'number') {
|
|
158
|
+
const panelUser = await prisma.panelUser.findUnique({
|
|
159
|
+
where: { id: req.user.userId },
|
|
160
|
+
include: { botAccess: { select: { botId: true } } }
|
|
161
|
+
});
|
|
162
|
+
if (panelUser && panelUser.allBots === false) {
|
|
163
|
+
const allowedIds = panelUser.botAccess.map(a => a.botId);
|
|
164
|
+
whereFilter = { id: { in: allowedIds.length ? allowedIds : [-1] } };
|
|
143
165
|
}
|
|
144
166
|
}
|
|
145
167
|
|
|
146
168
|
const bots = await prisma.bot.findMany({
|
|
169
|
+
where: whereFilter,
|
|
147
170
|
include: { server: true },
|
|
148
171
|
orderBy: { sortOrder: 'asc' }
|
|
149
172
|
});
|
|
@@ -161,7 +184,119 @@ router.get('/state', conditionalListAuth, (req, res) => {
|
|
|
161
184
|
} catch (error) { res.status(500).json({ error: 'Не удалось получить состояние ботов' }); }
|
|
162
185
|
});
|
|
163
186
|
|
|
164
|
-
router.
|
|
187
|
+
router.put('/bulk-proxy-update', authenticate, authorize('bot:update'), async (req, res) => {
|
|
188
|
+
try {
|
|
189
|
+
const { botIds, proxySettings } = req.body;
|
|
190
|
+
|
|
191
|
+
if (!Array.isArray(botIds) || botIds.length === 0) {
|
|
192
|
+
return res.status(400).json({ error: 'Bot IDs array is required and cannot be empty' });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!proxySettings || !proxySettings.proxyHost || !proxySettings.proxyPort) {
|
|
196
|
+
return res.status(400).json({ error: 'Proxy host and port are required' });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (proxySettings.proxyPort < 1 || proxySettings.proxyPort > 65535) {
|
|
200
|
+
return res.status(400).json({ error: 'Proxy port must be between 1 and 65535' });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const accessibleBots = [];
|
|
204
|
+
const inaccessibleBots = [];
|
|
205
|
+
|
|
206
|
+
for (const botId of botIds) {
|
|
207
|
+
try {
|
|
208
|
+
const userId = req.user?.userId;
|
|
209
|
+
if (!userId) {
|
|
210
|
+
inaccessibleBots.push(botId);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const botIdInt = parseInt(botId, 10);
|
|
215
|
+
if (isNaN(botIdInt)) {
|
|
216
|
+
inaccessibleBots.push(botId);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const user = await prisma.panelUser.findUnique({
|
|
221
|
+
where: { id: userId },
|
|
222
|
+
include: { botAccess: { select: { botId: true } } }
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (!user) {
|
|
226
|
+
inaccessibleBots.push(botId);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (user.allBots !== false || user.botAccess.some((a) => a.botId === botIdInt)) {
|
|
231
|
+
accessibleBots.push(botIdInt);
|
|
232
|
+
} else {
|
|
233
|
+
inaccessibleBots.push(botId);
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`Error checking access for bot ${botId}:`, error);
|
|
237
|
+
inaccessibleBots.push(botId);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (accessibleBots.length === 0) {
|
|
242
|
+
return res.status(403).json({ error: 'No accessible bots in the provided list' });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const encryptedSettings = {
|
|
246
|
+
proxyHost: proxySettings.proxyHost.trim(),
|
|
247
|
+
proxyPort: parseInt(proxySettings.proxyPort),
|
|
248
|
+
proxyUsername: proxySettings.proxyUsername ? proxySettings.proxyUsername.trim() : null,
|
|
249
|
+
proxyPassword: proxySettings.proxyPassword ? encrypt(proxySettings.proxyPassword) : null
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const updatedBots = await prisma.$transaction(
|
|
253
|
+
accessibleBots.map(botId =>
|
|
254
|
+
prisma.bot.update({
|
|
255
|
+
where: { id: parseInt(botId) },
|
|
256
|
+
data: encryptedSettings,
|
|
257
|
+
include: {
|
|
258
|
+
server: {
|
|
259
|
+
select: {
|
|
260
|
+
id: true,
|
|
261
|
+
name: true,
|
|
262
|
+
host: true,
|
|
263
|
+
port: true,
|
|
264
|
+
version: true
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
if (req.io) {
|
|
273
|
+
req.io.emit('bots-updated', updatedBots);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
res.json({
|
|
277
|
+
success: true,
|
|
278
|
+
message: `Proxy settings updated for ${updatedBots.length} bot(s)`,
|
|
279
|
+
updatedBots: updatedBots.map(bot => ({
|
|
280
|
+
id: bot.id,
|
|
281
|
+
username: bot.username,
|
|
282
|
+
proxyHost: bot.proxyHost,
|
|
283
|
+
proxyPort: bot.proxyPort,
|
|
284
|
+
server: bot.server
|
|
285
|
+
})),
|
|
286
|
+
inaccessibleBots: inaccessibleBots,
|
|
287
|
+
errors: inaccessibleBots.length > 0 ? [`Access denied to ${inaccessibleBots.length} bot(s)`] : []
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error('Bulk proxy update error:', error);
|
|
292
|
+
res.status(500).json({
|
|
293
|
+
error: 'Failed to update proxy settings',
|
|
294
|
+
details: error.message
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
router.get('/:id/logs', conditionalListAuth, authenticate, checkBotAccess, (req, res) => {
|
|
165
300
|
try {
|
|
166
301
|
const botId = parseInt(req.params.id, 10);
|
|
167
302
|
const { limit = 50, offset = 0 } = req.query;
|
|
@@ -264,7 +399,7 @@ router.post('/', authorize('bot:create'), async (req, res) => {
|
|
|
264
399
|
}
|
|
265
400
|
});
|
|
266
401
|
|
|
267
|
-
router.put('/:id', authorize('bot:update'), async (req, res) => {
|
|
402
|
+
router.put('/:id', authenticate, checkBotAccess, authorize('bot:update'), async (req, res) => {
|
|
268
403
|
try {
|
|
269
404
|
const {
|
|
270
405
|
username, password, prefix, serverId, note, owners,
|
|
@@ -337,76 +472,58 @@ router.put('/:id', authorize('bot:update'), async (req, res) => {
|
|
|
337
472
|
}
|
|
338
473
|
});
|
|
339
474
|
|
|
340
|
-
router.put('/:id/sort-order', authorize('bot:update'), async (req, res) => {
|
|
475
|
+
router.put('/:id/sort-order', authenticate, checkBotAccess, authorize('bot:update'), async (req, res) => {
|
|
341
476
|
try {
|
|
342
|
-
const { newPosition } = req.body;
|
|
477
|
+
const { newPosition, oldIndex, newIndex } = req.body;
|
|
343
478
|
const botId = parseInt(req.params.id, 10);
|
|
344
479
|
|
|
345
|
-
console.log(`[API] Запрос на изменение порядка бота ${botId}
|
|
480
|
+
console.log(`[API] Запрос на изменение порядка бота ${botId}: oldIndex=${oldIndex}, newIndex=${newIndex}, newPosition=${newPosition}`);
|
|
346
481
|
|
|
347
|
-
if (isNaN(botId)
|
|
348
|
-
console.log(`[API]
|
|
349
|
-
return res.status(400).json({ error: '
|
|
482
|
+
if (isNaN(botId)) {
|
|
483
|
+
console.log(`[API] Неверный botId: ${botId}`);
|
|
484
|
+
return res.status(400).json({ error: 'Неверный ID бота' });
|
|
350
485
|
}
|
|
351
486
|
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
select: { sortOrder: true }
|
|
487
|
+
const allBots = await prisma.bot.findMany({
|
|
488
|
+
orderBy: { sortOrder: 'asc' },
|
|
489
|
+
select: { id: true, sortOrder: true }
|
|
355
490
|
});
|
|
356
491
|
|
|
357
|
-
|
|
492
|
+
console.log(`[API] Всего ботов: ${allBots.length}`);
|
|
493
|
+
const currentBotIndex = allBots.findIndex(bot => bot.id === botId);
|
|
494
|
+
if (currentBotIndex === -1) {
|
|
358
495
|
console.log(`[API] Бот ${botId} не найден`);
|
|
359
496
|
return res.status(404).json({ error: 'Бот не найден' });
|
|
360
497
|
}
|
|
361
498
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
499
|
+
if (newIndex < 0 || newIndex >= allBots.length) {
|
|
500
|
+
console.log(`[API] Неверная новая позиция: ${newIndex}`);
|
|
501
|
+
return res.status(400).json({ error: 'Неверная позиция' });
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (currentBotIndex === newIndex) {
|
|
366
505
|
console.log(`[API] Позиция не изменилась для бота ${botId}`);
|
|
367
506
|
return res.json({ success: true, message: 'Позиция не изменилась' });
|
|
368
507
|
}
|
|
508
|
+
const reorderedBots = [...allBots];
|
|
509
|
+
const [movedBot] = reorderedBots.splice(currentBotIndex, 1);
|
|
510
|
+
reorderedBots.splice(newIndex, 0, movedBot);
|
|
369
511
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
console.log(`[API] Обновлено ${updateResult.count} ботов при перемещении вниз`);
|
|
386
|
-
} else {
|
|
387
|
-
console.log(`[API] Перемещаем бота ${botId} вверх с позиции ${currentPosition} на ${newPosition}`);
|
|
388
|
-
const updateResult = await prisma.bot.updateMany({
|
|
389
|
-
where: {
|
|
390
|
-
sortOrder: {
|
|
391
|
-
gte: newPosition,
|
|
392
|
-
lt: currentPosition
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
data: {
|
|
396
|
-
sortOrder: {
|
|
397
|
-
increment: 1
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
console.log(`[API] Обновлено ${updateResult.count} ботов при перемещении вверх`);
|
|
512
|
+
console.log(`[API] Обновляем порядок для всех ботов`);
|
|
513
|
+
for (let i = 0; i < reorderedBots.length; i++) {
|
|
514
|
+
const bot = reorderedBots[i];
|
|
515
|
+
const newSortOrder = i + 1; // 1-based позиции
|
|
516
|
+
|
|
517
|
+
if (bot.sortOrder !== newSortOrder) {
|
|
518
|
+
await prisma.bot.update({
|
|
519
|
+
where: { id: bot.id },
|
|
520
|
+
data: { sortOrder: newSortOrder }
|
|
521
|
+
});
|
|
522
|
+
console.log(`[API] Обновлен бот ${bot.id}: sortOrder ${bot.sortOrder} -> ${newSortOrder}`);
|
|
523
|
+
}
|
|
402
524
|
}
|
|
403
525
|
|
|
404
|
-
|
|
405
|
-
where: { id: botId },
|
|
406
|
-
data: { sortOrder: newPosition }
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
console.log(`[API] Успешно обновлен порядок бота ${botId} на позицию ${newPosition}`);
|
|
526
|
+
console.log(`[API] Успешно обновлен порядок бота ${botId}`);
|
|
410
527
|
res.json({ success: true, message: 'Порядок ботов обновлен' });
|
|
411
528
|
} catch (error) {
|
|
412
529
|
console.error("[API Error] /bots sort-order PUT:", error);
|
|
@@ -414,7 +531,7 @@ router.put('/:id/sort-order', authorize('bot:update'), async (req, res) => {
|
|
|
414
531
|
}
|
|
415
532
|
});
|
|
416
533
|
|
|
417
|
-
router.delete('/:id', authorize('bot:delete'), async (req, res) => {
|
|
534
|
+
router.delete('/:id', authenticate, checkBotAccess, authorize('bot:delete'), async (req, res) => {
|
|
418
535
|
try {
|
|
419
536
|
const botId = parseInt(req.params.id, 10);
|
|
420
537
|
if (botManager.bots.has(botId)) return res.status(400).json({ error: 'Нельзя удалить запущенного бота' });
|
|
@@ -433,7 +550,7 @@ router.get('/servers', authorize('bot:list'), async (req, res) => {
|
|
|
433
550
|
}
|
|
434
551
|
});
|
|
435
552
|
|
|
436
|
-
router.get('/:botId/plugins', authorize('plugin:list'), async (req, res) => {
|
|
553
|
+
router.get('/:botId/plugins', authenticate, checkBotAccess, authorize('plugin:list'), async (req, res) => {
|
|
437
554
|
try {
|
|
438
555
|
const botId = parseInt(req.params.botId);
|
|
439
556
|
const plugins = await prisma.installedPlugin.findMany({ where: { botId } });
|
|
@@ -441,7 +558,7 @@ router.get('/:botId/plugins', authorize('plugin:list'), async (req, res) => {
|
|
|
441
558
|
} catch (error) { res.status(500).json({ error: 'Не удалось получить плагины бота' }); }
|
|
442
559
|
});
|
|
443
560
|
|
|
444
|
-
router.post('/:botId/plugins/install/github', authorize('plugin:install'), async (req, res) => {
|
|
561
|
+
router.post('/:botId/plugins/install/github', authenticate, checkBotAccess, authorize('plugin:install'), async (req, res) => {
|
|
445
562
|
const { botId } = req.params;
|
|
446
563
|
const { repoUrl } = req.body;
|
|
447
564
|
try {
|
|
@@ -452,7 +569,7 @@ router.post('/:botId/plugins/install/github', authorize('plugin:install'), async
|
|
|
452
569
|
}
|
|
453
570
|
});
|
|
454
571
|
|
|
455
|
-
router.post('/:botId/plugins/install/local', authorize('plugin:install'), async (req, res) => {
|
|
572
|
+
router.post('/:botId/plugins/install/local', authenticate, checkBotAccess, authorize('plugin:install'), async (req, res) => {
|
|
456
573
|
const { botId } = req.params;
|
|
457
574
|
const { path } = req.body;
|
|
458
575
|
try {
|
|
@@ -463,7 +580,7 @@ router.post('/:botId/plugins/install/local', authorize('plugin:install'), async
|
|
|
463
580
|
}
|
|
464
581
|
});
|
|
465
582
|
|
|
466
|
-
router.delete('/:botId/plugins/:pluginId', authorize('plugin:delete'), async (req, res) => {
|
|
583
|
+
router.delete('/:botId/plugins/:pluginId', authenticate, checkBotAccess, authorize('plugin:delete'), async (req, res) => {
|
|
467
584
|
const { pluginId } = req.params;
|
|
468
585
|
try {
|
|
469
586
|
await pluginManager.deletePlugin(parseInt(pluginId));
|
|
@@ -473,7 +590,7 @@ router.delete('/:botId/plugins/:pluginId', authorize('plugin:delete'), async (re
|
|
|
473
590
|
}
|
|
474
591
|
});
|
|
475
592
|
|
|
476
|
-
router.get('/:botId/plugins/:pluginId/settings', authorize('plugin:settings:view'), async (req, res) => {
|
|
593
|
+
router.get('/:botId/plugins/:pluginId/settings', authenticate, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
|
|
477
594
|
try {
|
|
478
595
|
const pluginId = parseInt(req.params.pluginId);
|
|
479
596
|
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
@@ -531,120 +648,107 @@ router.get('/:botId/plugins/:pluginId/settings', authorize('plugin:settings:view
|
|
|
531
648
|
}
|
|
532
649
|
});
|
|
533
650
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
|
|
540
|
-
|
|
541
|
-
const rows = await prisma.pluginDataStore.findMany({
|
|
542
|
-
where: { botId: plugin.botId, pluginName: plugin.name },
|
|
543
|
-
orderBy: { updatedAt: 'desc' }
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
const result = rows.map(r => {
|
|
547
|
-
let value;
|
|
548
|
-
try { value = JSON.parse(r.value); } catch { value = r.value; }
|
|
549
|
-
return { key: r.key, value, createdAt: r.createdAt, updatedAt: r.updatedAt };
|
|
550
|
-
});
|
|
551
|
-
res.json(result);
|
|
552
|
-
} catch (error) {
|
|
553
|
-
console.error('[API Error] GET plugin data:', error);
|
|
554
|
-
res.status(500).json({ error: 'Не удалось получить данные плагина' });
|
|
555
|
-
}
|
|
556
|
-
});
|
|
651
|
+
router.get('/:botId/plugins/:pluginId/data', authenticate, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
|
|
652
|
+
try {
|
|
653
|
+
const pluginId = parseInt(req.params.pluginId);
|
|
654
|
+
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
655
|
+
if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
|
|
557
656
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
563
|
-
if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
|
|
657
|
+
const rows = await prisma.pluginDataStore.findMany({
|
|
658
|
+
where: { botId: plugin.botId, pluginName: plugin.name },
|
|
659
|
+
orderBy: { updatedAt: 'desc' }
|
|
660
|
+
});
|
|
564
661
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
});
|
|
574
|
-
if (!row) return res.status(404).json({ error: 'Ключ не найден' });
|
|
575
|
-
let value; try { value = JSON.parse(row.value); } catch { value = row.value; }
|
|
576
|
-
res.json({ key: row.key, value, createdAt: row.createdAt, updatedAt: row.updatedAt });
|
|
577
|
-
} catch (error) {
|
|
578
|
-
console.error('[API Error] GET plugin data by key:', error);
|
|
579
|
-
res.status(500).json({ error: 'Не удалось получить значение по ключу' });
|
|
580
|
-
}
|
|
662
|
+
const result = rows.map(r => {
|
|
663
|
+
let value;
|
|
664
|
+
try { value = JSON.parse(r.value); } catch { value = r.value; }
|
|
665
|
+
return { key: r.key, value, createdAt: r.createdAt, updatedAt: r.updatedAt };
|
|
666
|
+
});
|
|
667
|
+
res.json(result);
|
|
668
|
+
} catch (error) { res.status(500).json({ error: 'Не удалось получить данные плагина' }); }
|
|
581
669
|
});
|
|
582
670
|
|
|
583
|
-
router.
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
let parsed; try { parsed = JSON.parse(upserted.value); } catch { parsed = upserted.value; }
|
|
604
|
-
res.json({ key: upserted.key, value: parsed, createdAt: upserted.createdAt, updatedAt: upserted.updatedAt });
|
|
605
|
-
} catch (error) {
|
|
606
|
-
console.error('[API Error] PUT plugin data by key:', error);
|
|
607
|
-
res.status(500).json({ error: 'Не удалось сохранить значение' });
|
|
608
|
-
}
|
|
671
|
+
router.get('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
|
|
672
|
+
try {
|
|
673
|
+
const pluginId = parseInt(req.params.pluginId);
|
|
674
|
+
const { key } = req.params;
|
|
675
|
+
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
676
|
+
if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
|
|
677
|
+
|
|
678
|
+
const row = await prisma.pluginDataStore.findUnique({
|
|
679
|
+
where: {
|
|
680
|
+
pluginName_botId_key: {
|
|
681
|
+
pluginName: plugin.name,
|
|
682
|
+
botId: plugin.botId,
|
|
683
|
+
key
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
if (!row) return res.status(404).json({ error: 'Ключ не найден' });
|
|
688
|
+
let value; try { value = JSON.parse(row.value); } catch { value = row.value; }
|
|
689
|
+
res.json({ key: row.key, value, createdAt: row.createdAt, updatedAt: row.updatedAt });
|
|
690
|
+
} catch (error) { res.status(500).json({ error: 'Не удалось получить значение по ключу' }); }
|
|
609
691
|
});
|
|
610
692
|
|
|
611
|
-
router.
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
693
|
+
router.put('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
|
|
694
|
+
try {
|
|
695
|
+
const pluginId = parseInt(req.params.pluginId);
|
|
696
|
+
const { key } = req.params;
|
|
697
|
+
const { value } = req.body;
|
|
698
|
+
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
699
|
+
if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
|
|
700
|
+
|
|
701
|
+
const jsonValue = JSON.stringify(value ?? null);
|
|
702
|
+
const upserted = await prisma.pluginDataStore.upsert({
|
|
703
|
+
where: {
|
|
704
|
+
pluginName_botId_key: {
|
|
705
|
+
pluginName: plugin.name,
|
|
706
|
+
botId: plugin.botId,
|
|
707
|
+
key
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
update: { value: jsonValue },
|
|
711
|
+
create: { pluginName: plugin.name, botId: plugin.botId, key, value: jsonValue }
|
|
712
|
+
});
|
|
713
|
+
let parsed; try { parsed = JSON.parse(upserted.value); } catch { parsed = upserted.value; }
|
|
714
|
+
res.json({ key: upserted.key, value: parsed, createdAt: upserted.createdAt, updatedAt: upserted.updatedAt });
|
|
715
|
+
} catch (error) { res.status(500).json({ error: 'Не удалось сохранить значение' }); }
|
|
716
|
+
});
|
|
617
717
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
718
|
+
router.delete('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
|
|
719
|
+
try {
|
|
720
|
+
const pluginId = parseInt(req.params.pluginId);
|
|
721
|
+
const { key } = req.params;
|
|
722
|
+
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
723
|
+
if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
|
|
724
|
+
|
|
725
|
+
await prisma.pluginDataStore.delete({
|
|
726
|
+
where: {
|
|
727
|
+
pluginName_botId_key: {
|
|
728
|
+
pluginName: plugin.name,
|
|
729
|
+
botId: plugin.botId,
|
|
730
|
+
key
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
res.status(204).send();
|
|
735
|
+
} catch (error) { res.status(500).json({ error: 'Не удалось удалить значение' }); }
|
|
632
736
|
});
|
|
633
737
|
|
|
634
|
-
router.put('/:botId/plugins/:pluginId', authorize('plugin:settings:edit'), async (req, res) => {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
738
|
+
router.put('/:botId/plugins/:pluginId', authenticate, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
|
|
739
|
+
try {
|
|
740
|
+
const pluginId = parseInt(req.params.pluginId);
|
|
741
|
+
const { isEnabled, settings } = req.body;
|
|
742
|
+
const dataToUpdate = {};
|
|
743
|
+
if (typeof isEnabled === 'boolean') dataToUpdate.isEnabled = isEnabled;
|
|
744
|
+
if (settings) dataToUpdate.settings = JSON.stringify(settings);
|
|
745
|
+
if (Object.keys(dataToUpdate).length === 0) return res.status(400).json({ error: "Нет данных для обновления" });
|
|
746
|
+
const updated = await prisma.installedPlugin.update({ where: { id: pluginId }, data: dataToUpdate });
|
|
747
|
+
res.json(updated);
|
|
748
|
+
} catch (error) { res.status(500).json({ error: 'Не удалось обновить плагин' }); }
|
|
645
749
|
});
|
|
646
750
|
|
|
647
|
-
router.get('/:botId/management-data', authorize('management:view'), async (req, res) => {
|
|
751
|
+
router.get('/:botId/management-data', authenticate, checkBotAccess, authorize('management:view'), async (req, res) => {
|
|
648
752
|
try {
|
|
649
753
|
const botId = parseInt(req.params.botId, 10);
|
|
650
754
|
if (isNaN(botId)) return res.status(400).json({ error: 'Неверный ID бота' });
|
|
@@ -1007,7 +1111,7 @@ router.post('/stop-all', authorize('bot:start_stop'), (req, res) => {
|
|
|
1007
1111
|
}
|
|
1008
1112
|
});
|
|
1009
1113
|
|
|
1010
|
-
router.get('/:id/settings/all', authorize('bot:update'), async (req, res) => {
|
|
1114
|
+
router.get('/:id/settings/all', authenticate, checkBotAccess, authorize('bot:update'), async (req, res) => {
|
|
1011
1115
|
try {
|
|
1012
1116
|
const botId = parseInt(req.params.id, 10);
|
|
1013
1117
|
|
|
@@ -1086,7 +1190,7 @@ router.get('/:id/settings/all', authorize('bot:update'), async (req, res) => {
|
|
|
1086
1190
|
|
|
1087
1191
|
const nodeRegistry = require('../../core/NodeRegistry');
|
|
1088
1192
|
|
|
1089
|
-
router.get('/:botId/visual-editor/nodes', authorize('management:view'), (req, res) => {
|
|
1193
|
+
router.get('/:botId/visual-editor/nodes', authenticate, checkBotAccess, authorize('management:view'), (req, res) => {
|
|
1090
1194
|
try {
|
|
1091
1195
|
const { graphType } = req.query;
|
|
1092
1196
|
const nodesByCategory = nodeRegistry.getNodesByCategory(graphType);
|
|
@@ -1097,7 +1201,7 @@ router.get('/:botId/visual-editor/nodes', authorize('management:view'), (req, re
|
|
|
1097
1201
|
}
|
|
1098
1202
|
});
|
|
1099
1203
|
|
|
1100
|
-
router.get('/:botId/visual-editor/node-config', authorize('management:view'), (req, res) => {
|
|
1204
|
+
router.get('/:botId/visual-editor/node-config', authenticate, checkBotAccess, authorize('management:view'), (req, res) => {
|
|
1101
1205
|
try {
|
|
1102
1206
|
const { types } = req.query;
|
|
1103
1207
|
if (!types) {
|
|
@@ -1112,7 +1216,7 @@ router.get('/:botId/visual-editor/node-config', authorize('management:view'), (r
|
|
|
1112
1216
|
}
|
|
1113
1217
|
});
|
|
1114
1218
|
|
|
1115
|
-
router.get('/:botId/visual-editor/permissions', authorize('management:view'), async (req, res) => {
|
|
1219
|
+
router.get('/:botId/visual-editor/permissions', authenticate, checkBotAccess, authorize('management:view'), async (req, res) => {
|
|
1116
1220
|
try {
|
|
1117
1221
|
const botId = parseInt(req.params.botId, 10);
|
|
1118
1222
|
const permissions = await prisma.permission.findMany({
|
|
@@ -1733,7 +1837,7 @@ router.post('/:botId/plugins/:pluginName/action', authorize('plugin:list'), asyn
|
|
|
1733
1837
|
});
|
|
1734
1838
|
|
|
1735
1839
|
|
|
1736
|
-
router.get('/:botId/export', authorize('bot:export'), async (req, res) => {
|
|
1840
|
+
router.get('/:botId/export', authenticate, checkBotAccess, authorize('bot:export'), async (req, res) => {
|
|
1737
1841
|
try {
|
|
1738
1842
|
const botId = parseInt(req.params.botId, 10);
|
|
1739
1843
|
const {
|
|
@@ -1964,7 +2068,18 @@ router.post('/import', authorize('bot:create'), upload.single('file'), async (re
|
|
|
1964
2068
|
}
|
|
1965
2069
|
}
|
|
1966
2070
|
|
|
1967
|
-
|
|
2071
|
+
try {
|
|
2072
|
+
await pluginManager._installDependencies(newPluginPath);
|
|
2073
|
+
} catch (e) {
|
|
2074
|
+
console.warn(`[Import] Не удалось установить зависимости для плагина ${pluginName}: ${e.message}`);
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
let newPlugin;
|
|
2078
|
+
try {
|
|
2079
|
+
newPlugin = await pluginManager.registerPlugin(newBot.id, newPluginPath, 'LOCAL', newPluginPath);
|
|
2080
|
+
} catch (e) {
|
|
2081
|
+
newPlugin = await prisma.installedPlugin.create({ data: pluginData });
|
|
2082
|
+
}
|
|
1968
2083
|
pluginMap.set(oldPluginId, newPlugin.id);
|
|
1969
2084
|
}
|
|
1970
2085
|
}
|