blockmine 1.18.4 → 1.19.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.
Files changed (29) hide show
  1. package/CHANGELOG.md +144 -117
  2. package/backend/cli.js +59 -57
  3. package/backend/prisma/migrations/20250701190321_add/migration.sql +2 -2
  4. package/backend/prisma/migrations/20250709150611_add_run_on_startup_to_tasks/migration.sql +21 -21
  5. package/backend/prisma/migrations/20250709151124_make_cron_pattern_optional/migration.sql +21 -21
  6. package/backend/prisma/migrations/20250718181335_add_plugin_data_store/migration.sql +14 -14
  7. package/backend/prisma/migrations/20250719115906_add_plugin_owner_to_graphs/migration.sql +45 -45
  8. package/backend/prisma/migrations/20250723160648_add_bot_sort_order/migration.sql +2 -2
  9. package/backend/prisma/migrations/20250816083216_add_panel_user_bot_access/migration.sql +30 -0
  10. package/backend/prisma/migrations/migration_lock.toml +2 -2
  11. package/backend/prisma/schema.prisma +244 -229
  12. package/backend/src/api/middleware/botAccess.js +35 -0
  13. package/backend/src/api/routes/auth.js +633 -595
  14. package/backend/src/api/routes/bots.js +251 -127
  15. package/backend/src/api/routes/eventGraphs.js +459 -459
  16. package/backend/src/api/routes/servers.js +27 -0
  17. package/backend/src/core/GraphExecutionEngine.js +917 -917
  18. package/backend/src/core/PluginLoader.js +208 -86
  19. package/backend/src/core/PluginManager.js +465 -427
  20. package/backend/src/core/commands/dev.js +6 -1
  21. package/backend/src/real-time/presence.js +74 -0
  22. package/backend/src/real-time/socketHandler.js +2 -0
  23. package/backend/src/server.js +193 -186
  24. package/frontend/dist/assets/index-5m_JZxJ-.js +8352 -0
  25. package/frontend/dist/assets/index-BFd7YoAj.css +1 -0
  26. package/frontend/dist/index.html +2 -2
  27. package/package.json +1 -1
  28. package/frontend/dist/assets/index-CA3XrPPP.css +0 -1
  29. 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);
@@ -142,8 +143,21 @@ router.get('/', conditionalListAuth, async (req, res) => {
142
143
  });
143
144
  }
144
145
  }
146
+
147
+ let whereFilter = {};
148
+ if (req.user && typeof req.user.userId === 'number') {
149
+ const panelUser = await prisma.panelUser.findUnique({
150
+ where: { id: req.user.userId },
151
+ include: { botAccess: { select: { botId: true } } }
152
+ });
153
+ if (panelUser && panelUser.allBots === false) {
154
+ const allowedIds = panelUser.botAccess.map(a => a.botId);
155
+ whereFilter = { id: { in: allowedIds.length ? allowedIds : [-1] } };
156
+ }
157
+ }
145
158
 
146
159
  const bots = await prisma.bot.findMany({
160
+ where: whereFilter,
147
161
  include: { server: true },
148
162
  orderBy: { sortOrder: 'asc' }
149
163
  });
@@ -161,7 +175,119 @@ router.get('/state', conditionalListAuth, (req, res) => {
161
175
  } catch (error) { res.status(500).json({ error: 'Не удалось получить состояние ботов' }); }
162
176
  });
163
177
 
164
- router.get('/:id/logs', conditionalListAuth, (req, res) => {
178
+ router.put('/bulk-proxy-update', authenticate, authorize('bot:update'), async (req, res) => {
179
+ try {
180
+ const { botIds, proxySettings } = req.body;
181
+
182
+ if (!Array.isArray(botIds) || botIds.length === 0) {
183
+ return res.status(400).json({ error: 'Bot IDs array is required and cannot be empty' });
184
+ }
185
+
186
+ if (!proxySettings || !proxySettings.proxyHost || !proxySettings.proxyPort) {
187
+ return res.status(400).json({ error: 'Proxy host and port are required' });
188
+ }
189
+
190
+ if (proxySettings.proxyPort < 1 || proxySettings.proxyPort > 65535) {
191
+ return res.status(400).json({ error: 'Proxy port must be between 1 and 65535' });
192
+ }
193
+
194
+ const accessibleBots = [];
195
+ const inaccessibleBots = [];
196
+
197
+ for (const botId of botIds) {
198
+ try {
199
+ const userId = req.user?.userId;
200
+ if (!userId) {
201
+ inaccessibleBots.push(botId);
202
+ continue;
203
+ }
204
+
205
+ const botIdInt = parseInt(botId, 10);
206
+ if (isNaN(botIdInt)) {
207
+ inaccessibleBots.push(botId);
208
+ continue;
209
+ }
210
+
211
+ const user = await prisma.panelUser.findUnique({
212
+ where: { id: userId },
213
+ include: { botAccess: { select: { botId: true } } }
214
+ });
215
+
216
+ if (!user) {
217
+ inaccessibleBots.push(botId);
218
+ continue;
219
+ }
220
+
221
+ if (user.allBots !== false || user.botAccess.some((a) => a.botId === botIdInt)) {
222
+ accessibleBots.push(botIdInt);
223
+ } else {
224
+ inaccessibleBots.push(botId);
225
+ }
226
+ } catch (error) {
227
+ console.error(`Error checking access for bot ${botId}:`, error);
228
+ inaccessibleBots.push(botId);
229
+ }
230
+ }
231
+
232
+ if (accessibleBots.length === 0) {
233
+ return res.status(403).json({ error: 'No accessible bots in the provided list' });
234
+ }
235
+
236
+ const encryptedSettings = {
237
+ proxyHost: proxySettings.proxyHost.trim(),
238
+ proxyPort: parseInt(proxySettings.proxyPort),
239
+ proxyUsername: proxySettings.proxyUsername ? encrypt(proxySettings.proxyUsername.trim()) : null,
240
+ proxyPassword: proxySettings.proxyPassword ? encrypt(proxySettings.proxyPassword) : null
241
+ };
242
+
243
+ const updatedBots = await prisma.$transaction(
244
+ accessibleBots.map(botId =>
245
+ prisma.bot.update({
246
+ where: { id: parseInt(botId) },
247
+ data: encryptedSettings,
248
+ include: {
249
+ server: {
250
+ select: {
251
+ id: true,
252
+ name: true,
253
+ host: true,
254
+ port: true,
255
+ version: true
256
+ }
257
+ }
258
+ }
259
+ })
260
+ )
261
+ );
262
+
263
+ if (req.io) {
264
+ req.io.emit('bots-updated', updatedBots);
265
+ }
266
+
267
+ res.json({
268
+ success: true,
269
+ message: `Proxy settings updated for ${updatedBots.length} bot(s)`,
270
+ updatedBots: updatedBots.map(bot => ({
271
+ id: bot.id,
272
+ username: bot.username,
273
+ proxyHost: bot.proxyHost,
274
+ proxyPort: bot.proxyPort,
275
+ server: bot.server
276
+ })),
277
+ inaccessibleBots: inaccessibleBots,
278
+ errors: inaccessibleBots.length > 0 ? [`Access denied to ${inaccessibleBots.length} bot(s)`] : []
279
+ });
280
+
281
+ } catch (error) {
282
+ console.error('Bulk proxy update error:', error);
283
+ res.status(500).json({
284
+ error: 'Failed to update proxy settings',
285
+ details: error.message
286
+ });
287
+ }
288
+ });
289
+
290
+ router.get('/:id/logs', conditionalListAuth, authenticate, checkBotAccess, (req, res) => {
165
291
  try {
166
292
  const botId = parseInt(req.params.id, 10);
167
293
  const { limit = 50, offset = 0 } = req.query;
@@ -264,7 +390,7 @@ router.post('/', authorize('bot:create'), async (req, res) => {
264
390
  }
265
391
  });
266
392
 
267
- router.put('/:id', authorize('bot:update'), async (req, res) => {
393
+ router.put('/:id', authenticate, checkBotAccess, authorize('bot:update'), async (req, res) => {
268
394
  try {
269
395
  const {
270
396
  username, password, prefix, serverId, note, owners,
@@ -337,7 +463,7 @@ router.put('/:id', authorize('bot:update'), async (req, res) => {
337
463
  }
338
464
  });
339
465
 
340
- router.put('/:id/sort-order', authorize('bot:update'), async (req, res) => {
466
+ router.put('/:id/sort-order', authenticate, checkBotAccess, authorize('bot:update'), async (req, res) => {
341
467
  try {
342
468
  const { newPosition } = req.body;
343
469
  const botId = parseInt(req.params.id, 10);
@@ -414,7 +540,7 @@ router.put('/:id/sort-order', authorize('bot:update'), async (req, res) => {
414
540
  }
415
541
  });
416
542
 
417
- router.delete('/:id', authorize('bot:delete'), async (req, res) => {
543
+ router.delete('/:id', authenticate, checkBotAccess, authorize('bot:delete'), async (req, res) => {
418
544
  try {
419
545
  const botId = parseInt(req.params.id, 10);
420
546
  if (botManager.bots.has(botId)) return res.status(400).json({ error: 'Нельзя удалить запущенного бота' });
@@ -433,7 +559,7 @@ router.get('/servers', authorize('bot:list'), async (req, res) => {
433
559
  }
434
560
  });
435
561
 
436
- router.get('/:botId/plugins', authorize('plugin:list'), async (req, res) => {
562
+ router.get('/:botId/plugins', authenticate, checkBotAccess, authorize('plugin:list'), async (req, res) => {
437
563
  try {
438
564
  const botId = parseInt(req.params.botId);
439
565
  const plugins = await prisma.installedPlugin.findMany({ where: { botId } });
@@ -441,7 +567,7 @@ router.get('/:botId/plugins', authorize('plugin:list'), async (req, res) => {
441
567
  } catch (error) { res.status(500).json({ error: 'Не удалось получить плагины бота' }); }
442
568
  });
443
569
 
444
- router.post('/:botId/plugins/install/github', authorize('plugin:install'), async (req, res) => {
570
+ router.post('/:botId/plugins/install/github', authenticate, checkBotAccess, authorize('plugin:install'), async (req, res) => {
445
571
  const { botId } = req.params;
446
572
  const { repoUrl } = req.body;
447
573
  try {
@@ -452,7 +578,7 @@ router.post('/:botId/plugins/install/github', authorize('plugin:install'), async
452
578
  }
453
579
  });
454
580
 
455
- router.post('/:botId/plugins/install/local', authorize('plugin:install'), async (req, res) => {
581
+ router.post('/:botId/plugins/install/local', authenticate, checkBotAccess, authorize('plugin:install'), async (req, res) => {
456
582
  const { botId } = req.params;
457
583
  const { path } = req.body;
458
584
  try {
@@ -463,7 +589,7 @@ router.post('/:botId/plugins/install/local', authorize('plugin:install'), async
463
589
  }
464
590
  });
465
591
 
466
- router.delete('/:botId/plugins/:pluginId', authorize('plugin:delete'), async (req, res) => {
592
+ router.delete('/:botId/plugins/:pluginId', authenticate, checkBotAccess, authorize('plugin:delete'), async (req, res) => {
467
593
  const { pluginId } = req.params;
468
594
  try {
469
595
  await pluginManager.deletePlugin(parseInt(pluginId));
@@ -473,7 +599,7 @@ router.delete('/:botId/plugins/:pluginId', authorize('plugin:delete'), async (re
473
599
  }
474
600
  });
475
601
 
476
- router.get('/:botId/plugins/:pluginId/settings', authorize('plugin:settings:view'), async (req, res) => {
602
+ router.get('/:botId/plugins/:pluginId/settings', authenticate, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
477
603
  try {
478
604
  const pluginId = parseInt(req.params.pluginId);
479
605
  const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
@@ -531,120 +657,107 @@ router.get('/:botId/plugins/:pluginId/settings', authorize('plugin:settings:view
531
657
  }
532
658
  });
533
659
 
534
- // Вкладка Данные плагина (PluginDataStore)
535
- router.get('/:botId/plugins/:pluginId/data', authorize('plugin:settings:view'), async (req, res) => {
536
- try {
537
- const pluginId = parseInt(req.params.pluginId);
538
- const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
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
- });
660
+ router.get('/:botId/plugins/:pluginId/data', authenticate, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
661
+ try {
662
+ const pluginId = parseInt(req.params.pluginId);
663
+ const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
664
+ if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
557
665
 
558
- router.get('/:botId/plugins/:pluginId/data/:key', authorize('plugin:settings:view'), async (req, res) => {
559
- try {
560
- const pluginId = parseInt(req.params.pluginId);
561
- const { key } = req.params;
562
- const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
563
- if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
666
+ const rows = await prisma.pluginDataStore.findMany({
667
+ where: { botId: plugin.botId, pluginName: plugin.name },
668
+ orderBy: { updatedAt: 'desc' }
669
+ });
564
670
 
565
- const row = await prisma.pluginDataStore.findUnique({
566
- where: {
567
- pluginName_botId_key: {
568
- pluginName: plugin.name,
569
- botId: plugin.botId,
570
- key
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
- }
671
+ const result = rows.map(r => {
672
+ let value;
673
+ try { value = JSON.parse(r.value); } catch { value = r.value; }
674
+ return { key: r.key, value, createdAt: r.createdAt, updatedAt: r.updatedAt };
675
+ });
676
+ res.json(result);
677
+ } catch (error) { res.status(500).json({ error: 'Не удалось получить данные плагина' }); }
581
678
  });
582
679
 
583
- router.put('/:botId/plugins/:pluginId/data/:key', authorize('plugin:settings:edit'), async (req, res) => {
584
- try {
585
- const pluginId = parseInt(req.params.pluginId);
586
- const { key } = req.params;
587
- const { value } = req.body;
588
- const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
589
- if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
590
-
591
- const jsonValue = JSON.stringify(value ?? null);
592
- const upserted = await prisma.pluginDataStore.upsert({
593
- where: {
594
- pluginName_botId_key: {
595
- pluginName: plugin.name,
596
- botId: plugin.botId,
597
- key
598
- }
599
- },
600
- update: { value: jsonValue },
601
- create: { pluginName: plugin.name, botId: plugin.botId, key, value: jsonValue }
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
- }
680
+ router.get('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
681
+ try {
682
+ const pluginId = parseInt(req.params.pluginId);
683
+ const { key } = req.params;
684
+ const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
685
+ if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
686
+
687
+ const row = await prisma.pluginDataStore.findUnique({
688
+ where: {
689
+ pluginName_botId_key: {
690
+ pluginName: plugin.name,
691
+ botId: plugin.botId,
692
+ key
693
+ }
694
+ }
695
+ });
696
+ if (!row) return res.status(404).json({ error: 'Ключ не найден' });
697
+ let value; try { value = JSON.parse(row.value); } catch { value = row.value; }
698
+ res.json({ key: row.key, value, createdAt: row.createdAt, updatedAt: row.updatedAt });
699
+ } catch (error) { res.status(500).json({ error: 'Не удалось получить значение по ключу' }); }
609
700
  });
610
701
 
611
- router.delete('/:botId/plugins/:pluginId/data/:key', authorize('plugin:settings:edit'), async (req, res) => {
612
- try {
613
- const pluginId = parseInt(req.params.pluginId);
614
- const { key } = req.params;
615
- const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
616
- if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
702
+ router.put('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
703
+ try {
704
+ const pluginId = parseInt(req.params.pluginId);
705
+ const { key } = req.params;
706
+ const { value } = req.body;
707
+ const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
708
+ if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
709
+
710
+ const jsonValue = JSON.stringify(value ?? null);
711
+ const upserted = await prisma.pluginDataStore.upsert({
712
+ where: {
713
+ pluginName_botId_key: {
714
+ pluginName: plugin.name,
715
+ botId: plugin.botId,
716
+ key
717
+ }
718
+ },
719
+ update: { value: jsonValue },
720
+ create: { pluginName: plugin.name, botId: plugin.botId, key, value: jsonValue }
721
+ });
722
+ let parsed; try { parsed = JSON.parse(upserted.value); } catch { parsed = upserted.value; }
723
+ res.json({ key: upserted.key, value: parsed, createdAt: upserted.createdAt, updatedAt: upserted.updatedAt });
724
+ } catch (error) { res.status(500).json({ error: 'Не удалось сохранить значение' }); }
725
+ });
617
726
 
618
- await prisma.pluginDataStore.delete({
619
- where: {
620
- pluginName_botId_key: {
621
- pluginName: plugin.name,
622
- botId: plugin.botId,
623
- key
624
- }
625
- }
626
- });
627
- res.status(204).send();
628
- } catch (error) {
629
- console.error('[API Error] DELETE plugin data by key:', error);
630
- res.status(500).json({ error: 'Не удалось удалить значение' });
631
- }
727
+ router.delete('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
728
+ try {
729
+ const pluginId = parseInt(req.params.pluginId);
730
+ const { key } = req.params;
731
+ const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
732
+ if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
733
+
734
+ await prisma.pluginDataStore.delete({
735
+ where: {
736
+ pluginName_botId_key: {
737
+ pluginName: plugin.name,
738
+ botId: plugin.botId,
739
+ key
740
+ }
741
+ }
742
+ });
743
+ res.status(204).send();
744
+ } catch (error) { res.status(500).json({ error: 'Не удалось удалить значение' }); }
632
745
  });
633
746
 
634
- router.put('/:botId/plugins/:pluginId', authorize('plugin:settings:edit'), async (req, res) => {
635
- try {
636
- const pluginId = parseInt(req.params.pluginId);
637
- const { isEnabled, settings } = req.body;
638
- const dataToUpdate = {};
639
- if (typeof isEnabled === 'boolean') dataToUpdate.isEnabled = isEnabled;
640
- if (settings) dataToUpdate.settings = JSON.stringify(settings);
641
- if (Object.keys(dataToUpdate).length === 0) return res.status(400).json({ error: "Нет данных для обновления" });
642
- const updated = await prisma.installedPlugin.update({ where: { id: pluginId }, data: dataToUpdate });
643
- res.json(updated);
644
- } catch (error) { res.status(500).json({ error: 'Не удалось обновить плагин' }); }
747
+ router.put('/:botId/plugins/:pluginId', authenticate, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
748
+ try {
749
+ const pluginId = parseInt(req.params.pluginId);
750
+ const { isEnabled, settings } = req.body;
751
+ const dataToUpdate = {};
752
+ if (typeof isEnabled === 'boolean') dataToUpdate.isEnabled = isEnabled;
753
+ if (settings) dataToUpdate.settings = JSON.stringify(settings);
754
+ if (Object.keys(dataToUpdate).length === 0) return res.status(400).json({ error: "Нет данных для обновления" });
755
+ const updated = await prisma.installedPlugin.update({ where: { id: pluginId }, data: dataToUpdate });
756
+ res.json(updated);
757
+ } catch (error) { res.status(500).json({ error: 'Не удалось обновить плагин' }); }
645
758
  });
646
759
 
647
- router.get('/:botId/management-data', authorize('management:view'), async (req, res) => {
760
+ router.get('/:botId/management-data', authenticate, checkBotAccess, authorize('management:view'), async (req, res) => {
648
761
  try {
649
762
  const botId = parseInt(req.params.botId, 10);
650
763
  if (isNaN(botId)) return res.status(400).json({ error: 'Неверный ID бота' });
@@ -1007,7 +1120,7 @@ router.post('/stop-all', authorize('bot:start_stop'), (req, res) => {
1007
1120
  }
1008
1121
  });
1009
1122
 
1010
- router.get('/:id/settings/all', authorize('bot:update'), async (req, res) => {
1123
+ router.get('/:id/settings/all', authenticate, checkBotAccess, authorize('bot:update'), async (req, res) => {
1011
1124
  try {
1012
1125
  const botId = parseInt(req.params.id, 10);
1013
1126
 
@@ -1086,7 +1199,7 @@ router.get('/:id/settings/all', authorize('bot:update'), async (req, res) => {
1086
1199
 
1087
1200
  const nodeRegistry = require('../../core/NodeRegistry');
1088
1201
 
1089
- router.get('/:botId/visual-editor/nodes', authorize('management:view'), (req, res) => {
1202
+ router.get('/:botId/visual-editor/nodes', authenticate, checkBotAccess, authorize('management:view'), (req, res) => {
1090
1203
  try {
1091
1204
  const { graphType } = req.query;
1092
1205
  const nodesByCategory = nodeRegistry.getNodesByCategory(graphType);
@@ -1097,7 +1210,7 @@ router.get('/:botId/visual-editor/nodes', authorize('management:view'), (req, re
1097
1210
  }
1098
1211
  });
1099
1212
 
1100
- router.get('/:botId/visual-editor/node-config', authorize('management:view'), (req, res) => {
1213
+ router.get('/:botId/visual-editor/node-config', authenticate, checkBotAccess, authorize('management:view'), (req, res) => {
1101
1214
  try {
1102
1215
  const { types } = req.query;
1103
1216
  if (!types) {
@@ -1112,7 +1225,7 @@ router.get('/:botId/visual-editor/node-config', authorize('management:view'), (r
1112
1225
  }
1113
1226
  });
1114
1227
 
1115
- router.get('/:botId/visual-editor/permissions', authorize('management:view'), async (req, res) => {
1228
+ router.get('/:botId/visual-editor/permissions', authenticate, checkBotAccess, authorize('management:view'), async (req, res) => {
1116
1229
  try {
1117
1230
  const botId = parseInt(req.params.botId, 10);
1118
1231
  const permissions = await prisma.permission.findMany({
@@ -1733,7 +1846,7 @@ router.post('/:botId/plugins/:pluginName/action', authorize('plugin:list'), asyn
1733
1846
  });
1734
1847
 
1735
1848
 
1736
- router.get('/:botId/export', authorize('bot:export'), async (req, res) => {
1849
+ router.get('/:botId/export', authenticate, checkBotAccess, authorize('bot:export'), async (req, res) => {
1737
1850
  try {
1738
1851
  const botId = parseInt(req.params.botId, 10);
1739
1852
  const {
@@ -1964,7 +2077,18 @@ router.post('/import', authorize('bot:create'), upload.single('file'), async (re
1964
2077
  }
1965
2078
  }
1966
2079
 
1967
- const newPlugin = await prisma.installedPlugin.create({ data: pluginData });
2080
+ try {
2081
+ await pluginManager._installDependencies(newPluginPath);
2082
+ } catch (e) {
2083
+ console.warn(`[Import] Не удалось установить зависимости для плагина ${pluginName}: ${e.message}`);
2084
+ }
2085
+
2086
+ let newPlugin;
2087
+ try {
2088
+ newPlugin = await pluginManager.registerPlugin(newBot.id, newPluginPath, 'LOCAL', newPluginPath);
2089
+ } catch (e) {
2090
+ newPlugin = await prisma.installedPlugin.create({ data: pluginData });
2091
+ }
1968
2092
  pluginMap.set(oldPluginId, newPlugin.id);
1969
2093
  }
1970
2094
  }