blockmine 1.22.0 → 1.23.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/.claude/agents/code-architect.md +34 -0
- package/.claude/agents/code-explorer.md +51 -0
- package/.claude/agents/code-reviewer.md +46 -0
- package/.claude/commands/feature-dev.md +125 -0
- package/.claude/settings.json +5 -1
- package/.claude/settings.local.json +12 -1
- package/.claude/skills/frontend-design/SKILL.md +42 -0
- package/CHANGELOG.md +32 -1
- package/README.md +302 -152
- package/backend/package-lock.json +681 -9
- package/backend/package.json +8 -0
- package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
- package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
- package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
- package/backend/prisma/schema.prisma +70 -1
- package/backend/src/__tests__/services/BotLifecycleService.test.js +9 -4
- package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
- package/backend/src/api/middleware/auth.js +27 -0
- package/backend/src/api/middleware/botAccess.js +7 -3
- package/backend/src/api/middleware/panelApiAuth.js +135 -0
- package/backend/src/api/routes/aiAssistant.js +995 -0
- package/backend/src/api/routes/auth.js +90 -54
- package/backend/src/api/routes/botCommands.js +107 -0
- package/backend/src/api/routes/botGroups.js +165 -0
- package/backend/src/api/routes/botHistory.js +108 -0
- package/backend/src/api/routes/botPermissions.js +99 -0
- package/backend/src/api/routes/botStatus.js +36 -0
- package/backend/src/api/routes/botUsers.js +162 -0
- package/backend/src/api/routes/bots.js +108 -59
- package/backend/src/api/routes/eventGraphs.js +4 -1
- package/backend/src/api/routes/logs.js +13 -3
- package/backend/src/api/routes/panel.js +3 -3
- package/backend/src/api/routes/panelApiKeys.js +179 -0
- package/backend/src/api/routes/pluginIde.js +1715 -135
- package/backend/src/api/routes/plugins.js +170 -13
- package/backend/src/api/routes/proxies.js +130 -0
- package/backend/src/api/routes/search.js +4 -0
- package/backend/src/api/routes/servers.js +20 -3
- package/backend/src/api/routes/settings.js +5 -0
- package/backend/src/api/routes/system.js +3 -3
- package/backend/src/api/routes/traces.js +131 -0
- package/backend/src/config/debug.config.js +36 -0
- package/backend/src/core/BotHistoryStore.js +180 -0
- package/backend/src/core/BotManager.js +14 -4
- package/backend/src/core/BotProcess.js +1517 -1092
- package/backend/src/core/EventGraphManager.js +194 -280
- package/backend/src/core/GraphExecutionEngine.js +1004 -321
- package/backend/src/core/MessageQueue.js +12 -6
- package/backend/src/core/PluginLoader.js +99 -5
- package/backend/src/core/PluginManager.js +74 -13
- package/backend/src/core/TaskScheduler.js +1 -1
- package/backend/src/core/commands/whois.js +1 -1
- package/backend/src/core/node-registries/actions.js +72 -2
- package/backend/src/core/node-registries/arrays.js +18 -0
- package/backend/src/core/node-registries/data.js +1 -1
- package/backend/src/core/node-registries/events.js +14 -0
- package/backend/src/core/node-registries/logic.js +17 -0
- package/backend/src/core/node-registries/strings.js +34 -0
- package/backend/src/core/node-registries/type.js +25 -0
- package/backend/src/core/nodes/actions/bot_look_at.js +1 -1
- package/backend/src/core/nodes/actions/create_command.js +189 -0
- package/backend/src/core/nodes/actions/delete_command.js +92 -0
- package/backend/src/core/nodes/actions/http_request.js +23 -4
- package/backend/src/core/nodes/actions/send_message.js +2 -12
- package/backend/src/core/nodes/actions/update_command.js +133 -0
- package/backend/src/core/nodes/arrays/join.js +28 -0
- package/backend/src/core/nodes/data/cast.js +2 -1
- package/backend/src/core/nodes/data/string_literal.js +2 -13
- package/backend/src/core/nodes/logic/not.js +22 -0
- package/backend/src/core/nodes/strings/starts_with.js +1 -1
- package/backend/src/core/nodes/strings/to_lower.js +22 -0
- package/backend/src/core/nodes/strings/to_upper.js +22 -0
- package/backend/src/core/nodes/type/to_string.js +32 -0
- package/backend/src/core/services/BotLifecycleService.js +835 -596
- package/backend/src/core/services/CommandExecutionService.js +430 -351
- package/backend/src/core/services/DebugSessionManager.js +347 -0
- package/backend/src/core/services/GraphCollaborationManager.js +501 -0
- package/backend/src/core/services/MinecraftBotManager.js +259 -0
- package/backend/src/core/services/MinecraftViewerService.js +216 -0
- package/backend/src/core/services/TraceCollectorService.js +545 -0
- package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
- package/backend/src/core/system/Transport.js +0 -4
- package/backend/src/core/validation/nodeSchemas.js +6 -6
- package/backend/src/real-time/botApi/handlers/graphHandlers.js +2 -2
- package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +1 -1
- package/backend/src/real-time/botApi/utils.js +11 -0
- package/backend/src/real-time/panelNamespace.js +387 -0
- package/backend/src/real-time/presence.js +7 -2
- package/backend/src/real-time/socketHandler.js +395 -4
- package/backend/src/server.js +18 -0
- package/frontend/dist/assets/index-DqzDkFsP.js +11210 -0
- package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package-lock.json +9437 -0
- package/frontend/package.json +8 -0
- package/package.json +2 -2
- package/screen/console.png +0 -0
- package/screen/dashboard.png +0 -0
- package/screen/graph_collabe.png +0 -0
- package/screen/graph_live_debug.png +0 -0
- package/screen/management_command.png +0 -0
- package/screen/node_debug_trace.png +0 -0
- package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
- package/screen/websocket.png +0 -0
- package/screen//320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270_/320/276/321/202/320/264/320/265/320/273/321/214/320/275/321/213/321/205_/320/272/320/276/320/274/320/260/320/275/320/264_/320/272/320/260/320/266/320/264/321/203_/320/272/320/276/320/274/320/260/320/275/320/273/320/264/321/203_/320/274/320/276/320/266/320/275/320/276_/320/275/320/260/321/201/321/202/321/200/320/260/320/270/320/262/320/260/321/202/321/214.png +0 -0
- package/screen//320/277/320/273/320/260/320/275/320/270/321/200/320/276/320/262/321/211/320/270/320/272_/320/274/320/276/320/266/320/275/320/276_/320/267/320/260/320/264/320/260/320/262/320/260/321/202/321/214_/320/264/320/265/320/271/321/201/321/202/320/262/320/270/321/217_/320/277/320/276_/320/262/321/200/320/265/320/274/320/265/320/275/320/270.png +0 -0
- package/frontend/dist/assets/index-CfTo92bP.css +0 -1
- package/frontend/dist/assets/index-CiFD5X9Z.js +0 -8344
|
@@ -5,6 +5,7 @@ const crypto = require('crypto');
|
|
|
5
5
|
const { PrismaClient } = require('@prisma/client');
|
|
6
6
|
const config = require('../../config');
|
|
7
7
|
const { authenticate, authorize } = require('../middleware/auth');
|
|
8
|
+
const { authenticatePanelApiKey } = require('../middleware/panelApiAuth');
|
|
8
9
|
const path = require('path');
|
|
9
10
|
const os = require('os');
|
|
10
11
|
|
|
@@ -16,6 +17,66 @@ const JWT_EXPIRES_IN = '7d';
|
|
|
16
17
|
|
|
17
18
|
const activeResetTokens = new Map();
|
|
18
19
|
|
|
20
|
+
const ALL_PERMISSIONS = [
|
|
21
|
+
{ id: '*', label: 'Все права (Администратор)' },
|
|
22
|
+
{ id: 'bot:list', label: 'Просмотр ботов' },
|
|
23
|
+
{ id: 'bot:create', label: 'Создание ботов' },
|
|
24
|
+
{ id: 'bot:update', label: 'Редактирование ботов' },
|
|
25
|
+
{ id: 'bot:delete', label: 'Удаление ботов' },
|
|
26
|
+
{ id: 'bot:start_stop', label: 'Запуск/остановка ботов' },
|
|
27
|
+
{ id: 'bot:interact', label: 'Взаимодействие с ботом (консоль)' },
|
|
28
|
+
{ id: 'bot:export', label: 'Экспорт ботов' },
|
|
29
|
+
{ id: 'bot:import', label: 'Импорт ботов' },
|
|
30
|
+
{ id: 'bot:history:view', label: 'Просмотр истории чата и команд' },
|
|
31
|
+
{ id: 'bot:history:manage', label: 'Управление историей (очистка)' },
|
|
32
|
+
{ id: 'management:view', label: 'Просмотр вкладки "Управление" у бота' },
|
|
33
|
+
{ id: 'management:edit', label: 'Редактирование на вкладке "Управление" у бота' },
|
|
34
|
+
{ id: 'plugin:list', label: 'Просмотр плагинов' },
|
|
35
|
+
{ id: 'plugin:install', label: 'Установка плагинов' },
|
|
36
|
+
{ id: 'plugin:delete', label: 'Удаление плагинов' },
|
|
37
|
+
{ id: 'plugin:update', label: 'Обновление плагинов' },
|
|
38
|
+
{ id: 'plugin:settings:view', label: 'Просмотр настроек плагинов' },
|
|
39
|
+
{ id: 'plugin:settings:edit', label: 'Редактирование настроек плагинов' },
|
|
40
|
+
{ id: 'plugin:browse', label: 'Просмотр каталога плагинов' },
|
|
41
|
+
{ id: 'plugin:develop', label: 'Разработка и редактирование плагинов (IDE)' },
|
|
42
|
+
{ id: 'server:list', label: 'Просмотр серверов' },
|
|
43
|
+
{ id: 'server:create', label: 'Создание серверов' },
|
|
44
|
+
{ id: 'server:delete', label: 'Удаление серверов' },
|
|
45
|
+
{ id: 'proxy:list', label: 'Просмотр прокси' },
|
|
46
|
+
{ id: 'proxy:create', label: 'Создание и редактирование прокси' },
|
|
47
|
+
{ id: 'proxy:delete', label: 'Удаление прокси' },
|
|
48
|
+
{ id: 'task:list', label: 'Просмотр задач' },
|
|
49
|
+
{ id: 'task:create', label: 'Создание задач' },
|
|
50
|
+
{ id: 'task:edit', label: 'Редактирование задач' },
|
|
51
|
+
{ id: 'task:delete', label: 'Удаление задач' },
|
|
52
|
+
{ id: 'panel:user:list', label: 'Просмотр пользователей панели' },
|
|
53
|
+
{ id: 'panel:user:create', label: 'Создание пользователей панели' },
|
|
54
|
+
{ id: 'panel:user:edit', label: 'Редактирование пользователей панели' },
|
|
55
|
+
{ id: 'panel:user:delete', label: 'Удаление пользователей панели' },
|
|
56
|
+
{ id: 'panel:role:list', label: 'Просмотр ролей панели' },
|
|
57
|
+
{ id: 'panel:role:create', label: 'Создание ролей панели' },
|
|
58
|
+
{ id: 'panel:role:edit', label: 'Редактирование ролей панели' },
|
|
59
|
+
{ id: 'panel:role:delete', label: 'Удаление ролей панели' },
|
|
60
|
+
{ id: 'panel:settings:view', label: 'Просмотр глобальных настроек' },
|
|
61
|
+
{ id: 'panel:settings:edit', label: 'Редактирование глобальных настроек' },
|
|
62
|
+
{ id: 'graph:read', label: 'Просмотр магазина графов' },
|
|
63
|
+
{ id: 'graph:download', label: 'Скачивание графов из магазина' },
|
|
64
|
+
{ id: 'graph:like', label: 'Лайки графов в магазине' },
|
|
65
|
+
{ id: 'graph:publish', label: 'Публикация графов в магазин' },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const VIEWER_PERMISSIONS = [
|
|
69
|
+
'bot:list',
|
|
70
|
+
'bot:history:view',
|
|
71
|
+
'plugin:list',
|
|
72
|
+
'plugin:settings:view',
|
|
73
|
+
'management:view',
|
|
74
|
+
'server:list',
|
|
75
|
+
'proxy:list',
|
|
76
|
+
'task:list',
|
|
77
|
+
'graph:read',
|
|
78
|
+
];
|
|
79
|
+
|
|
19
80
|
function ownerOnly(req, res, next) {
|
|
20
81
|
if (req.user && req.user.userId === 1) return next();
|
|
21
82
|
return res.status(403).json({ error: 'Только владелец может изменять права пользователей и роли.' });
|
|
@@ -271,7 +332,12 @@ router.post('/login', async (req, res) => {
|
|
|
271
332
|
return res.status(401).json({ error: 'Неверные учетные данные' });
|
|
272
333
|
}
|
|
273
334
|
|
|
274
|
-
|
|
335
|
+
let permissions = JSON.parse(user.role.permissions || '[]');
|
|
336
|
+
|
|
337
|
+
// Владелец (первый пользователь) всегда получает все актуальные права
|
|
338
|
+
if (user.id === 1) {
|
|
339
|
+
permissions = ALL_PERMISSIONS.map(p => p.id).filter(id => id !== '*');
|
|
340
|
+
}
|
|
275
341
|
|
|
276
342
|
const payload = {
|
|
277
343
|
userId: user.id,
|
|
@@ -297,6 +363,28 @@ router.post('/login', async (req, res) => {
|
|
|
297
363
|
});
|
|
298
364
|
|
|
299
365
|
|
|
366
|
+
/**
|
|
367
|
+
* @route GET /api/auth/validate
|
|
368
|
+
* @desc Validate Panel API key
|
|
369
|
+
* @access Public (requires API key)
|
|
370
|
+
*/
|
|
371
|
+
router.get('/validate', authenticatePanelApiKey, async (req, res) => {
|
|
372
|
+
res.json({
|
|
373
|
+
valid: true,
|
|
374
|
+
user: {
|
|
375
|
+
id: req.user.id,
|
|
376
|
+
username: req.user.username,
|
|
377
|
+
role: req.user.roleName,
|
|
378
|
+
permissions: req.user.permissions
|
|
379
|
+
},
|
|
380
|
+
apiKey: {
|
|
381
|
+
id: req.apiKey.id,
|
|
382
|
+
name: req.apiKey.name,
|
|
383
|
+
prefix: req.apiKey.prefix
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
300
388
|
/**
|
|
301
389
|
* @route GET /api/auth/me
|
|
302
390
|
* @desc Получить данные текущего пользователя по токену
|
|
@@ -312,7 +400,7 @@ router.get('/me', authenticate, async (req, res) => {
|
|
|
312
400
|
if (!user) {
|
|
313
401
|
return res.status(404).json({ error: "Пользователь не найден." });
|
|
314
402
|
}
|
|
315
|
-
|
|
403
|
+
|
|
316
404
|
res.json({
|
|
317
405
|
id: user.id,
|
|
318
406
|
username: user.username,
|
|
@@ -401,58 +489,6 @@ router.get('/roles', authenticate, authorize('panel:role:list'), async (req, res
|
|
|
401
489
|
}
|
|
402
490
|
});
|
|
403
491
|
|
|
404
|
-
const ALL_PERMISSIONS = [
|
|
405
|
-
{ id: '*', label: 'Все права (Администратор)' },
|
|
406
|
-
{ id: 'bot:list', label: 'Просмотр ботов' },
|
|
407
|
-
{ id: 'bot:create', label: 'Создание ботов' },
|
|
408
|
-
{ id: 'bot:update', label: 'Редактирование ботов' },
|
|
409
|
-
{ id: 'bot:delete', label: 'Удаление ботов' },
|
|
410
|
-
{ id: 'bot:start_stop', label: 'Запуск/остановка ботов' },
|
|
411
|
-
{ id: 'bot:interact', label: 'Взаимодействие с ботом (консоль)' },
|
|
412
|
-
{ id: 'bot:export', label: 'Экспорт ботов' },
|
|
413
|
-
{ id: 'bot:import', label: 'Импорт ботов' },
|
|
414
|
-
{ id: 'management:view', label: 'Просмотр вкладки "Управление" у бота' },
|
|
415
|
-
{ id: 'management:edit', label: 'Редактирование на вкладке "Управление" у бота' },
|
|
416
|
-
{ id: 'plugin:list', label: 'Просмотр плагинов' },
|
|
417
|
-
{ id: 'plugin:install', label: 'Установка плагинов' },
|
|
418
|
-
{ id: 'plugin:delete', label: 'Удаление плагинов' },
|
|
419
|
-
{ id: 'plugin:update', label: 'Обновление плагинов' },
|
|
420
|
-
{ id: 'plugin:settings:view', label: 'Просмотр настроек плагинов' },
|
|
421
|
-
{ id: 'plugin:settings:edit', label: 'Редактирование настроек плагинов' },
|
|
422
|
-
{ id: 'plugin:browse', label: 'Просмотр каталога плагинов' },
|
|
423
|
-
{ id: 'plugin:develop', label: 'Разработка и редактирование плагинов (IDE)' },
|
|
424
|
-
{ id: 'server:list', label: 'Просмотр серверов' },
|
|
425
|
-
{ id: 'server:create', label: 'Создание серверов' },
|
|
426
|
-
{ id: 'server:delete', label: 'Удаление серверов' },
|
|
427
|
-
{ id: 'task:list', label: 'Просмотр задач' },
|
|
428
|
-
{ id: 'task:create', label: 'Создание задач' },
|
|
429
|
-
{ id: 'task:edit', label: 'Редактирование задач' },
|
|
430
|
-
{ id: 'task:delete', label: 'Удаление задач' },
|
|
431
|
-
{ id: 'panel:user:list', label: 'Просмотр пользователей панели' },
|
|
432
|
-
{ id: 'panel:user:create', label: 'Создание пользователей панели' },
|
|
433
|
-
{ id: 'panel:user:edit', label: 'Редактирование пользователей панели' },
|
|
434
|
-
{ id: 'panel:user:delete', label: 'Удаление пользователей панели' },
|
|
435
|
-
{ id: 'panel:role:list', label: 'Просмотр ролей панели' },
|
|
436
|
-
{ id: 'panel:role:create', label: 'Создание ролей панели' },
|
|
437
|
-
{ id: 'panel:role:edit', label: 'Редактирование ролей панели' },
|
|
438
|
-
{ id: 'panel:role:delete', label: 'Удаление ролей панели' },
|
|
439
|
-
{ id: 'panel:settings:view', label: 'Просмотр глобальных настроек' },
|
|
440
|
-
{ id: 'panel:settings:edit', label: 'Редактирование глобальных настроек' },
|
|
441
|
-
{ id: 'graph:read', label: 'Просмотр магазина графов' },
|
|
442
|
-
{ id: 'graph:download', label: 'Скачивание графов из магазина' },
|
|
443
|
-
{ id: 'graph:like', label: 'Лайки графов в магазине' },
|
|
444
|
-
{ id: 'graph:publish', label: 'Публикация графов в магазин' },
|
|
445
|
-
];
|
|
446
|
-
|
|
447
|
-
const VIEWER_PERMISSIONS = [
|
|
448
|
-
'bot:list',
|
|
449
|
-
'plugin:list',
|
|
450
|
-
'plugin:settings:view',
|
|
451
|
-
'management:view',
|
|
452
|
-
'server:list',
|
|
453
|
-
'task:list',
|
|
454
|
-
'graph:read',
|
|
455
|
-
];
|
|
456
492
|
|
|
457
493
|
/**
|
|
458
494
|
* @route GET /api/auth/permissions
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const prisma = require('../../lib/prisma');
|
|
3
|
+
const { authenticateUniversal, authorize } = require('../middleware/auth');
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
router.get('/:botId/commands', authenticateUniversal, authorize('management:view'), async (req, res) => {
|
|
8
|
+
try {
|
|
9
|
+
const botId = parseInt(req.params.botId, 10);
|
|
10
|
+
const { page = 1, pageSize = 100, search } = req.query;
|
|
11
|
+
|
|
12
|
+
const skip = (parseInt(page) - 1) * parseInt(pageSize);
|
|
13
|
+
const take = parseInt(pageSize);
|
|
14
|
+
|
|
15
|
+
const where = { botId };
|
|
16
|
+
if (search) {
|
|
17
|
+
where.name = { contains: search };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const [commands, total] = await Promise.all([
|
|
21
|
+
prisma.command.findMany({
|
|
22
|
+
where,
|
|
23
|
+
include: {
|
|
24
|
+
permission: true,
|
|
25
|
+
pluginOwner: { select: { id: true, name: true, version: true } }
|
|
26
|
+
},
|
|
27
|
+
orderBy: { name: 'asc' },
|
|
28
|
+
skip,
|
|
29
|
+
take
|
|
30
|
+
}),
|
|
31
|
+
prisma.command.count({ where })
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
res.json({
|
|
35
|
+
items: commands,
|
|
36
|
+
total,
|
|
37
|
+
page: parseInt(page),
|
|
38
|
+
pageSize: take
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('[API Error] GET /bots/:botId/commands:', error);
|
|
42
|
+
res.status(500).json({ error: 'Не удалось получить список команд' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
router.get('/:botId/commands/:commandId', authenticateUniversal, authorize('management:view'), async (req, res) => {
|
|
47
|
+
try {
|
|
48
|
+
const botId = parseInt(req.params.botId, 10);
|
|
49
|
+
const commandId = parseInt(req.params.commandId, 10);
|
|
50
|
+
|
|
51
|
+
const command = await prisma.command.findFirst({
|
|
52
|
+
where: { id: commandId, botId },
|
|
53
|
+
include: {
|
|
54
|
+
permission: true,
|
|
55
|
+
pluginOwner: { select: { id: true, name: true, version: true } }
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!command) {
|
|
60
|
+
return res.status(404).json({ error: 'Команда не найдена' });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
res.json(command);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('[API Error] GET /bots/:botId/commands/:commandId:', error);
|
|
66
|
+
res.status(500).json({ error: 'Не удалось получить команду' });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
router.patch('/:botId/commands/:commandId', authenticateUniversal, authorize('management:edit'), async (req, res) => {
|
|
71
|
+
try {
|
|
72
|
+
const botId = parseInt(req.params.botId, 10);
|
|
73
|
+
const commandId = parseInt(req.params.commandId, 10);
|
|
74
|
+
const { isEnabled, cooldown, aliases, permissionId, allowedChatTypes } = req.body;
|
|
75
|
+
|
|
76
|
+
const command = await prisma.command.findFirst({
|
|
77
|
+
where: { id: commandId, botId }
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!command) {
|
|
81
|
+
return res.status(404).json({ error: 'Команда не найдена' });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const updateData = {};
|
|
85
|
+
if (typeof isEnabled === 'boolean') updateData.isEnabled = isEnabled;
|
|
86
|
+
if (typeof cooldown === 'number') updateData.cooldown = cooldown;
|
|
87
|
+
if (aliases !== undefined) updateData.aliases = JSON.stringify(aliases);
|
|
88
|
+
if (permissionId !== undefined) updateData.permissionId = permissionId;
|
|
89
|
+
if (allowedChatTypes !== undefined) updateData.allowedChatTypes = JSON.stringify(allowedChatTypes);
|
|
90
|
+
|
|
91
|
+
const updated = await prisma.command.update({
|
|
92
|
+
where: { id: commandId },
|
|
93
|
+
data: updateData,
|
|
94
|
+
include: {
|
|
95
|
+
permission: true,
|
|
96
|
+
pluginOwner: { select: { id: true, name: true, version: true } }
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
res.json(updated);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error('[API Error] PATCH /bots/:botId/commands/:commandId:', error);
|
|
103
|
+
res.status(500).json({ error: 'Не удалось обновить команду' });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
module.exports = router;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const prisma = require('../../lib/prisma');
|
|
3
|
+
const { authenticateUniversal, authorize } = require('../middleware/auth');
|
|
4
|
+
const { botManager } = require('../../core/services');
|
|
5
|
+
const UserService = require('../../core/UserService');
|
|
6
|
+
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
|
|
9
|
+
router.get('/:botId/groups', authenticateUniversal, authorize('management:view'), async (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const botId = parseInt(req.params.botId, 10);
|
|
12
|
+
|
|
13
|
+
const groups = await prisma.group.findMany({
|
|
14
|
+
where: { botId },
|
|
15
|
+
include: { permissions: { include: { permission: true } } },
|
|
16
|
+
orderBy: { name: 'asc' }
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
res.json({
|
|
20
|
+
items: groups,
|
|
21
|
+
total: groups.length
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error('[API Error] GET /bots/:botId/groups:', error);
|
|
25
|
+
res.status(500).json({ error: 'Не удалось получить список групп' });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
router.get('/:botId/groups/:groupId', authenticateUniversal, authorize('management:view'), async (req, res) => {
|
|
30
|
+
try {
|
|
31
|
+
const botId = parseInt(req.params.botId, 10);
|
|
32
|
+
const groupId = parseInt(req.params.groupId, 10);
|
|
33
|
+
|
|
34
|
+
const group = await prisma.group.findFirst({
|
|
35
|
+
where: { id: groupId, botId },
|
|
36
|
+
include: { permissions: { include: { permission: true } } }
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!group) {
|
|
40
|
+
return res.status(404).json({ error: 'Группа не найдена' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
res.json(group);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('[API Error] GET /bots/:botId/groups/:groupId:', error);
|
|
46
|
+
res.status(500).json({ error: 'Не удалось получить группу' });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
router.post('/:botId/groups/:groupId/permissions', authenticateUniversal, authorize('management:edit'), async (req, res) => {
|
|
51
|
+
try {
|
|
52
|
+
const botId = parseInt(req.params.botId, 10);
|
|
53
|
+
const groupId = parseInt(req.params.groupId, 10);
|
|
54
|
+
const { permissionId } = req.body;
|
|
55
|
+
|
|
56
|
+
const group = await prisma.group.findFirst({ where: { id: groupId, botId } });
|
|
57
|
+
if (!group) {
|
|
58
|
+
return res.status(404).json({ error: 'Группа не найдена' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const permission = await prisma.permission.findFirst({ where: { id: permissionId, botId } });
|
|
62
|
+
if (!permission) {
|
|
63
|
+
return res.status(404).json({ error: 'Право не найдено' });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await prisma.groupPermission.upsert({
|
|
67
|
+
where: { groupId_permissionId: { groupId, permissionId } },
|
|
68
|
+
create: { groupId, permissionId },
|
|
69
|
+
update: {}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
res.json({ success: true });
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('[API Error] POST /bots/:botId/groups/:groupId/permissions:', error);
|
|
75
|
+
res.status(500).json({ error: 'Не удалось добавить право в группу' });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
router.delete('/:botId/groups/:groupId/permissions/:permissionId', authenticateUniversal, authorize('management:edit'), async (req, res) => {
|
|
80
|
+
try {
|
|
81
|
+
const botId = parseInt(req.params.botId, 10);
|
|
82
|
+
const groupId = parseInt(req.params.groupId, 10);
|
|
83
|
+
const permissionId = parseInt(req.params.permissionId, 10);
|
|
84
|
+
|
|
85
|
+
const group = await prisma.group.findFirst({ where: { id: groupId, botId } });
|
|
86
|
+
if (!group) {
|
|
87
|
+
return res.status(404).json({ error: 'Группа не найдена' });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await prisma.groupPermission.deleteMany({
|
|
91
|
+
where: { groupId, permissionId }
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
res.json({ success: true });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error('[API Error] DELETE /bots/:botId/groups/:groupId/permissions/:permissionId:', error);
|
|
97
|
+
res.status(500).json({ error: 'Не удалось удалить право из группы' });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
router.post('/:botId/groups/:groupId/users', authenticateUniversal, authorize('management:edit'), async (req, res) => {
|
|
102
|
+
try {
|
|
103
|
+
const botId = parseInt(req.params.botId, 10);
|
|
104
|
+
const groupId = parseInt(req.params.groupId, 10);
|
|
105
|
+
const { username } = req.body;
|
|
106
|
+
|
|
107
|
+
const group = await prisma.group.findFirst({ where: { id: groupId, botId } });
|
|
108
|
+
if (!group) {
|
|
109
|
+
return res.status(404).json({ error: 'Группа не найдена' });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let user = await prisma.user.findFirst({ where: { botId, username } });
|
|
113
|
+
if (!user) {
|
|
114
|
+
user = await prisma.user.create({
|
|
115
|
+
data: { botId, username, isBlacklisted: false }
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await prisma.userGroup.upsert({
|
|
120
|
+
where: { userId_groupId: { userId: user.id, groupId } },
|
|
121
|
+
create: { userId: user.id, groupId },
|
|
122
|
+
update: {}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
botManager.invalidateUserCache(botId, username);
|
|
126
|
+
UserService.clearCache(username, botId);
|
|
127
|
+
|
|
128
|
+
res.json({ success: true });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('[API Error] POST /bots/:botId/groups/:groupId/users:', error);
|
|
131
|
+
res.status(500).json({ error: 'Не удалось добавить пользователя в группу' });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
router.delete('/:botId/groups/:groupId/users/:username', authenticateUniversal, authorize('management:edit'), async (req, res) => {
|
|
136
|
+
try {
|
|
137
|
+
const botId = parseInt(req.params.botId, 10);
|
|
138
|
+
const groupId = parseInt(req.params.groupId, 10);
|
|
139
|
+
const { username } = req.params;
|
|
140
|
+
|
|
141
|
+
const group = await prisma.group.findFirst({ where: { id: groupId, botId } });
|
|
142
|
+
if (!group) {
|
|
143
|
+
return res.status(404).json({ error: 'Группа не найдена' });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const user = await prisma.user.findFirst({ where: { botId, username } });
|
|
147
|
+
if (!user) {
|
|
148
|
+
return res.status(404).json({ error: 'Пользователь не найден' });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
await prisma.userGroup.deleteMany({
|
|
152
|
+
where: { userId: user.id, groupId }
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
botManager.invalidateUserCache(botId, username);
|
|
156
|
+
UserService.clearCache(username, botId);
|
|
157
|
+
|
|
158
|
+
res.json({ success: true });
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error('[API Error] DELETE /bots/:botId/groups/:groupId/users/:username:', error);
|
|
161
|
+
res.status(500).json({ error: 'Не удалось удалить пользователя из группы' });
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
module.exports = router;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const { authenticateUniversal, authorize } = require('../middleware/auth');
|
|
3
|
+
const botHistoryStore = require('../../core/BotHistoryStore');
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
router.get('/:botId/chat', authenticateUniversal, authorize('bot:list'), async (req, res) => {
|
|
8
|
+
try {
|
|
9
|
+
const botId = parseInt(req.params.botId);
|
|
10
|
+
const { type, username, search, from, to, limit, offset } = req.query;
|
|
11
|
+
|
|
12
|
+
const result = botHistoryStore.getChatHistory(botId, {
|
|
13
|
+
type,
|
|
14
|
+
username,
|
|
15
|
+
search,
|
|
16
|
+
from,
|
|
17
|
+
to,
|
|
18
|
+
limit: limit || 100,
|
|
19
|
+
offset: offset || 0
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
res.json({
|
|
23
|
+
messages: result.messages,
|
|
24
|
+
total: result.total,
|
|
25
|
+
pagination: {
|
|
26
|
+
total: result.total,
|
|
27
|
+
limit: parseInt(limit) || 100,
|
|
28
|
+
offset: parseInt(offset) || 0,
|
|
29
|
+
hasMore: (parseInt(offset) || 0) + (parseInt(limit) || 100) < result.total
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('[API Error] GET /bots/:botId/chat:', error);
|
|
34
|
+
res.status(500).json({
|
|
35
|
+
success: false,
|
|
36
|
+
error: 'Не удалось получить историю чата.'
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
router.get('/:botId/commands', authenticateUniversal, authorize('bot:list'), async (req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
const botId = parseInt(req.params.botId);
|
|
44
|
+
const { username, command, success, from, to, limit, offset } = req.query;
|
|
45
|
+
|
|
46
|
+
const result = botHistoryStore.getCommandLogs(botId, {
|
|
47
|
+
username,
|
|
48
|
+
command,
|
|
49
|
+
success,
|
|
50
|
+
from,
|
|
51
|
+
to,
|
|
52
|
+
limit: limit || 100,
|
|
53
|
+
offset: offset || 0
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
res.json({
|
|
57
|
+
logs: result.logs,
|
|
58
|
+
total: result.total,
|
|
59
|
+
pagination: {
|
|
60
|
+
total: result.total,
|
|
61
|
+
limit: parseInt(limit) || 100,
|
|
62
|
+
offset: parseInt(offset) || 0,
|
|
63
|
+
hasMore: (parseInt(offset) || 0) + (parseInt(limit) || 100) < result.total
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('[API Error] GET /bots/:botId/commands:', error);
|
|
68
|
+
res.status(500).json({
|
|
69
|
+
success: false,
|
|
70
|
+
error: 'Не удалось получить логи команд.'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
router.get('/:botId/stats', authenticateUniversal, authorize('bot:list'), async (req, res) => {
|
|
76
|
+
try {
|
|
77
|
+
const botId = parseInt(req.params.botId);
|
|
78
|
+
const stats = botHistoryStore.getStats(botId);
|
|
79
|
+
|
|
80
|
+
res.json(stats);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('[API Error] GET /bots/:botId/stats:', error);
|
|
83
|
+
res.status(500).json({
|
|
84
|
+
success: false,
|
|
85
|
+
error: 'Не удалось получить статистику.'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
router.delete('/:botId/clear', authenticateUniversal, authorize('bot:list'), async (req, res) => {
|
|
91
|
+
try {
|
|
92
|
+
const botId = parseInt(req.params.botId);
|
|
93
|
+
botHistoryStore.clearBot(botId);
|
|
94
|
+
|
|
95
|
+
res.json({
|
|
96
|
+
success: true,
|
|
97
|
+
message: 'История бота очищена.'
|
|
98
|
+
});
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('[API Error] DELETE /bots/:botId/clear:', error);
|
|
101
|
+
res.status(500).json({
|
|
102
|
+
success: false,
|
|
103
|
+
error: 'Не удалось очистить историю.'
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
module.exports = router;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const prisma = require('../../lib/prisma');
|
|
3
|
+
const { authenticateUniversal, authorize } = require('../middleware/auth');
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
router.get('/:botId/permissions', authenticateUniversal, authorize('management:view'), async (req, res) => {
|
|
8
|
+
try {
|
|
9
|
+
const botId = parseInt(req.params.botId, 10);
|
|
10
|
+
|
|
11
|
+
const permissions = await prisma.permission.findMany({
|
|
12
|
+
where: { botId },
|
|
13
|
+
orderBy: { name: 'asc' }
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
res.json({
|
|
17
|
+
items: permissions,
|
|
18
|
+
total: permissions.length
|
|
19
|
+
});
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error('[API Error] GET /bots/:botId/permissions:', error);
|
|
22
|
+
res.status(500).json({ error: 'Не удалось получить список прав' });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
router.get('/:botId/permissions/:permissionId', authenticateUniversal, authorize('management:view'), async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const botId = parseInt(req.params.botId, 10);
|
|
29
|
+
const permissionId = parseInt(req.params.permissionId, 10);
|
|
30
|
+
|
|
31
|
+
const permission = await prisma.permission.findFirst({
|
|
32
|
+
where: { id: permissionId, botId }
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!permission) {
|
|
36
|
+
return res.status(404).json({ error: 'Право не найдено' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
res.json(permission);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('[API Error] GET /bots/:botId/permissions/:permissionId:', error);
|
|
42
|
+
res.status(500).json({ error: 'Не удалось получить право' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
router.put('/:botId/permissions/:permissionId', authenticateUniversal, authorize('management:edit'), async (req, res) => {
|
|
47
|
+
try {
|
|
48
|
+
const botId = parseInt(req.params.botId, 10);
|
|
49
|
+
const permissionId = parseInt(req.params.permissionId, 10);
|
|
50
|
+
const { name, description } = req.body;
|
|
51
|
+
|
|
52
|
+
const permission = await prisma.permission.findFirst({
|
|
53
|
+
where: { id: permissionId, botId }
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!permission) {
|
|
57
|
+
return res.status(404).json({ error: 'Право не найдено' });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const updateData = {};
|
|
61
|
+
if (name !== undefined) updateData.name = name;
|
|
62
|
+
if (description !== undefined) updateData.description = description;
|
|
63
|
+
|
|
64
|
+
const updated = await prisma.permission.update({
|
|
65
|
+
where: { id: permissionId },
|
|
66
|
+
data: updateData
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
res.json(updated);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('[API Error] PUT /bots/:botId/permissions/:permissionId:', error);
|
|
72
|
+
res.status(500).json({ error: 'Не удалось обновить право' });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
router.delete('/:botId/permissions/:permissionId', authenticateUniversal, authorize('management:edit'), async (req, res) => {
|
|
77
|
+
try {
|
|
78
|
+
const botId = parseInt(req.params.botId, 10);
|
|
79
|
+
const permissionId = parseInt(req.params.permissionId, 10);
|
|
80
|
+
|
|
81
|
+
const permission = await prisma.permission.findFirst({
|
|
82
|
+
where: { id: permissionId, botId }
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!permission) {
|
|
86
|
+
return res.status(404).json({ error: 'Право не найдено' });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await prisma.groupPermission.deleteMany({ where: { permissionId } });
|
|
90
|
+
await prisma.permission.delete({ where: { id: permissionId } });
|
|
91
|
+
|
|
92
|
+
res.json({ success: true });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('[API Error] DELETE /bots/:botId/permissions/:permissionId:', error);
|
|
95
|
+
res.status(500).json({ error: 'Не удалось удалить право' });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
module.exports = router;
|