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,7 +1,7 @@
1
1
  const express = require('express');
2
2
  const { param, body, validationResult } = require('express-validator');
3
3
  const { PrismaClient } = require('@prisma/client');
4
- const { authorize } = require('../middleware/auth');
4
+ const { authenticate, authorize } = require('../middleware/auth');
5
5
  const logger = require('../../lib/logger');
6
6
  const crypto = require('crypto');
7
7
  const fse = require('fs-extra');
@@ -10,6 +10,9 @@ const path = require('path');
10
10
  const prisma = new PrismaClient();
11
11
  const router = express.Router({ mergeParams: true });
12
12
 
13
+ // Все роуты требуют аутентификации
14
+ router.use(authenticate);
15
+
13
16
  router.get('/',
14
17
  authorize('management:view'),
15
18
  async (req, res) => {
@@ -222,9 +222,19 @@ function broadcastLog(log) {
222
222
  const originalAddToLogBuffer = addToLogBuffer;
223
223
  addToLogBuffer = function(level, ...args) {
224
224
  const timestamp = new Date().toISOString();
225
- const message = args.map(arg =>
226
- typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
227
- ).join(' ');
225
+ const message = args.map(arg => {
226
+ if (typeof arg === 'object') {
227
+ try {
228
+ return JSON.stringify(arg, null, 2);
229
+ } catch (e) {
230
+ if (arg instanceof Error) {
231
+ return `${arg.name}: ${arg.message}`;
232
+ }
233
+ return '[Object with circular reference]';
234
+ }
235
+ }
236
+ return String(arg);
237
+ }).join(' ');
228
238
 
229
239
  const logEntry = {
230
240
  timestamp,
@@ -1,67 +1,67 @@
1
- const express = require('express');
2
- const fs = require('fs/promises');
3
- const path = require('path');
4
- const os = require('os');
5
- const { authenticate, authorize } = require('../middleware/auth');
6
- const config = require('../../config');
7
-
8
- const router = express.Router();
9
-
10
- const DATA_DIR = path.join(os.homedir(), '.blockmine');
11
- const CONFIG_PATH = path.join(DATA_DIR, 'config.json');
12
-
13
- /**
14
- * @route GET /api/panel/settings
15
- * @desc Получить текущие глобальные настройки
16
- * @access Private (Admin only)
17
- */
18
- router.get('/settings', authenticate, authorize('panel:settings:view'), (req, res) => {
19
- const { server, telemetry } = config;
20
- res.json({
21
- server: {
22
- allowExternalAccess: server.allowExternalAccess
23
- },
24
- telemetry: {
25
- enabled: telemetry?.enabled ?? true
26
- }
27
- });
28
- });
29
-
30
- /**
31
- * @route PUT /api/panel/settings
32
- * @desc Обновить глобальные настройки
33
- * @access Private (Admin only)
34
- */
35
- router.put('/settings', authenticate, authorize('panel:settings:edit'), async (req, res) => {
36
- const { allowExternalAccess, telemetryEnabled } = req.body;
37
-
38
- try {
39
- const currentConfig = JSON.parse(await fs.readFile(CONFIG_PATH, 'utf-8'));
40
-
41
- if (typeof allowExternalAccess === 'boolean') {
42
- currentConfig.server.allowExternalAccess = allowExternalAccess;
43
- currentConfig.server.host = allowExternalAccess ? '0.0.0.0' : '127.0.0.1';
44
- console.log(`[Config Update] Внешний доступ ${allowExternalAccess ? 'ВКЛЮЧЕН' : 'ВЫКЛЮЧЕН'}. Хост изменен на ${currentConfig.server.host}`);
45
- }
46
-
47
- if (typeof telemetryEnabled === 'boolean') {
48
- if (!currentConfig.telemetry) {
49
- currentConfig.telemetry = {};
50
- }
51
- currentConfig.telemetry.enabled = telemetryEnabled;
52
- }
53
-
54
- await fs.writeFile(CONFIG_PATH, JSON.stringify(currentConfig, null, 2), 'utf-8');
55
-
56
- res.json({
57
- message: 'Настройки сохранены. Для применения требуется перезапуск панели.',
58
- requiresRestart: true
59
- });
60
-
61
- } catch (error) {
62
- console.error("Ошибка обновления файла конфигурации:", error);
63
- res.status(500).json({ error: 'Не удалось сохранить настройки.' });
64
- }
65
- });
66
-
1
+ const express = require('express');
2
+ const fs = require('fs/promises');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { authenticate, authenticateUniversal, authorize } = require('../middleware/auth');
6
+ const config = require('../../config');
7
+
8
+ const router = express.Router();
9
+
10
+ const DATA_DIR = path.join(os.homedir(), '.blockmine');
11
+ const CONFIG_PATH = path.join(DATA_DIR, 'config.json');
12
+
13
+ /**
14
+ * @route GET /api/panel/settings
15
+ * @desc Получить текущие глобальные настройки
16
+ * @access Private (Admin only)
17
+ */
18
+ router.get('/settings', authenticateUniversal, authorize('panel:settings:view'), (req, res) => {
19
+ const { server, telemetry } = config;
20
+ res.json({
21
+ server: {
22
+ allowExternalAccess: server.allowExternalAccess
23
+ },
24
+ telemetry: {
25
+ enabled: telemetry?.enabled ?? true
26
+ }
27
+ });
28
+ });
29
+
30
+ /**
31
+ * @route PUT /api/panel/settings
32
+ * @desc Обновить глобальные настройки
33
+ * @access Private (Admin only)
34
+ */
35
+ router.put('/settings', authenticateUniversal, authorize('panel:settings:edit'), async (req, res) => {
36
+ const { allowExternalAccess, telemetryEnabled } = req.body;
37
+
38
+ try {
39
+ const currentConfig = JSON.parse(await fs.readFile(CONFIG_PATH, 'utf-8'));
40
+
41
+ if (typeof allowExternalAccess === 'boolean') {
42
+ currentConfig.server.allowExternalAccess = allowExternalAccess;
43
+ currentConfig.server.host = allowExternalAccess ? '0.0.0.0' : '127.0.0.1';
44
+ console.log(`[Config Update] Внешний доступ ${allowExternalAccess ? 'ВКЛЮЧЕН' : 'ВЫКЛЮЧЕН'}. Хост изменен на ${currentConfig.server.host}`);
45
+ }
46
+
47
+ if (typeof telemetryEnabled === 'boolean') {
48
+ if (!currentConfig.telemetry) {
49
+ currentConfig.telemetry = {};
50
+ }
51
+ currentConfig.telemetry.enabled = telemetryEnabled;
52
+ }
53
+
54
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(currentConfig, null, 2), 'utf-8');
55
+
56
+ res.json({
57
+ message: 'Настройки сохранены. Для применения требуется перезапуск панели.',
58
+ requiresRestart: true
59
+ });
60
+
61
+ } catch (error) {
62
+ console.error("Ошибка обновления файла конфигурации:", error);
63
+ res.status(500).json({ error: 'Не удалось сохранить настройки.' });
64
+ }
65
+ });
66
+
67
67
  module.exports = router;
@@ -0,0 +1,179 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const bcrypt = require('bcryptjs');
4
+ const crypto = require('crypto');
5
+ const prisma = require('../../lib/prisma');
6
+ const { authenticate, authenticateUniversal } = require('../middleware/auth');
7
+
8
+ /**
9
+ * Генерация случайного API ключа
10
+ */
11
+ function generateApiKey() {
12
+ return 'pk_' + crypto.randomBytes(32).toString('hex');
13
+ }
14
+
15
+ /**
16
+ * GET /api/panel/api-keys
17
+ * Получить список API ключей пользователя
18
+ */
19
+ router.get('/', authenticateUniversal, async (req, res) => {
20
+ try {
21
+ const keys = await prisma.panelApiKey.findMany({
22
+ where: {
23
+ userId: req.user.userId
24
+ },
25
+ select: {
26
+ id: true,
27
+ name: true,
28
+ prefix: true,
29
+ customScopes: true,
30
+ lastUsedAt: true,
31
+ expiresAt: true,
32
+ isActive: true,
33
+ createdAt: true
34
+ },
35
+ orderBy: {
36
+ createdAt: 'desc'
37
+ }
38
+ });
39
+
40
+ res.json({
41
+ success: true,
42
+ keys: keys.map(key => ({
43
+ ...key,
44
+ customScopes: key.customScopes ? JSON.parse(key.customScopes) : null
45
+ }))
46
+ });
47
+ } catch (error) {
48
+ console.error('Ошибка при получении списка API ключей:', error);
49
+ res.status(500).json({ error: 'Не удалось получить список API ключей' });
50
+ }
51
+ });
52
+
53
+ /**
54
+ * POST /api/panel/api-keys
55
+ * Создать новый API ключ
56
+ */
57
+ router.post('/', authenticateUniversal, async (req, res) => {
58
+ try {
59
+ const { name, customScopes, expiresAt } = req.body;
60
+
61
+ if (!name || typeof name !== 'string' || name.trim().length === 0) {
62
+ return res.status(400).json({ error: 'Имя ключа обязательно' });
63
+ }
64
+
65
+ const apiKey = generateApiKey();
66
+ const keyHash = await bcrypt.hash(apiKey, 10);
67
+ const prefix = apiKey.substring(0, 10);
68
+
69
+ const newKey = await prisma.panelApiKey.create({
70
+ data: {
71
+ userId: req.user.userId,
72
+ name: name.trim(),
73
+ keyHash,
74
+ prefix,
75
+ customScopes: customScopes ? JSON.stringify(customScopes) : null,
76
+ expiresAt: expiresAt ? new Date(expiresAt) : null
77
+ }
78
+ });
79
+
80
+ res.json({
81
+ success: true,
82
+ message: 'API ключ успешно создан',
83
+ key: {
84
+ id: newKey.id,
85
+ name: newKey.name,
86
+ prefix: newKey.prefix,
87
+ customScopes: newKey.customScopes ? JSON.parse(newKey.customScopes) : null,
88
+ expiresAt: newKey.expiresAt,
89
+ createdAt: newKey.createdAt,
90
+ apiKey
91
+ }
92
+ });
93
+ } catch (error) {
94
+ console.error('Ошибка при создании API ключа:', error);
95
+ res.status(500).json({ error: 'Не удалось создать API ключ' });
96
+ }
97
+ });
98
+
99
+ /**
100
+ * PATCH /api/panel/api-keys/:id
101
+ * Обновить API ключ (название, статус, срок действия)
102
+ */
103
+ router.patch('/:id', authenticateUniversal, async (req, res) => {
104
+ try {
105
+ const keyId = parseInt(req.params.id);
106
+ const { name, isActive, expiresAt } = req.body;
107
+
108
+ const existingKey = await prisma.panelApiKey.findFirst({
109
+ where: {
110
+ id: keyId,
111
+ userId: req.user.userId
112
+ }
113
+ });
114
+
115
+ if (!existingKey) {
116
+ return res.status(404).json({ error: 'API ключ не найден' });
117
+ }
118
+
119
+ const updateData = {};
120
+ if (name !== undefined) updateData.name = name;
121
+ if (isActive !== undefined) updateData.isActive = isActive;
122
+ if (expiresAt !== undefined) updateData.expiresAt = expiresAt ? new Date(expiresAt) : null;
123
+
124
+ const updatedKey = await prisma.panelApiKey.update({
125
+ where: { id: keyId },
126
+ data: updateData
127
+ });
128
+
129
+ res.json({
130
+ success: true,
131
+ message: 'API ключ успешно обновлён',
132
+ key: {
133
+ id: updatedKey.id,
134
+ name: updatedKey.name,
135
+ prefix: updatedKey.prefix,
136
+ isActive: updatedKey.isActive,
137
+ expiresAt: updatedKey.expiresAt
138
+ }
139
+ });
140
+ } catch (error) {
141
+ console.error('Ошибка при обновлении API ключа:', error);
142
+ res.status(500).json({ error: 'Не удалось обновить API ключ' });
143
+ }
144
+ });
145
+
146
+ /**
147
+ * DELETE /api/panel/api-keys/:id
148
+ * Удалить (отозвать) API ключ
149
+ */
150
+ router.delete('/:id', authenticateUniversal, async (req, res) => {
151
+ try {
152
+ const keyId = parseInt(req.params.id);
153
+
154
+ const existingKey = await prisma.panelApiKey.findFirst({
155
+ where: {
156
+ id: keyId,
157
+ userId: req.user.userId
158
+ }
159
+ });
160
+
161
+ if (!existingKey) {
162
+ return res.status(404).json({ error: 'API ключ не найден' });
163
+ }
164
+
165
+ await prisma.panelApiKey.delete({
166
+ where: { id: keyId }
167
+ });
168
+
169
+ res.json({
170
+ success: true,
171
+ message: 'API ключ успешно удалён'
172
+ });
173
+ } catch (error) {
174
+ console.error('Ошибка при удалении API ключа:', error);
175
+ res.status(500).json({ error: 'Не удалось удалить API ключ' });
176
+ }
177
+ });
178
+
179
+ module.exports = router;