blockmine 1.22.0 → 1.23.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 (102) hide show
  1. package/.claude/settings.json +5 -1
  2. package/.claude/settings.local.json +10 -1
  3. package/CHANGELOG.md +27 -3
  4. package/CLAUDE.md +284 -0
  5. package/README.md +302 -152
  6. package/backend/package-lock.json +681 -9
  7. package/backend/package.json +8 -0
  8. package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
  9. package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
  10. package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
  11. package/backend/prisma/migrations/migration_lock.toml +2 -2
  12. package/backend/prisma/schema.prisma +70 -1
  13. package/backend/src/__tests__/services/BotLifecycleService.test.js +9 -4
  14. package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
  15. package/backend/src/api/middleware/auth.js +27 -0
  16. package/backend/src/api/middleware/botAccess.js +7 -3
  17. package/backend/src/api/middleware/panelApiAuth.js +135 -0
  18. package/backend/src/api/routes/aiAssistant.js +995 -0
  19. package/backend/src/api/routes/auth.js +669 -633
  20. package/backend/src/api/routes/botCommands.js +107 -0
  21. package/backend/src/api/routes/botGroups.js +165 -0
  22. package/backend/src/api/routes/botHistory.js +108 -0
  23. package/backend/src/api/routes/botPermissions.js +99 -0
  24. package/backend/src/api/routes/botStatus.js +36 -0
  25. package/backend/src/api/routes/botUsers.js +162 -0
  26. package/backend/src/api/routes/bots.js +2451 -2402
  27. package/backend/src/api/routes/eventGraphs.js +4 -1
  28. package/backend/src/api/routes/logs.js +13 -3
  29. package/backend/src/api/routes/panel.js +66 -66
  30. package/backend/src/api/routes/panelApiKeys.js +179 -0
  31. package/backend/src/api/routes/pluginIde.js +1715 -135
  32. package/backend/src/api/routes/plugins.js +376 -219
  33. package/backend/src/api/routes/proxies.js +130 -0
  34. package/backend/src/api/routes/search.js +4 -0
  35. package/backend/src/api/routes/servers.js +20 -3
  36. package/backend/src/api/routes/settings.js +5 -0
  37. package/backend/src/api/routes/system.js +174 -174
  38. package/backend/src/api/routes/traces.js +131 -0
  39. package/backend/src/config/debug.config.js +36 -0
  40. package/backend/src/core/BotHistoryStore.js +180 -0
  41. package/backend/src/core/BotManager.js +14 -4
  42. package/backend/src/core/BotProcess.js +1517 -1092
  43. package/backend/src/core/EventGraphManager.js +37 -123
  44. package/backend/src/core/GraphExecutionEngine.js +977 -321
  45. package/backend/src/core/MessageQueue.js +12 -6
  46. package/backend/src/core/PluginLoader.js +99 -5
  47. package/backend/src/core/PluginManager.js +74 -13
  48. package/backend/src/core/TaskScheduler.js +1 -1
  49. package/backend/src/core/commands/whois.js +1 -1
  50. package/backend/src/core/node-registries/actions.js +70 -0
  51. package/backend/src/core/node-registries/arrays.js +18 -0
  52. package/backend/src/core/node-registries/data.js +1 -1
  53. package/backend/src/core/node-registries/events.js +14 -0
  54. package/backend/src/core/node-registries/logic.js +17 -0
  55. package/backend/src/core/node-registries/strings.js +34 -0
  56. package/backend/src/core/node-registries/type.js +25 -0
  57. package/backend/src/core/nodes/actions/bot_look_at.js +1 -1
  58. package/backend/src/core/nodes/actions/create_command.js +189 -0
  59. package/backend/src/core/nodes/actions/delete_command.js +92 -0
  60. package/backend/src/core/nodes/actions/update_command.js +133 -0
  61. package/backend/src/core/nodes/arrays/join.js +28 -0
  62. package/backend/src/core/nodes/data/cast.js +2 -1
  63. package/backend/src/core/nodes/logic/not.js +22 -0
  64. package/backend/src/core/nodes/strings/starts_with.js +1 -1
  65. package/backend/src/core/nodes/strings/to_lower.js +22 -0
  66. package/backend/src/core/nodes/strings/to_upper.js +22 -0
  67. package/backend/src/core/nodes/type/to_string.js +32 -0
  68. package/backend/src/core/services/BotLifecycleService.js +255 -16
  69. package/backend/src/core/services/CommandExecutionService.js +430 -351
  70. package/backend/src/core/services/DebugSessionManager.js +347 -0
  71. package/backend/src/core/services/GraphCollaborationManager.js +501 -0
  72. package/backend/src/core/services/MinecraftBotManager.js +259 -0
  73. package/backend/src/core/services/MinecraftViewerService.js +216 -0
  74. package/backend/src/core/services/TraceCollectorService.js +545 -0
  75. package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
  76. package/backend/src/core/system/Transport.js +0 -4
  77. package/backend/src/core/validation/nodeSchemas.js +6 -6
  78. package/backend/src/real-time/botApi/handlers/graphHandlers.js +2 -2
  79. package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +1 -1
  80. package/backend/src/real-time/botApi/utils.js +11 -0
  81. package/backend/src/real-time/panelNamespace.js +387 -0
  82. package/backend/src/real-time/presence.js +7 -2
  83. package/backend/src/real-time/socketHandler.js +395 -4
  84. package/backend/src/server.js +18 -0
  85. package/frontend/dist/assets/index-B1serztM.js +11210 -0
  86. package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
  87. package/frontend/dist/index.html +2 -2
  88. package/frontend/package-lock.json +9437 -0
  89. package/frontend/package.json +8 -0
  90. package/package.json +2 -2
  91. package/screen/console.png +0 -0
  92. package/screen/dashboard.png +0 -0
  93. package/screen/graph_collabe.png +0 -0
  94. package/screen/graph_live_debug.png +0 -0
  95. package/screen/management_command.png +0 -0
  96. package/screen/node_debug_trace.png +0 -0
  97. package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
  98. package/screen/websocket.png +0 -0
  99. 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
  100. 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
  101. package/frontend/dist/assets/index-CfTo92bP.css +0 -1
  102. package/frontend/dist/assets/index-CiFD5X9Z.js +0 -8344
@@ -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;
@@ -0,0 +1,36 @@
1
+ const express = require('express');
2
+ const { authenticateUniversal, authorize } = require('../middleware/auth');
3
+ const { botManager } = require('../../core/services');
4
+
5
+ const router = express.Router();
6
+
7
+ router.get('/:id/status', authenticateUniversal, authorize('bot:list'), async (req, res) => {
8
+ try {
9
+ const botId = parseInt(req.params.id, 10);
10
+
11
+ const botProcess = botManager.bots.get(botId);
12
+
13
+ if (!botProcess) {
14
+ return res.json({
15
+ online: false,
16
+ connected: false,
17
+ status: 'offline'
18
+ });
19
+ }
20
+
21
+ const status = botProcess.status || 'unknown';
22
+ const isOnline = status === 'running' || status === 'online';
23
+ const isConnected = botProcess.bot?.isConnected?.() || false;
24
+
25
+ res.json({
26
+ online: isOnline,
27
+ connected: isConnected,
28
+ status: status
29
+ });
30
+ } catch (error) {
31
+ console.error('[API Error] GET /bots/:id/status:', error);
32
+ res.status(500).json({ error: 'Не удалось получить статус бота' });
33
+ }
34
+ });
35
+
36
+ module.exports = router;
@@ -0,0 +1,162 @@
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/users/:username', authenticateUniversal, authorize('management:view'), async (req, res) => {
10
+ console.log('[botUsers] GET /:botId/users/:username called', req.params);
11
+ try {
12
+ const botId = parseInt(req.params.botId, 10);
13
+ const { username } = req.params;
14
+
15
+ const user = await prisma.user.findFirst({
16
+ where: { botId, username },
17
+ include: { groups: { include: { group: true } } }
18
+ });
19
+
20
+ if (!user) {
21
+ return res.status(404).json({ error: 'Пользователь не найден' });
22
+ }
23
+
24
+ res.json(user);
25
+ } catch (error) {
26
+ console.error('[API Error] GET /bots/:botId/users/:username:', error);
27
+ res.status(500).json({ error: 'Не удалось получить пользователя' });
28
+ }
29
+ });
30
+
31
+ router.get('/:botId/users', authenticateUniversal, authorize('management:view'), async (req, res) => {
32
+ try {
33
+ const botId = parseInt(req.params.botId, 10);
34
+ const { page = 1, pageSize = 100, search } = req.query;
35
+
36
+ const skip = (parseInt(page) - 1) * parseInt(pageSize);
37
+ const take = parseInt(pageSize);
38
+
39
+ const where = { botId };
40
+ if (search) {
41
+ where.username = { contains: search };
42
+ }
43
+
44
+ const [users, total] = await Promise.all([
45
+ prisma.user.findMany({
46
+ where,
47
+ include: { groups: { include: { group: true } } },
48
+ orderBy: { username: 'asc' },
49
+ skip,
50
+ take
51
+ }),
52
+ prisma.user.count({ where })
53
+ ]);
54
+
55
+ res.json({
56
+ items: users,
57
+ total,
58
+ page: parseInt(page),
59
+ pageSize: take
60
+ });
61
+ } catch (error) {
62
+ console.error('[API Error] GET /bots/:botId/users:', error);
63
+ res.status(500).json({ error: 'Не удалось получить список пользователей' });
64
+ }
65
+ });
66
+
67
+ router.put('/:botId/users/:username', authenticateUniversal, authorize('management:edit'), async (req, res) => {
68
+ try {
69
+ const botId = parseInt(req.params.botId, 10);
70
+ const { username } = req.params;
71
+ const { isBlacklisted, groupIds } = req.body;
72
+
73
+ let user = await prisma.user.findFirst({
74
+ where: { botId, username }
75
+ });
76
+
77
+ if (!user) {
78
+ user = await prisma.user.create({
79
+ data: {
80
+ botId,
81
+ username,
82
+ isBlacklisted: isBlacklisted || false
83
+ }
84
+ });
85
+ }
86
+
87
+ const updateData = {};
88
+ if (typeof isBlacklisted === 'boolean') {
89
+ updateData.isBlacklisted = isBlacklisted;
90
+ }
91
+
92
+ if (Array.isArray(groupIds)) {
93
+ await prisma.userGroup.deleteMany({ where: { userId: user.id } });
94
+ updateData.groups = {
95
+ create: groupIds.map(gid => ({ groupId: gid }))
96
+ };
97
+ }
98
+
99
+ const updatedUser = await prisma.user.update({
100
+ where: { id: user.id },
101
+ data: updateData,
102
+ include: { groups: { include: { group: true } } }
103
+ });
104
+
105
+ botManager.invalidateUserCache(botId, username);
106
+ UserService.clearCache(username, botId);
107
+
108
+ res.json(updatedUser);
109
+ } catch (error) {
110
+ console.error('[API Error] PUT /bots/:botId/users/:username:', error);
111
+ res.status(500).json({ error: 'Не удалось обновить пользователя' });
112
+ }
113
+ });
114
+
115
+ router.delete('/:botId/users/:username', authenticateUniversal, authorize('management:edit'), async (req, res) => {
116
+ try {
117
+ const botId = parseInt(req.params.botId, 10);
118
+ const { username } = req.params;
119
+
120
+ const user = await prisma.user.findFirst({
121
+ where: { botId, username }
122
+ });
123
+
124
+ if (!user) {
125
+ return res.status(404).json({ error: 'Пользователь не найден' });
126
+ }
127
+
128
+ await prisma.userGroup.deleteMany({ where: { userId: user.id } });
129
+ await prisma.user.delete({ where: { id: user.id } });
130
+
131
+ botManager.invalidateUserCache(botId, username);
132
+ UserService.clearCache(username, botId);
133
+
134
+ res.json({ success: true });
135
+ } catch (error) {
136
+ console.error('[API Error] DELETE /bots/:botId/users/:username:', error);
137
+ res.status(500).json({ error: 'Не удалось удалить пользователя' });
138
+ }
139
+ });
140
+
141
+ router.get('/:botId/users/:username/groups', authenticateUniversal, authorize('management:view'), async (req, res) => {
142
+ try {
143
+ const botId = parseInt(req.params.botId, 10);
144
+ const { username } = req.params;
145
+
146
+ const user = await prisma.user.findFirst({
147
+ where: { botId, username },
148
+ include: { groups: true }
149
+ });
150
+
151
+ if (!user) {
152
+ return res.status(404).json({ error: 'Пользователь не найден' });
153
+ }
154
+
155
+ res.json({ groupIds: user.groups.map(g => g.groupId) });
156
+ } catch (error) {
157
+ console.error('[API Error] GET /bots/:botId/users/:username/groups:', error);
158
+ res.status(500).json({ error: 'Не удалось получить группы пользователя' });
159
+ }
160
+ });
161
+
162
+ module.exports = router;