blockmine 1.18.3 → 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 (28) hide show
  1. package/CHANGELOG.md +144 -107
  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 +292 -68
  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-BqqUSU9S.js → index-5m_JZxJ-.js} +1693 -1688
  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-THQP1_d3.css +0 -1
@@ -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,65 +599,152 @@ 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) => {
603
+ try {
604
+ const pluginId = parseInt(req.params.pluginId);
605
+ const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
606
+ if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
607
+
608
+ const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
609
+ const defaultSettings = {};
610
+ const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
611
+ const manifestSettings = manifest.settings || {};
612
+
613
+
614
+ const firstSettingValue = Object.values(manifestSettings)[0];
615
+ const isGrouped = firstSettingValue && typeof firstSettingValue === 'object' && !firstSettingValue.type && firstSettingValue.label;
616
+
617
+ const processSetting = async (settingKey, config) => {
618
+ if (!config || !config.type) return;
619
+
620
+ if (config.type === 'json_file' && config.defaultPath) {
621
+ const configFilePath = path.join(plugin.path, config.defaultPath);
622
+ try {
623
+ const fileContent = await fs.readFile(configFilePath, 'utf-8');
624
+ defaultSettings[settingKey] = JSON.parse(fileContent);
625
+ } catch (e) {
626
+ console.error(`[API Settings] Не удалось прочитать defaultPath ${config.defaultPath} для плагина ${plugin.name}: ${e.message}`);
627
+ defaultSettings[settingKey] = {};
628
+ }
629
+ } else if (config.default !== undefined) {
630
+ try {
631
+ defaultSettings[settingKey] = JSON.parse(config.default);
632
+ } catch {
633
+ defaultSettings[settingKey] = config.default;
634
+ }
635
+ }
636
+ };
637
+
638
+ if (isGrouped) {
639
+ for (const categoryKey in manifestSettings) {
640
+ const categoryConfig = manifestSettings[categoryKey];
641
+ for (const settingKey in categoryConfig) {
642
+ if (settingKey === 'label') continue;
643
+ await processSetting(settingKey, categoryConfig[settingKey]);
644
+ }
645
+ }
646
+ } else {
647
+ for (const settingKey in manifestSettings) {
648
+ await processSetting(settingKey, manifestSettings[settingKey]);
649
+ }
650
+ }
651
+
652
+ const finalSettings = deepMergeSettings(defaultSettings, savedSettings);
653
+ res.json(finalSettings);
654
+ } catch (error) {
655
+ console.error("[API Error] /settings GET:", error);
656
+ res.status(500).json({ error: 'Не удалось получить настройки плагина' });
657
+ }
658
+ });
659
+
660
+ router.get('/:botId/plugins/:pluginId/data', authenticate, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
477
661
  try {
478
662
  const pluginId = parseInt(req.params.pluginId);
479
663
  const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
480
664
  if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
481
665
 
482
- const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
483
- const defaultSettings = {};
484
- const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
485
- const manifestSettings = manifest.settings || {};
486
-
666
+ const rows = await prisma.pluginDataStore.findMany({
667
+ where: { botId: plugin.botId, pluginName: plugin.name },
668
+ orderBy: { updatedAt: 'desc' }
669
+ });
487
670
 
488
- const firstSettingValue = Object.values(manifestSettings)[0];
489
- const isGrouped = firstSettingValue && typeof firstSettingValue === 'object' && !firstSettingValue.type && firstSettingValue.label;
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: 'Не удалось получить данные плагина' }); }
678
+ });
490
679
 
491
- const processSetting = async (settingKey, config) => {
492
- if (!config || !config.type) return;
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: 'Установленный плагин не найден' });
493
686
 
494
- if (config.type === 'json_file' && config.defaultPath) {
495
- const configFilePath = path.join(plugin.path, config.defaultPath);
496
- try {
497
- const fileContent = await fs.readFile(configFilePath, 'utf-8');
498
- defaultSettings[settingKey] = JSON.parse(fileContent);
499
- } catch (e) {
500
- console.error(`[API Settings] Не удалось прочитать defaultPath ${config.defaultPath} для плагина ${plugin.name}: ${e.message}`);
501
- defaultSettings[settingKey] = {};
502
- }
503
- } else if (config.default !== undefined) {
504
- try {
505
- defaultSettings[settingKey] = JSON.parse(config.default);
506
- } catch {
507
- defaultSettings[settingKey] = config.default;
687
+ const row = await prisma.pluginDataStore.findUnique({
688
+ where: {
689
+ pluginName_botId_key: {
690
+ pluginName: plugin.name,
691
+ botId: plugin.botId,
692
+ key
508
693
  }
509
694
  }
510
- };
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: 'Не удалось получить значение по ключу' }); }
700
+ });
701
+
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: 'Установленный плагин не найден' });
511
709
 
512
- if (isGrouped) {
513
- for (const categoryKey in manifestSettings) {
514
- const categoryConfig = manifestSettings[categoryKey];
515
- for (const settingKey in categoryConfig) {
516
- if (settingKey === 'label') continue;
517
- await processSetting(settingKey, categoryConfig[settingKey]);
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
518
717
  }
519
- }
520
- } else {
521
- for (const settingKey in manifestSettings) {
522
- await processSetting(settingKey, manifestSettings[settingKey]);
523
- }
524
- }
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
+ });
525
726
 
526
- const finalSettings = deepMergeSettings(defaultSettings, savedSettings);
527
- res.json(finalSettings);
528
- } catch (error) {
529
- console.error("[API Error] /settings GET:", error);
530
- res.status(500).json({ error: 'Не удалось получить настройки плагина' });
531
- }
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: 'Не удалось удалить значение' }); }
532
745
  });
533
746
 
534
- router.put('/:botId/plugins/:pluginId', authorize('plugin:settings:edit'), async (req, res) => {
747
+ router.put('/:botId/plugins/:pluginId', authenticate, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
535
748
  try {
536
749
  const pluginId = parseInt(req.params.pluginId);
537
750
  const { isEnabled, settings } = req.body;
@@ -544,7 +757,7 @@ router.put('/:botId/plugins/:pluginId', authorize('plugin:settings:edit'), async
544
757
  } catch (error) { res.status(500).json({ error: 'Не удалось обновить плагин' }); }
545
758
  });
546
759
 
547
- 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) => {
548
761
  try {
549
762
  const botId = parseInt(req.params.botId, 10);
550
763
  if (isNaN(botId)) return res.status(400).json({ error: 'Неверный ID бота' });
@@ -907,7 +1120,7 @@ router.post('/stop-all', authorize('bot:start_stop'), (req, res) => {
907
1120
  }
908
1121
  });
909
1122
 
910
- 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) => {
911
1124
  try {
912
1125
  const botId = parseInt(req.params.id, 10);
913
1126
 
@@ -986,7 +1199,7 @@ router.get('/:id/settings/all', authorize('bot:update'), async (req, res) => {
986
1199
 
987
1200
  const nodeRegistry = require('../../core/NodeRegistry');
988
1201
 
989
- 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) => {
990
1203
  try {
991
1204
  const { graphType } = req.query;
992
1205
  const nodesByCategory = nodeRegistry.getNodesByCategory(graphType);
@@ -997,7 +1210,7 @@ router.get('/:botId/visual-editor/nodes', authorize('management:view'), (req, re
997
1210
  }
998
1211
  });
999
1212
 
1000
- 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) => {
1001
1214
  try {
1002
1215
  const { types } = req.query;
1003
1216
  if (!types) {
@@ -1012,7 +1225,7 @@ router.get('/:botId/visual-editor/node-config', authorize('management:view'), (r
1012
1225
  }
1013
1226
  });
1014
1227
 
1015
- 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) => {
1016
1229
  try {
1017
1230
  const botId = parseInt(req.params.botId, 10);
1018
1231
  const permissions = await prisma.permission.findMany({
@@ -1633,7 +1846,7 @@ router.post('/:botId/plugins/:pluginName/action', authorize('plugin:list'), asyn
1633
1846
  });
1634
1847
 
1635
1848
 
1636
- router.get('/:botId/export', authorize('bot:export'), async (req, res) => {
1849
+ router.get('/:botId/export', authenticate, checkBotAccess, authorize('bot:export'), async (req, res) => {
1637
1850
  try {
1638
1851
  const botId = parseInt(req.params.botId, 10);
1639
1852
  const {
@@ -1864,7 +2077,18 @@ router.post('/import', authorize('bot:create'), upload.single('file'), async (re
1864
2077
  }
1865
2078
  }
1866
2079
 
1867
- 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
+ }
1868
2092
  pluginMap.set(oldPluginId, newPlugin.id);
1869
2093
  }
1870
2094
  }