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.
- package/CHANGELOG.md +144 -107
- 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 +292 -68
- package/backend/src/api/routes/eventGraphs.js +459 -459
- package/backend/src/api/routes/servers.js +27 -0
- 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-BqqUSU9S.js → index-5m_JZxJ-.js} +1693 -1688
- package/frontend/dist/assets/index-BFd7YoAj.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- 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.
|
|
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
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
|
489
|
-
|
|
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
|
-
|
|
492
|
-
|
|
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
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
|
|
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
|
}
|