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
|
@@ -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;
|
|
@@ -7,7 +7,7 @@ const { botManager, pluginManager } = require('../../core/services');
|
|
|
7
7
|
const UserService = require('../../core/UserService');
|
|
8
8
|
const commandManager = require('../../core/system/CommandManager');
|
|
9
9
|
const NodeRegistry = require('../../core/NodeRegistry');
|
|
10
|
-
const { authenticate, authorize } = require('../middleware/auth');
|
|
10
|
+
const { authenticate, authenticateUniversal, authorize } = require('../middleware/auth');
|
|
11
11
|
const { encrypt } = require('../../core/utils/crypto');
|
|
12
12
|
const { randomUUID } = require('crypto');
|
|
13
13
|
const eventGraphsRouter = require('./eventGraphs');
|
|
@@ -26,15 +26,13 @@ const upload = multer({ storage: multer.memoryStorage() });
|
|
|
26
26
|
|
|
27
27
|
const router = express.Router();
|
|
28
28
|
|
|
29
|
-
router.use('/:botId(\\d+)/*', authenticate, (req, res, next) => checkBotAccess(req, res, next));
|
|
30
|
-
|
|
31
29
|
const conditionalRestartAuth = (req, res, next) => {
|
|
32
30
|
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
|
33
31
|
console.log('[Debug] Роут перезапуска бота доступен без проверки прав');
|
|
34
32
|
return next();
|
|
35
33
|
}
|
|
36
|
-
|
|
37
|
-
return
|
|
34
|
+
|
|
35
|
+
return authenticateUniversal(req, res, (err) => {
|
|
38
36
|
if (err) return next(err);
|
|
39
37
|
return authorize('bot:start_stop')(req, res, next);
|
|
40
38
|
});
|
|
@@ -45,8 +43,8 @@ const conditionalChatAuth = (req, res, next) => {
|
|
|
45
43
|
console.log('[Debug] Роут отправки сообщения боту доступен без проверки прав');
|
|
46
44
|
return next();
|
|
47
45
|
}
|
|
48
|
-
|
|
49
|
-
return
|
|
46
|
+
|
|
47
|
+
return authenticateUniversal(req, res, (err) => {
|
|
50
48
|
if (err) return next(err);
|
|
51
49
|
return authorize('bot:interact')(req, res, next);
|
|
52
50
|
});
|
|
@@ -57,29 +55,33 @@ const conditionalStartStopAuth = (req, res, next) => {
|
|
|
57
55
|
console.log('[Debug] Роут запуска/остановки бота доступен без проверки прав');
|
|
58
56
|
return next();
|
|
59
57
|
}
|
|
60
|
-
|
|
61
|
-
return
|
|
58
|
+
|
|
59
|
+
return authenticateUniversal(req, res, (err) => {
|
|
62
60
|
if (err) return next(err);
|
|
63
61
|
return authorize('bot:start_stop')(req, res, next);
|
|
64
62
|
});
|
|
65
63
|
};
|
|
66
64
|
|
|
67
65
|
const conditionalListAuth = (req, res, next) => {
|
|
68
|
-
|
|
66
|
+
console.log('[conditionalListAuth] START, env:', process.env.NODE_ENV, 'debug:', process.env.DEBUG);
|
|
67
|
+
return authenticateUniversal(req, res, (err) => {
|
|
68
|
+
console.log('[conditionalListAuth] After auth, err:', err, 'user:', req.user?.username);
|
|
69
69
|
if (err) return next(err);
|
|
70
70
|
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
|
71
|
+
console.log('[conditionalListAuth] DEBUG mode, skipping authorize');
|
|
71
72
|
return next();
|
|
72
73
|
}
|
|
74
|
+
console.log('[conditionalListAuth] Calling authorize(bot:list)');
|
|
73
75
|
return authorize('bot:list')(req, res, next);
|
|
74
76
|
});
|
|
75
77
|
};
|
|
76
78
|
|
|
77
|
-
router.post('/:id/restart', conditionalRestartAuth,
|
|
79
|
+
router.post('/:id/restart', conditionalRestartAuth, authenticateUniversal, checkBotAccess, async (req, res) => {
|
|
78
80
|
try {
|
|
79
81
|
const botId = parseInt(req.params.id, 10);
|
|
80
82
|
botManager.stopBot(botId);
|
|
81
83
|
setTimeout(async () => {
|
|
82
|
-
const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
|
|
84
|
+
const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true, proxy: true } });
|
|
83
85
|
if (!botConfig) {
|
|
84
86
|
return res.status(404).json({ success: false, message: 'Бот не найден' });
|
|
85
87
|
}
|
|
@@ -92,7 +94,7 @@ router.post('/:id/restart', conditionalRestartAuth, authenticate, checkBotAccess
|
|
|
92
94
|
}
|
|
93
95
|
});
|
|
94
96
|
|
|
95
|
-
router.post('/:id/chat', conditionalChatAuth,
|
|
97
|
+
router.post('/:id/chat', conditionalChatAuth, authenticateUniversal, checkBotAccess, (req, res) => {
|
|
96
98
|
try {
|
|
97
99
|
const botId = parseInt(req.params.id, 10);
|
|
98
100
|
const { message } = req.body;
|
|
@@ -103,10 +105,10 @@ router.post('/:id/chat', conditionalChatAuth, authenticate, checkBotAccess, (req
|
|
|
103
105
|
} catch (error) { res.status(500).json({ error: 'Внутренняя ошибка сервера: ' + error.message }); }
|
|
104
106
|
});
|
|
105
107
|
|
|
106
|
-
router.post('/:id/start', conditionalStartStopAuth,
|
|
108
|
+
router.post('/:id/start', conditionalStartStopAuth, authenticateUniversal, checkBotAccess, async (req, res) => {
|
|
107
109
|
try {
|
|
108
110
|
const botId = parseInt(req.params.id, 10);
|
|
109
|
-
const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
|
|
111
|
+
const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true, proxy: true } });
|
|
110
112
|
if (!botConfig) {
|
|
111
113
|
return res.status(404).json({ success: false, message: 'Бот не найден' });
|
|
112
114
|
}
|
|
@@ -118,7 +120,7 @@ router.post('/:id/start', conditionalStartStopAuth, authenticate, checkBotAccess
|
|
|
118
120
|
}
|
|
119
121
|
});
|
|
120
122
|
|
|
121
|
-
router.post('/:id/stop', conditionalStartStopAuth,
|
|
123
|
+
router.post('/:id/stop', conditionalStartStopAuth, authenticateUniversal, checkBotAccess, (req, res) => {
|
|
122
124
|
try {
|
|
123
125
|
const botId = parseInt(req.params.id, 10);
|
|
124
126
|
botManager.stopBot(botId);
|
|
@@ -129,7 +131,15 @@ router.post('/:id/stop', conditionalStartStopAuth, authenticate, checkBotAccess,
|
|
|
129
131
|
}
|
|
130
132
|
});
|
|
131
133
|
|
|
134
|
+
router.get('/state', conditionalListAuth, (req, res) => {
|
|
135
|
+
try {
|
|
136
|
+
const state = botManager.getFullState();
|
|
137
|
+
res.json(state);
|
|
138
|
+
} catch (error) { res.status(500).json({ error: 'Не удалось получить состояние ботов' }); }
|
|
139
|
+
});
|
|
140
|
+
|
|
132
141
|
router.get('/', conditionalListAuth, async (req, res) => {
|
|
142
|
+
console.log('[API /api/bots GET] Запрос получен, user:', req.user?.username, 'permissions:', req.user?.permissions?.slice(0, 3));
|
|
133
143
|
try {
|
|
134
144
|
const botsWithoutSortOrder = await prisma.bot.findMany({
|
|
135
145
|
where: { sortOrder: null },
|
|
@@ -167,26 +177,40 @@ router.get('/', conditionalListAuth, async (req, res) => {
|
|
|
167
177
|
}
|
|
168
178
|
}
|
|
169
179
|
|
|
170
|
-
const bots = await prisma.bot.findMany({
|
|
180
|
+
const bots = await prisma.bot.findMany({
|
|
171
181
|
where: whereFilter,
|
|
172
|
-
include: { server: true },
|
|
173
|
-
orderBy: { sortOrder: 'asc' }
|
|
182
|
+
include: { server: true },
|
|
183
|
+
orderBy: { sortOrder: 'asc' }
|
|
174
184
|
});
|
|
185
|
+
console.log(`[API /api/bots GET] Отправляем ${bots.length} ботов, status: 200`);
|
|
175
186
|
res.json(bots);
|
|
176
|
-
} catch (error) {
|
|
187
|
+
} catch (error) {
|
|
177
188
|
console.error("[API /api/bots] Ошибка получения списка ботов:", error);
|
|
178
|
-
res.status(500).json({ error: 'Не удалось получить список ботов' });
|
|
189
|
+
res.status(500).json({ error: 'Не удалось получить список ботов' });
|
|
179
190
|
}
|
|
180
191
|
});
|
|
181
192
|
|
|
182
|
-
router.get('
|
|
193
|
+
router.get('/:id', conditionalListAuth, async (req, res) => {
|
|
183
194
|
try {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
195
|
+
const botId = parseInt(req.params.id, 10);
|
|
196
|
+
|
|
197
|
+
const bot = await prisma.bot.findUnique({
|
|
198
|
+
where: { id: botId },
|
|
199
|
+
include: { server: true, proxy: true }
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
if (!bot) {
|
|
203
|
+
return res.status(404).json({ error: 'Бот не найден' });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
res.json(bot);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error(`[API /api/bots/:id] Ошибка получения бота:`, error);
|
|
209
|
+
res.status(500).json({ error: 'Не удалось получить бота' });
|
|
210
|
+
}
|
|
187
211
|
});
|
|
188
212
|
|
|
189
|
-
router.put('/bulk-proxy-update',
|
|
213
|
+
router.put('/bulk-proxy-update', authenticateUniversal, authorize('bot:update'), async (req, res) => {
|
|
190
214
|
try {
|
|
191
215
|
const { botIds, proxySettings } = req.body;
|
|
192
216
|
|
|
@@ -298,7 +322,7 @@ router.put('/bulk-proxy-update', authenticate, authorize('bot:update'), async (r
|
|
|
298
322
|
}
|
|
299
323
|
});
|
|
300
324
|
|
|
301
|
-
router.get('/:id/logs', conditionalListAuth,
|
|
325
|
+
router.get('/:id/logs', conditionalListAuth, authenticateUniversal, checkBotAccess, (req, res) => {
|
|
302
326
|
try {
|
|
303
327
|
const botId = parseInt(req.params.id, 10);
|
|
304
328
|
const { limit = 50, offset = 0 } = req.query;
|
|
@@ -372,26 +396,31 @@ async function setupDefaultPermissionsForBot(botId, prismaClient = prisma) {
|
|
|
372
396
|
|
|
373
397
|
router.post('/', authorize('bot:create'), async (req, res) => {
|
|
374
398
|
try {
|
|
375
|
-
const { username, password, prefix, serverId, note } = req.body;
|
|
399
|
+
const { username, password, prefix, serverId, note, proxyId, proxyHost, proxyPort, proxyUsername, proxyPassword } = req.body;
|
|
376
400
|
if (!username || !serverId) return res.status(400).json({ error: 'Имя и сервер обязательны' });
|
|
377
|
-
|
|
401
|
+
|
|
378
402
|
const maxSortOrder = await prisma.bot.aggregate({
|
|
379
403
|
_max: { sortOrder: true }
|
|
380
404
|
});
|
|
381
405
|
const nextSortOrder = (maxSortOrder._max.sortOrder || 0) + 1;
|
|
382
|
-
|
|
383
|
-
const data = {
|
|
384
|
-
username,
|
|
385
|
-
prefix,
|
|
386
|
-
note,
|
|
406
|
+
|
|
407
|
+
const data = {
|
|
408
|
+
username,
|
|
409
|
+
prefix,
|
|
410
|
+
note,
|
|
387
411
|
serverId: parseInt(serverId, 10),
|
|
388
412
|
password: password ? encrypt(password) : null,
|
|
413
|
+
proxyId: proxyId ? parseInt(proxyId, 10) : null,
|
|
414
|
+
proxyHost: proxyHost || null,
|
|
415
|
+
proxyPort: proxyPort ? parseInt(proxyPort, 10) : null,
|
|
416
|
+
proxyUsername: proxyUsername || null,
|
|
417
|
+
proxyPassword: proxyPassword ? encrypt(proxyPassword) : null,
|
|
389
418
|
sortOrder: nextSortOrder
|
|
390
419
|
};
|
|
391
420
|
|
|
392
421
|
const newBot = await prisma.bot.create({
|
|
393
422
|
data: data,
|
|
394
|
-
include: { server: true }
|
|
423
|
+
include: { server: true, proxy: true }
|
|
395
424
|
});
|
|
396
425
|
await setupDefaultPermissionsForBot(newBot.id);
|
|
397
426
|
res.status(201).json(newBot);
|
|
@@ -402,11 +431,11 @@ router.post('/', authorize('bot:create'), async (req, res) => {
|
|
|
402
431
|
}
|
|
403
432
|
});
|
|
404
433
|
|
|
405
|
-
router.put('/:id',
|
|
434
|
+
router.put('/:id', authenticateUniversal, checkBotAccess, authorize('bot:update'), async (req, res) => {
|
|
406
435
|
try {
|
|
407
|
-
const {
|
|
436
|
+
const {
|
|
408
437
|
username, password, prefix, serverId, note, owners,
|
|
409
|
-
proxyHost, proxyPort, proxyUsername, proxyPassword
|
|
438
|
+
proxyId, proxyHost, proxyPort, proxyUsername, proxyPassword
|
|
410
439
|
} = req.body;
|
|
411
440
|
|
|
412
441
|
let dataToUpdate = {
|
|
@@ -414,6 +443,7 @@ router.put('/:id', authenticate, checkBotAccess, authorize('bot:update'), async
|
|
|
414
443
|
prefix,
|
|
415
444
|
note,
|
|
416
445
|
owners,
|
|
446
|
+
proxyId: proxyId ? parseInt(proxyId, 10) : null,
|
|
417
447
|
proxyHost,
|
|
418
448
|
proxyPort: proxyPort ? parseInt(proxyPort, 10) : null,
|
|
419
449
|
proxyUsername,
|
|
@@ -441,7 +471,17 @@ router.put('/:id', authenticate, checkBotAccess, authorize('bot:update'), async
|
|
|
441
471
|
delete dataToUpdate.serverId;
|
|
442
472
|
dataToUpdate.server = { connect: { id: serverIdValue } };
|
|
443
473
|
}
|
|
444
|
-
|
|
474
|
+
|
|
475
|
+
if (dataToUpdate.proxyId !== undefined) {
|
|
476
|
+
const proxyIdValue = dataToUpdate.proxyId;
|
|
477
|
+
delete dataToUpdate.proxyId;
|
|
478
|
+
if (proxyIdValue) {
|
|
479
|
+
dataToUpdate.proxy = { connect: { id: proxyIdValue } };
|
|
480
|
+
} else {
|
|
481
|
+
dataToUpdate.proxy = { disconnect: true };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
445
485
|
const botId = parseInt(req.params.id, 10);
|
|
446
486
|
if (isNaN(botId)) {
|
|
447
487
|
return res.status(400).json({ message: 'Неверный ID бота.' });
|
|
@@ -465,9 +505,16 @@ router.put('/:id', authenticate, checkBotAccess, authorize('bot:update'), async
|
|
|
465
505
|
const updatedBot = await prisma.bot.update({
|
|
466
506
|
where: { id: botId },
|
|
467
507
|
data: dataToUpdate,
|
|
468
|
-
include: { server: true }
|
|
508
|
+
include: { server: true, proxy: true }
|
|
469
509
|
});
|
|
470
510
|
|
|
511
|
+
botManager.reloadBotConfigInRealTime(botId);
|
|
512
|
+
|
|
513
|
+
// Отложенная очистка кеша пользователей, чтобы BotProcess успел обновить config
|
|
514
|
+
setTimeout(() => {
|
|
515
|
+
botManager.invalidateAllUserCache(botId);
|
|
516
|
+
}, 500);
|
|
517
|
+
|
|
471
518
|
res.json(updatedBot);
|
|
472
519
|
} catch (error) {
|
|
473
520
|
console.error("[API Error] /bots PUT:", error);
|
|
@@ -475,7 +522,7 @@ router.put('/:id', authenticate, checkBotAccess, authorize('bot:update'), async
|
|
|
475
522
|
}
|
|
476
523
|
});
|
|
477
524
|
|
|
478
|
-
router.put('/:id/sort-order',
|
|
525
|
+
router.put('/:id/sort-order', authenticateUniversal, checkBotAccess, authorize('bot:update'), async (req, res) => {
|
|
479
526
|
try {
|
|
480
527
|
const { newPosition, oldIndex, newIndex } = req.body;
|
|
481
528
|
const botId = parseInt(req.params.id, 10);
|
|
@@ -534,7 +581,7 @@ router.put('/:id/sort-order', authenticate, checkBotAccess, authorize('bot:updat
|
|
|
534
581
|
}
|
|
535
582
|
});
|
|
536
583
|
|
|
537
|
-
router.delete('/:id',
|
|
584
|
+
router.delete('/:id', authenticateUniversal, checkBotAccess, authorize('bot:delete'), async (req, res) => {
|
|
538
585
|
try {
|
|
539
586
|
const botId = parseInt(req.params.id, 10);
|
|
540
587
|
if (botManager.bots.has(botId)) return res.status(400).json({ error: 'Нельзя удалить запущенного бота' });
|
|
@@ -553,7 +600,7 @@ router.get('/servers', authorize('bot:list'), async (req, res) => {
|
|
|
553
600
|
}
|
|
554
601
|
});
|
|
555
602
|
|
|
556
|
-
router.get('/:botId/plugins',
|
|
603
|
+
router.get('/:botId/plugins', authenticateUniversal, checkBotAccess, authorize('plugin:list'), async (req, res) => {
|
|
557
604
|
try {
|
|
558
605
|
const botId = parseInt(req.params.botId);
|
|
559
606
|
const plugins = await prisma.installedPlugin.findMany({ where: { botId } });
|
|
@@ -561,7 +608,7 @@ router.get('/:botId/plugins', authenticate, checkBotAccess, authorize('plugin:li
|
|
|
561
608
|
} catch (error) { res.status(500).json({ error: 'Не удалось получить плагины бота' }); }
|
|
562
609
|
});
|
|
563
610
|
|
|
564
|
-
router.post('/:botId/plugins/install/github',
|
|
611
|
+
router.post('/:botId/plugins/install/github', authenticateUniversal, checkBotAccess, authorize('plugin:install'), async (req, res) => {
|
|
565
612
|
const { botId } = req.params;
|
|
566
613
|
const { repoUrl } = req.body;
|
|
567
614
|
try {
|
|
@@ -572,7 +619,7 @@ router.post('/:botId/plugins/install/github', authenticate, checkBotAccess, auth
|
|
|
572
619
|
}
|
|
573
620
|
});
|
|
574
621
|
|
|
575
|
-
router.post('/:botId/plugins/install/local',
|
|
622
|
+
router.post('/:botId/plugins/install/local', authenticateUniversal, checkBotAccess, authorize('plugin:install'), async (req, res) => {
|
|
576
623
|
const { botId } = req.params;
|
|
577
624
|
const { path } = req.body;
|
|
578
625
|
try {
|
|
@@ -583,7 +630,7 @@ router.post('/:botId/plugins/install/local', authenticate, checkBotAccess, autho
|
|
|
583
630
|
}
|
|
584
631
|
});
|
|
585
632
|
|
|
586
|
-
router.delete('/:botId/plugins/:pluginId',
|
|
633
|
+
router.delete('/:botId/plugins/:pluginId', authenticateUniversal, checkBotAccess, authorize('plugin:delete'), async (req, res) => {
|
|
587
634
|
const { pluginId } = req.params;
|
|
588
635
|
try {
|
|
589
636
|
await pluginManager.deletePlugin(parseInt(pluginId));
|
|
@@ -593,7 +640,7 @@ router.delete('/:botId/plugins/:pluginId', authenticate, checkBotAccess, authori
|
|
|
593
640
|
}
|
|
594
641
|
});
|
|
595
642
|
|
|
596
|
-
router.get('/:botId/plugins/:pluginId/settings',
|
|
643
|
+
router.get('/:botId/plugins/:pluginId/settings', authenticateUniversal, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
|
|
597
644
|
try {
|
|
598
645
|
const pluginId = parseInt(req.params.pluginId);
|
|
599
646
|
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
@@ -654,7 +701,7 @@ router.get('/:botId/plugins/:pluginId/settings', authenticate, checkBotAccess, a
|
|
|
654
701
|
}
|
|
655
702
|
});
|
|
656
703
|
|
|
657
|
-
router.get('/:botId/plugins/:pluginId/data',
|
|
704
|
+
router.get('/:botId/plugins/:pluginId/data', authenticateUniversal, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
|
|
658
705
|
try {
|
|
659
706
|
const pluginId = parseInt(req.params.pluginId);
|
|
660
707
|
const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
|
|
@@ -674,7 +721,7 @@ router.get('/:botId/plugins/:pluginId/data', authenticate, checkBotAccess, autho
|
|
|
674
721
|
} catch (error) { res.status(500).json({ error: 'Не удалось получить данные плагина' }); }
|
|
675
722
|
});
|
|
676
723
|
|
|
677
|
-
router.get('/:botId/plugins/:pluginId/data/:key',
|
|
724
|
+
router.get('/:botId/plugins/:pluginId/data/:key', authenticateUniversal, checkBotAccess, authorize('plugin:settings:view'), async (req, res) => {
|
|
678
725
|
try {
|
|
679
726
|
const pluginId = parseInt(req.params.pluginId);
|
|
680
727
|
const { key } = req.params;
|
|
@@ -696,7 +743,7 @@ router.get('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess,
|
|
|
696
743
|
} catch (error) { res.status(500).json({ error: 'Не удалось получить значение по ключу' }); }
|
|
697
744
|
});
|
|
698
745
|
|
|
699
|
-
router.put('/:botId/plugins/:pluginId/data/:key',
|
|
746
|
+
router.put('/:botId/plugins/:pluginId/data/:key', authenticateUniversal, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
|
|
700
747
|
try {
|
|
701
748
|
const pluginId = parseInt(req.params.pluginId);
|
|
702
749
|
const { key } = req.params;
|
|
@@ -721,7 +768,7 @@ router.put('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAccess,
|
|
|
721
768
|
} catch (error) { res.status(500).json({ error: 'Не удалось сохранить значение' }); }
|
|
722
769
|
});
|
|
723
770
|
|
|
724
|
-
router.delete('/:botId/plugins/:pluginId/data/:key',
|
|
771
|
+
router.delete('/:botId/plugins/:pluginId/data/:key', authenticateUniversal, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
|
|
725
772
|
try {
|
|
726
773
|
const pluginId = parseInt(req.params.pluginId);
|
|
727
774
|
const { key } = req.params;
|
|
@@ -741,7 +788,7 @@ router.delete('/:botId/plugins/:pluginId/data/:key', authenticate, checkBotAcces
|
|
|
741
788
|
} catch (error) { res.status(500).json({ error: 'Не удалось удалить значение' }); }
|
|
742
789
|
});
|
|
743
790
|
|
|
744
|
-
router.put('/:botId/plugins/:pluginId',
|
|
791
|
+
router.put('/:botId/plugins/:pluginId', authenticateUniversal, checkBotAccess, authorize('plugin:settings:edit'), async (req, res) => {
|
|
745
792
|
try {
|
|
746
793
|
const pluginId = parseInt(req.params.pluginId);
|
|
747
794
|
const { isEnabled, settings } = req.body;
|
|
@@ -782,7 +829,7 @@ router.put('/:botId/plugins/:pluginId', authenticate, checkBotAccess, authorize(
|
|
|
782
829
|
}
|
|
783
830
|
});
|
|
784
831
|
|
|
785
|
-
router.get('/:botId/management-data',
|
|
832
|
+
router.get('/:botId/management-data', authenticateUniversal, checkBotAccess, authorize('management:view'), async (req, res) => {
|
|
786
833
|
try {
|
|
787
834
|
const botId = parseInt(req.params.botId, 10);
|
|
788
835
|
if (isNaN(botId)) return res.status(400).json({ error: 'Неверный ID бота' });
|
|
@@ -1114,7 +1161,7 @@ router.put('/:botId/users/:userId', authorize('management:edit'), async (req, re
|
|
|
1114
1161
|
router.post('/start-all', authorize('bot:start_stop'), async (req, res) => {
|
|
1115
1162
|
try {
|
|
1116
1163
|
console.log('[API] Получен запрос на запуск всех ботов.');
|
|
1117
|
-
const allBots = await prisma.bot.findMany({ include: { server: true } });
|
|
1164
|
+
const allBots = await prisma.bot.findMany({ include: { server: true, proxy: true } });
|
|
1118
1165
|
let startedCount = 0;
|
|
1119
1166
|
for (const botConfig of allBots) {
|
|
1120
1167
|
if (!botManager.bots.has(botConfig.id)) {
|
|
@@ -1145,7 +1192,7 @@ router.post('/stop-all', authorize('bot:start_stop'), (req, res) => {
|
|
|
1145
1192
|
}
|
|
1146
1193
|
});
|
|
1147
1194
|
|
|
1148
|
-
router.get('/:id/settings/all',
|
|
1195
|
+
router.get('/:id/settings/all', authenticateUniversal, checkBotAccess, authorize('bot:update'), async (req, res) => {
|
|
1149
1196
|
try {
|
|
1150
1197
|
const botId = parseInt(req.params.id, 10);
|
|
1151
1198
|
|
|
@@ -1153,6 +1200,7 @@ router.get('/:id/settings/all', authenticate, checkBotAccess, authorize('bot:upd
|
|
|
1153
1200
|
where: { id: botId },
|
|
1154
1201
|
include: {
|
|
1155
1202
|
server: true,
|
|
1203
|
+
proxy: true,
|
|
1156
1204
|
installedPlugins: {
|
|
1157
1205
|
orderBy: { name: 'asc' }
|
|
1158
1206
|
}
|
|
@@ -1171,6 +1219,7 @@ router.get('/:id/settings/all', authenticate, checkBotAccess, authorize('bot:upd
|
|
|
1171
1219
|
note: bot.note,
|
|
1172
1220
|
owners: bot.owners,
|
|
1173
1221
|
serverId: bot.serverId,
|
|
1222
|
+
proxyId: bot.proxyId,
|
|
1174
1223
|
proxyHost: bot.proxyHost,
|
|
1175
1224
|
proxyPort: bot.proxyPort,
|
|
1176
1225
|
proxyUsername: bot.proxyUsername,
|
|
@@ -1232,7 +1281,7 @@ router.get('/:id/settings/all', authenticate, checkBotAccess, authorize('bot:upd
|
|
|
1232
1281
|
|
|
1233
1282
|
const nodeRegistry = require('../../core/NodeRegistry');
|
|
1234
1283
|
|
|
1235
|
-
router.get('/:botId/visual-editor/nodes',
|
|
1284
|
+
router.get('/:botId/visual-editor/nodes', authenticateUniversal, checkBotAccess, authorize('management:view'), (req, res) => {
|
|
1236
1285
|
try {
|
|
1237
1286
|
const { graphType } = req.query;
|
|
1238
1287
|
const nodesByCategory = nodeRegistry.getNodesByCategory(graphType);
|
|
@@ -1243,7 +1292,7 @@ router.get('/:botId/visual-editor/nodes', authenticate, checkBotAccess, authoriz
|
|
|
1243
1292
|
}
|
|
1244
1293
|
});
|
|
1245
1294
|
|
|
1246
|
-
router.get('/:botId/visual-editor/node-config',
|
|
1295
|
+
router.get('/:botId/visual-editor/node-config', authenticateUniversal, checkBotAccess, authorize('management:view'), (req, res) => {
|
|
1247
1296
|
try {
|
|
1248
1297
|
const { types } = req.query;
|
|
1249
1298
|
if (!types) {
|
|
@@ -1258,7 +1307,7 @@ router.get('/:botId/visual-editor/node-config', authenticate, checkBotAccess, au
|
|
|
1258
1307
|
}
|
|
1259
1308
|
});
|
|
1260
1309
|
|
|
1261
|
-
router.get('/:botId/visual-editor/permissions',
|
|
1310
|
+
router.get('/:botId/visual-editor/permissions', authenticateUniversal, checkBotAccess, authorize('management:view'), async (req, res) => {
|
|
1262
1311
|
try {
|
|
1263
1312
|
const botId = parseInt(req.params.botId, 10);
|
|
1264
1313
|
const permissions = await prisma.permission.findMany({
|
|
@@ -1879,7 +1928,7 @@ router.post('/:botId/plugins/:pluginName/action', authorize('plugin:list'), asyn
|
|
|
1879
1928
|
});
|
|
1880
1929
|
|
|
1881
1930
|
|
|
1882
|
-
router.get('/:botId/export',
|
|
1931
|
+
router.get('/:botId/export', authenticateUniversal, checkBotAccess, authorize('bot:export'), async (req, res) => {
|
|
1883
1932
|
try {
|
|
1884
1933
|
const botId = parseInt(req.params.botId, 10);
|
|
1885
1934
|
const {
|