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,220 +1,377 @@
1
- const express = require('express');
2
- const { authenticate, authorize } = require('../middleware/auth');
3
- const { PrismaClient } = require('@prisma/client');
4
- const router = express.Router();
5
- const { pluginManager } = require('../../core/services');
6
-
7
- const prisma = new PrismaClient();
8
- const OFFICIAL_CATALOG_URL = "https://raw.githubusercontent.com/blockmineJS/official-plugins-list/main/index.json";
9
-
10
- const getCacheBustedUrl = (url) => `${url}?t=${new Date().getTime()}`;
11
-
12
-
13
- router.get('/catalog', async (req, res) => {
14
- try {
15
- const response = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
16
-
17
- if (!response.ok) {
18
- const errorText = await response.text();
19
- console.error(`[API Error] Failed to fetch catalog from GitHub. Status: ${response.status}, Response: ${errorText}`);
20
- throw new Error(`GitHub returned status ${response.status}`);
21
- }
22
-
23
- res.json(await response.json());
24
- } catch (error) {
25
- console.error(`[API Error] Could not fetch catalog URL. Reason: ${error.message}`);
26
- res.status(500).json({ error: 'Не удалось загрузить каталог плагинов.' });
27
- }
28
- });
29
-
30
- router.post('/check-updates/:botId', authenticate, authorize('plugin:update'), async (req, res) => {
31
- try {
32
- const botId = parseInt(req.params.botId);
33
-
34
- const catalogResponse = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
35
- if (!catalogResponse.ok) throw new Error('Не удалось загрузить каталог для проверки обновлений.');
36
- const catalog = await catalogResponse.json();
37
-
38
- const updates = await pluginManager.checkForUpdates(botId, catalog);
39
- res.json(updates);
40
- } catch (error) {
41
- console.error("[API Error] /check-updates:", error);
42
- res.status(500).json({ error: 'Не удалось проверить обновления.' });
43
- }
44
- });
45
-
46
- router.post('/update/:pluginId', authenticate, authorize('plugin:update'), async (req, res) => {
47
- try {
48
- const pluginId = parseInt(req.params.pluginId);
49
- const { targetTag } = req.body; // Получаем тег из тела запроса (если указан)
50
- const updatedPlugin = await pluginManager.updatePlugin(pluginId, targetTag);
51
- res.json(updatedPlugin);
52
- } catch (error) {
53
- res.status(500).json({ error: error.message });
54
- }
55
- });
56
-
57
- router.post('/:id/clear-data', authenticate, authorize('plugin:settings:edit'), async (req, res) => {
58
- try {
59
- const pluginId = parseInt(req.params.id);
60
- await pluginManager.clearPluginData(pluginId);
61
- res.status(200).json({ message: 'Данные плагина успешно очищены.' });
62
- } catch (error) {
63
- console.error(`[API Error] /plugins/:id/clear-data:`, error);
64
- res.status(500).json({ error: error.message || 'Не удалось очистить данные плагина.' });
65
- }
66
- });
67
-
68
- router.get('/:id/info', authenticate, authorize('plugin:list'), async (req, res) => {
69
- try {
70
- const pluginId = parseInt(req.params.id);
71
-
72
- const plugin = await prisma.installedPlugin.findUnique({
73
- where: { id: pluginId },
74
- select: {
75
- id: true,
76
- name: true,
77
- version: true,
78
- description: true,
79
- sourceType: true,
80
- sourceUri: true,
81
- isEnabled: true,
82
- manifest: true,
83
- settings: true,
84
- createdAt: true,
85
- commands: {
86
- select: {
87
- id: true,
88
- name: true,
89
- description: true,
90
- isEnabled: true,
91
- isVisual: true,
92
- owner: true
93
- }
94
- },
95
- eventGraphs: {
96
- select: {
97
- id: true,
98
- name: true,
99
- isEnabled: true,
100
- createdAt: true,
101
- updatedAt: true
102
- }
103
- }
104
- }
105
- });
106
-
107
- if (!plugin) {
108
- return res.status(404).json({ error: 'Плагин не найден.' });
109
- }
110
-
111
- res.json(plugin);
112
- } catch (error) {
113
- console.error(`[API Error] /plugins/:id/info:`, error);
114
- res.status(500).json({ error: 'Не удалось получить информацию о плагине.' });
115
- }
116
- });
117
-
118
- router.get('/bot/:botId', authenticate, authorize('plugin:list'), async (req, res) => {
119
- try {
120
- const botId = parseInt(req.params.botId);
121
-
122
- const plugins = await prisma.installedPlugin.findMany({
123
- where: { botId },
124
- select: {
125
- id: true,
126
- name: true,
127
- version: true,
128
- description: true,
129
- sourceType: true,
130
- sourceUri: true,
131
- isEnabled: true,
132
- manifest: true,
133
- settings: true,
134
- createdAt: true,
135
- commands: {
136
- select: {
137
- id: true,
138
- name: true,
139
- description: true,
140
- isEnabled: true,
141
- isVisual: true,
142
- owner: true
143
- }
144
- },
145
- eventGraphs: {
146
- select: {
147
- id: true,
148
- name: true,
149
- isEnabled: true,
150
- createdAt: true,
151
- updatedAt: true
152
- }
153
- }
154
- },
155
- orderBy: { name: 'asc' }
156
- });
157
-
158
- res.json(plugins);
159
- } catch (error) {
160
- console.error(`[API Error] /plugins/bot/:botId:`, error);
161
- res.status(500).json({ error: 'Не удалось получить список плагинов.' });
162
- }
163
- });
164
-
165
- router.get('/catalog/:name', async (req, res) => {
166
- try {
167
- const pluginName = req.params.name;
168
-
169
- const catalogResponse = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
170
- if (!catalogResponse.ok) throw new Error(`Failed to fetch catalog, status: ${catalogResponse.status}`);
171
-
172
- const catalog = await catalogResponse.json();
173
- const pluginInfo = catalog.find(p => p.name === pluginName);
174
-
175
- if (!pluginInfo) {
176
- return res.status(404).json({ error: 'Плагин с таким именем не найден в каталоге.' });
177
- }
178
-
179
- let readmeContent = pluginInfo.description || 'Описание для этого плагина не предоставлено.';
180
-
181
- try {
182
- const urlParts = new URL(pluginInfo.repoUrl);
183
- const pathParts = urlParts.pathname.split('/').filter(p => p);
184
-
185
- if (pathParts.length >= 2) {
186
- const owner = pathParts[0];
187
- const repo = pathParts[1].replace('.git', '');
188
-
189
- const readmeUrls = [
190
- `https://raw.githubusercontent.com/${owner}/${repo}/main/README.md`,
191
- `https://raw.githubusercontent.com/${owner}/${repo}/master/README.md`,
192
- `https://raw.githubusercontent.com/${owner}/${repo}/main/readme.md`,
193
- `https://raw.githubusercontent.com/${owner}/${repo}/master/readme.md`,
194
- ];
195
-
196
- for (const url of readmeUrls) {
197
- const readmeResponse = await fetch(getCacheBustedUrl(url));
198
- if (readmeResponse.ok) {
199
- readmeContent = await readmeResponse.text();
200
- break;
201
- }
202
- }
203
- }
204
- } catch (readmeError) {
205
- console.error(`[API] Не удалось загрузить README для ${pluginName}:`, readmeError.message);
206
- }
207
-
208
- const finalPluginData = {
209
- ...pluginInfo,
210
- fullDescription: readmeContent
211
- };
212
-
213
- res.json(finalPluginData);
214
- } catch (error) {
215
- console.error(`[API Error] /catalog/:name :`, error);
216
- res.status(500).json({ error: 'Не удалось загрузить данные плагина.' });
217
- }
218
- });
219
-
1
+ const express = require('express');
2
+ const { authenticateUniversal, authorize } = require('../middleware/auth');
3
+ const { PrismaClient } = require('@prisma/client');
4
+ const router = express.Router();
5
+ const { pluginManager } = require('../../core/services');
6
+
7
+ const prisma = new PrismaClient();
8
+ const OFFICIAL_CATALOG_URL = "https://raw.githubusercontent.com/blockmineJS/official-plugins-list/main/index.json";
9
+
10
+ const getCacheBustedUrl = (url) => `${url}?t=${new Date().getTime()}`;
11
+
12
+
13
+ router.get('/catalog', async (req, res) => {
14
+ try {
15
+ const response = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
16
+
17
+ if (!response.ok) {
18
+ const errorText = await response.text();
19
+ console.error(`[API Error] Failed to fetch catalog from GitHub. Status: ${response.status}, Response: ${errorText}`);
20
+ throw new Error(`GitHub returned status ${response.status}`);
21
+ }
22
+
23
+ res.json(await response.json());
24
+ } catch (error) {
25
+ console.error(`[API Error] Could not fetch catalog URL. Reason: ${error.message}`);
26
+ res.status(500).json({ error: 'Не удалось загрузить каталог плагинов.' });
27
+ }
28
+ });
29
+
30
+ router.post('/check-updates/:botId', authenticateUniversal, authorize('plugin:update'), async (req, res) => {
31
+ try {
32
+ const botId = parseInt(req.params.botId);
33
+
34
+ const catalogResponse = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
35
+ if (!catalogResponse.ok) throw new Error('Не удалось загрузить каталог для проверки обновлений.');
36
+ const catalog = await catalogResponse.json();
37
+
38
+ const updates = await pluginManager.checkForUpdates(botId, catalog);
39
+ res.json(updates);
40
+ } catch (error) {
41
+ console.error("[API Error] /check-updates:", error);
42
+ res.status(500).json({ error: 'Не удалось проверить обновления.' });
43
+ }
44
+ });
45
+
46
+ router.post('/update/:pluginId', authenticateUniversal, authorize('plugin:update'), async (req, res) => {
47
+ try {
48
+ const pluginId = parseInt(req.params.pluginId);
49
+ const { targetTag } = req.body; // Получаем тег из тела запроса (если указан)
50
+ const updatedPlugin = await pluginManager.updatePlugin(pluginId, targetTag);
51
+ res.json(updatedPlugin);
52
+ } catch (error) {
53
+ res.status(500).json({ error: error.message });
54
+ }
55
+ });
56
+
57
+ router.post('/:id/clear-data', authenticateUniversal, authorize('plugin:settings:edit'), async (req, res) => {
58
+ try {
59
+ const pluginId = parseInt(req.params.id);
60
+ await pluginManager.clearPluginData(pluginId);
61
+ res.status(200).json({ message: 'Данные плагина успешно очищены.' });
62
+ } catch (error) {
63
+ console.error(`[API Error] /plugins/:id/clear-data:`, error);
64
+ res.status(500).json({ error: error.message || 'Не удалось очистить данные плагина.' });
65
+ }
66
+ });
67
+
68
+ router.post('/:id/reload', authenticateUniversal, authorize('plugin:settings:edit'), async (req, res) => {
69
+ try {
70
+ const pluginId = parseInt(req.params.id);
71
+ const updatedPlugin = await pluginManager.reloadLocalPlugin(pluginId);
72
+ res.status(200).json({
73
+ message: 'Плагин перезагружен, настройки сброшены.',
74
+ plugin: updatedPlugin
75
+ });
76
+ } catch (error) {
77
+ console.error(`[API Error] /plugins/:id/reload:`, error);
78
+ res.status(500).json({ error: error.message || 'Не удалось перезагрузить плагин.' });
79
+ }
80
+ });
81
+
82
+ router.get('/:id/info', authenticateUniversal, authorize('plugin:list'), async (req, res) => {
83
+ try {
84
+ const pluginId = parseInt(req.params.id);
85
+
86
+ const plugin = await prisma.installedPlugin.findUnique({
87
+ where: { id: pluginId },
88
+ select: {
89
+ id: true,
90
+ name: true,
91
+ version: true,
92
+ description: true,
93
+ sourceType: true,
94
+ sourceUri: true,
95
+ isEnabled: true,
96
+ manifest: true,
97
+ settings: true,
98
+ createdAt: true,
99
+ commands: {
100
+ select: {
101
+ id: true,
102
+ name: true,
103
+ description: true,
104
+ isEnabled: true,
105
+ isVisual: true,
106
+ owner: true
107
+ }
108
+ },
109
+ eventGraphs: {
110
+ select: {
111
+ id: true,
112
+ name: true,
113
+ isEnabled: true,
114
+ createdAt: true,
115
+ updatedAt: true
116
+ }
117
+ }
118
+ }
119
+ });
120
+
121
+ if (!plugin) {
122
+ return res.status(404).json({ error: 'Плагин не найден.' });
123
+ }
124
+
125
+ res.json(plugin);
126
+ } catch (error) {
127
+ console.error(`[API Error] /plugins/:id/info:`, error);
128
+ res.status(500).json({ error: 'Не удалось получить информацию о плагине.' });
129
+ }
130
+ });
131
+
132
+ router.get('/bot/:botId', authenticateUniversal, authorize('plugin:list'), async (req, res) => {
133
+ try {
134
+ const botId = parseInt(req.params.botId);
135
+
136
+ const plugins = await prisma.installedPlugin.findMany({
137
+ where: { botId },
138
+ select: {
139
+ id: true,
140
+ name: true,
141
+ version: true,
142
+ description: true,
143
+ sourceType: true,
144
+ sourceUri: true,
145
+ isEnabled: true,
146
+ manifest: true,
147
+ settings: true,
148
+ createdAt: true,
149
+ commands: {
150
+ select: {
151
+ id: true,
152
+ name: true,
153
+ description: true,
154
+ isEnabled: true,
155
+ isVisual: true,
156
+ owner: true
157
+ }
158
+ },
159
+ eventGraphs: {
160
+ select: {
161
+ id: true,
162
+ name: true,
163
+ isEnabled: true,
164
+ createdAt: true,
165
+ updatedAt: true
166
+ }
167
+ }
168
+ },
169
+ orderBy: { name: 'asc' }
170
+ });
171
+
172
+ res.json(plugins);
173
+ } catch (error) {
174
+ console.error(`[API Error] /plugins/bot/:botId:`, error);
175
+ res.status(500).json({ error: 'Не удалось получить список плагинов.' });
176
+ }
177
+ });
178
+
179
+ router.get('/catalog/:name', async (req, res) => {
180
+ try {
181
+ const pluginName = req.params.name;
182
+
183
+ const catalogResponse = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
184
+ if (!catalogResponse.ok) throw new Error(`Failed to fetch catalog, status: ${catalogResponse.status}`);
185
+
186
+ const catalog = await catalogResponse.json();
187
+ const pluginInfo = catalog.find(p => p.name === pluginName);
188
+
189
+ if (!pluginInfo) {
190
+ return res.status(404).json({ error: 'Плагин с таким именем не найден в каталоге.' });
191
+ }
192
+
193
+ let readmeContent = pluginInfo.description || 'Описание для этого плагина не предоставлено.';
194
+
195
+ try {
196
+ const urlParts = new URL(pluginInfo.repoUrl);
197
+ const pathParts = urlParts.pathname.split('/').filter(p => p);
198
+
199
+ if (pathParts.length >= 2) {
200
+ const owner = pathParts[0];
201
+ const repo = pathParts[1].replace('.git', '');
202
+
203
+ const readmeUrls = [
204
+ `https://raw.githubusercontent.com/${owner}/${repo}/main/README.md`,
205
+ `https://raw.githubusercontent.com/${owner}/${repo}/master/README.md`,
206
+ `https://raw.githubusercontent.com/${owner}/${repo}/main/readme.md`,
207
+ `https://raw.githubusercontent.com/${owner}/${repo}/master/readme.md`,
208
+ ];
209
+
210
+ for (const url of readmeUrls) {
211
+ const readmeResponse = await fetch(getCacheBustedUrl(url));
212
+ if (readmeResponse.ok) {
213
+ readmeContent = await readmeResponse.text();
214
+ break;
215
+ }
216
+ }
217
+ }
218
+ } catch (readmeError) {
219
+ console.error(`[API] Не удалось загрузить README для ${pluginName}:`, readmeError.message);
220
+ }
221
+
222
+ const finalPluginData = {
223
+ ...pluginInfo,
224
+ fullDescription: readmeContent
225
+ };
226
+
227
+ res.json(finalPluginData);
228
+ } catch (error) {
229
+ console.error(`[API Error] /catalog/:name :`, error);
230
+ res.status(500).json({ error: 'Не удалось загрузить данные плагина.' });
231
+ }
232
+ });
233
+
234
+ router.get('/bot/:botId/:pluginName/store', authenticateUniversal, authorize('plugin:list'), async (req, res) => {
235
+ try {
236
+ const botId = parseInt(req.params.botId);
237
+ const pluginName = req.params.pluginName;
238
+
239
+ const storeData = await prisma.pluginDataStore.findMany({
240
+ where: {
241
+ botId,
242
+ pluginName
243
+ },
244
+ select: {
245
+ key: true,
246
+ value: true,
247
+ createdAt: true,
248
+ updatedAt: true
249
+ }
250
+ });
251
+
252
+ const result = {};
253
+ storeData.forEach(item => {
254
+ try {
255
+ result[item.key] = JSON.parse(item.value);
256
+ } catch (e) {
257
+ result[item.key] = item.value;
258
+ }
259
+ });
260
+
261
+ res.json(result);
262
+ } catch (error) {
263
+ console.error(`[API Error] GET /plugins/bot/:botId/:pluginName/store:`, error);
264
+ res.status(500).json({ error: 'Не удалось получить данные store плагина.' });
265
+ }
266
+ });
267
+
268
+ router.get('/bot/:botId/:pluginName/store/:key', authenticateUniversal, authorize('plugin:list'), async (req, res) => {
269
+ try {
270
+ const botId = parseInt(req.params.botId);
271
+ const pluginName = req.params.pluginName;
272
+ const key = req.params.key;
273
+
274
+ const storeItem = await prisma.pluginDataStore.findUnique({
275
+ where: {
276
+ pluginName_botId_key: {
277
+ pluginName,
278
+ botId,
279
+ key
280
+ }
281
+ },
282
+ select: {
283
+ value: true,
284
+ createdAt: true,
285
+ updatedAt: true
286
+ }
287
+ });
288
+
289
+ if (!storeItem) {
290
+ return res.status(404).json({ error: 'Ключ не найден в store.' });
291
+ }
292
+
293
+ let parsedValue;
294
+ try {
295
+ parsedValue = JSON.parse(storeItem.value);
296
+ } catch (e) {
297
+ parsedValue = storeItem.value;
298
+ }
299
+
300
+ res.json({
301
+ key,
302
+ value: parsedValue,
303
+ createdAt: storeItem.createdAt,
304
+ updatedAt: storeItem.updatedAt
305
+ });
306
+ } catch (error) {
307
+ console.error(`[API Error] GET /plugins/bot/:botId/:pluginName/store/:key:`, error);
308
+ res.status(500).json({ error: 'Не удалось получить значение из store.' });
309
+ }
310
+ });
311
+
312
+ router.put('/bot/:botId/:pluginName/store/:key', authenticateUniversal, authorize('plugin:settings:edit'), async (req, res) => {
313
+ try {
314
+ const botId = parseInt(req.params.botId);
315
+ const pluginName = req.params.pluginName;
316
+ const key = req.params.key;
317
+ const { value } = req.body;
318
+
319
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
320
+
321
+ const storeItem = await prisma.pluginDataStore.upsert({
322
+ where: {
323
+ pluginName_botId_key: {
324
+ pluginName,
325
+ botId,
326
+ key
327
+ }
328
+ },
329
+ update: {
330
+ value: stringValue
331
+ },
332
+ create: {
333
+ pluginName,
334
+ botId,
335
+ key,
336
+ value: stringValue
337
+ }
338
+ });
339
+
340
+ res.json({
341
+ key,
342
+ value: value,
343
+ updatedAt: storeItem.updatedAt
344
+ });
345
+ } catch (error) {
346
+ console.error(`[API Error] PUT /plugins/bot/:botId/:pluginName/store/:key:`, error);
347
+ res.status(500).json({ error: 'Не удалось сохранить значение в store.' });
348
+ }
349
+ });
350
+
351
+ router.delete('/bot/:botId/:pluginName/store/:key', authenticateUniversal, authorize('plugin:settings:edit'), async (req, res) => {
352
+ try {
353
+ const botId = parseInt(req.params.botId);
354
+ const pluginName = req.params.pluginName;
355
+ const key = req.params.key;
356
+
357
+ await prisma.pluginDataStore.delete({
358
+ where: {
359
+ pluginName_botId_key: {
360
+ pluginName,
361
+ botId,
362
+ key
363
+ }
364
+ }
365
+ });
366
+
367
+ res.json({ message: 'Значение удалено из store.' });
368
+ } catch (error) {
369
+ if (error.code === 'P2025') {
370
+ return res.status(404).json({ error: 'Ключ не найден в store.' });
371
+ }
372
+ console.error(`[API Error] DELETE /plugins/bot/:botId/:pluginName/store/:key:`, error);
373
+ res.status(500).json({ error: 'Не удалось удалить значение из store.' });
374
+ }
375
+ });
376
+
220
377
  module.exports = router;