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
@@ -1,11 +1,37 @@
1
1
  const jwt = require('jsonwebtoken');
2
2
  const config = require('../../config');
3
+ const { authenticatePanelApiKey } = require('./panelApiAuth');
3
4
 
4
5
  const JWT_SECRET = config.security.jwtSecret;
5
6
 
6
7
  const tokenCache = new Map();
7
8
  const CACHE_TTL = 5 * 60 * 1000;
8
9
 
10
+ /**
11
+ * Универсальный middleware аутентификации
12
+ * Поддерживает Panel API Keys и JWT токены
13
+ */
14
+ function authenticateUniversal(req, res, next) {
15
+ const authHeader = req.header('Authorization');
16
+ if (!authHeader) {
17
+ return res.status(401).json({ error: 'Нет токена, доступ запрещен' });
18
+ }
19
+
20
+ const tokenParts = authHeader.split(' ');
21
+ if (tokenParts.length !== 2 || tokenParts[0] !== 'Bearer') {
22
+ return res.status(401).json({ error: 'Неверный формат токена' });
23
+ }
24
+
25
+ const token = tokenParts[1];
26
+
27
+ // Если токен начинается с pk_ - это Panel API Key
28
+ if (token.startsWith('pk_')) {
29
+ return authenticatePanelApiKey(req, res, next);
30
+ }
31
+
32
+ // Иначе это JWT токен
33
+ return authenticate(req, res, next);
34
+ }
9
35
 
10
36
  function authenticate(req, res, next) {
11
37
  const authHeader = req.header('Authorization');
@@ -63,5 +89,6 @@ function authorize(requiredPermission) {
63
89
 
64
90
  module.exports = {
65
91
  authenticate,
92
+ authenticateUniversal,
66
93
  authorize,
67
94
  };
@@ -22,9 +22,13 @@ async function checkBotAccess(req, res, next) {
22
22
  if (!user) return res.status(401).json({ error: 'Пользователь не найден' });
23
23
 
24
24
  // Если поле allBots отсутствует (старый клиент) — считаем true
25
- if (user.allBots !== false) return next();
25
+ if (user.allBots !== false) {
26
+ return next();
27
+ }
26
28
  const allowed = user.botAccess.some((a) => a.botId === botId);
27
- if (!allowed) return res.status(403).json({ error: 'Доступ к боту запрещен' });
29
+ if (!allowed) {
30
+ return res.status(403).json({ error: 'Доступ к боту запрещен' });
31
+ }
28
32
  return next();
29
33
  } catch (e) {
30
34
  console.error('[checkBotAccess] error', e);
@@ -32,4 +36,4 @@ async function checkBotAccess(req, res, next) {
32
36
  }
33
37
  }
34
38
 
35
- module.exports = { checkBotAccess };
39
+ module.exports = { checkBotAccess };
@@ -0,0 +1,135 @@
1
+ const bcrypt = require('bcryptjs');
2
+ const prisma = require('../../lib/prisma');
3
+
4
+ const keyCache = new Map();
5
+ const CACHE_TTL = 5 * 60 * 1000;
6
+
7
+ /**
8
+ * Аутентификация запросов Panel API с использованием API ключей
9
+ */
10
+ async function authenticatePanelApiKey(req, res, next) {
11
+ const authHeader = req.header('Authorization');
12
+ if (!authHeader) {
13
+ return res.status(401).json({ error: 'API ключ не предоставлен' });
14
+ }
15
+
16
+ const keyParts = authHeader.split(' ');
17
+ if (keyParts.length !== 2 || keyParts[0] !== 'Bearer') {
18
+ return res.status(401).json({ error: 'Неверный формат API ключа' });
19
+ }
20
+
21
+ const apiKey = keyParts[1];
22
+
23
+ if (keyCache.has(apiKey)) {
24
+ const cached = keyCache.get(apiKey);
25
+ if (Date.now() < cached.expires) {
26
+ req.user = cached.user;
27
+ req.apiKey = cached.keyData;
28
+ return next();
29
+ } else {
30
+ keyCache.delete(apiKey);
31
+ }
32
+ }
33
+
34
+ try {
35
+ const allKeys = await prisma.panelApiKey.findMany({
36
+ where: {
37
+ isActive: true,
38
+ OR: [
39
+ { expiresAt: null },
40
+ { expiresAt: { gt: new Date() } }
41
+ ]
42
+ },
43
+ include: {
44
+ user: {
45
+ include: {
46
+ role: true
47
+ }
48
+ }
49
+ }
50
+ });
51
+
52
+ let matchedKey = null;
53
+ for (const keyRecord of allKeys) {
54
+ if (await bcrypt.compare(apiKey, keyRecord.keyHash)) {
55
+ matchedKey = keyRecord;
56
+ break;
57
+ }
58
+ }
59
+
60
+ if (!matchedKey) {
61
+ return res.status(401).json({ error: 'Неверный API ключ' });
62
+ }
63
+
64
+ await prisma.panelApiKey.update({
65
+ where: { id: matchedKey.id },
66
+ data: { lastUsedAt: new Date() }
67
+ });
68
+
69
+ let permissions;
70
+ try {
71
+ if (matchedKey.customScopes) {
72
+ permissions = JSON.parse(matchedKey.customScopes);
73
+ } else {
74
+ permissions = JSON.parse(matchedKey.user.role.permissions);
75
+ }
76
+ } catch (parseError) {
77
+ console.error('Ошибка парсинга прав доступа:', parseError);
78
+ return res.status(500).json({ error: 'Ошибка обработки прав доступа' });
79
+ }
80
+
81
+ console.log(`[Panel API Key] User: ${matchedKey.user.username}, Key: ${matchedKey.name}, Permissions:`, permissions);
82
+
83
+ const user = {
84
+ id: matchedKey.user.id,
85
+ userId: matchedKey.user.id, // для совместимости с JWT
86
+ uuid: matchedKey.user.uuid,
87
+ username: matchedKey.user.username,
88
+ roleId: matchedKey.user.roleId,
89
+ roleName: matchedKey.user.role.name,
90
+ permissions,
91
+ allBots: matchedKey.user.allBots
92
+ };
93
+
94
+ req.user = user;
95
+ req.apiKey = {
96
+ id: matchedKey.id,
97
+ name: matchedKey.name,
98
+ prefix: matchedKey.prefix
99
+ };
100
+
101
+ keyCache.set(apiKey, {
102
+ user,
103
+ keyData: req.apiKey,
104
+ expires: Date.now() + CACHE_TTL
105
+ });
106
+
107
+ next();
108
+ } catch (err) {
109
+ console.error('Ошибка аутентификации Panel API ключа:', err);
110
+ res.status(500).json({ error: 'Ошибка аутентификации' });
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Авторизация запросов Panel API на основе прав доступа
116
+ */
117
+ function authorizePanelApi(requiredPermission) {
118
+ return (req, res, next) => {
119
+ if (!req.user || !Array.isArray(req.user.permissions)) {
120
+ return res.status(403).json({ error: 'Доступ запрещён: пользователь не аутентифицирован или неверный формат прав' });
121
+ }
122
+
123
+ const userPermissions = req.user.permissions;
124
+ if (userPermissions.includes('*') || userPermissions.includes(requiredPermission)) {
125
+ next();
126
+ } else {
127
+ res.status(403).json({ error: 'Доступ запрещён: недостаточно прав' });
128
+ }
129
+ };
130
+ }
131
+
132
+ module.exports = {
133
+ authenticatePanelApiKey,
134
+ authorizePanelApi
135
+ };