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.
- package/.claude/settings.json +5 -1
- package/.claude/settings.local.json +10 -1
- package/CHANGELOG.md +27 -3
- package/CLAUDE.md +284 -0
- 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/migrations/migration_lock.toml +2 -2
- 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 +669 -633
- 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 +2451 -2402
- 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 +66 -66
- 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 +376 -219
- 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 +174 -174
- 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 +37 -123
- package/backend/src/core/GraphExecutionEngine.js +977 -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 +70 -0
- 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/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/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 +255 -16
- 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-B1serztM.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
|
@@ -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;
|