blockmine 1.14.1 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -48
- package/backend/prisma/migrations/20250719115906_add_plugin_owner_to_graphs/migration.sql +45 -0
- package/backend/prisma/schema.prisma +9 -0
- package/backend/src/api/routes/auth.js +595 -591
- package/backend/src/api/routes/bots.js +1686 -1509
- package/backend/src/api/routes/eventGraphs.js +68 -9
- package/backend/src/api/routes/plugins.js +83 -0
- package/backend/src/core/BotProcess.js +101 -5
- package/backend/src/core/GraphExecutionEngine.js +39 -1
- package/backend/src/core/NodeRegistry.js +1076 -1076
- package/backend/src/core/PluginManager.js +402 -300
- package/backend/src/server.js +183 -183
- package/frontend/dist/assets/index-B3e51n-B.css +1 -0
- package/frontend/dist/assets/index-Q43S68Ug.js +8331 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-BOFp308w.css +0 -1
- package/frontend/dist/assets/index-C7vFQnSR.js +0 -8331
|
@@ -4,6 +4,8 @@ const { PrismaClient } = require('@prisma/client');
|
|
|
4
4
|
const { authorize } = require('../middleware/auth');
|
|
5
5
|
const logger = require('../../lib/logger');
|
|
6
6
|
const crypto = require('crypto');
|
|
7
|
+
const fse = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
7
9
|
|
|
8
10
|
const prisma = new PrismaClient();
|
|
9
11
|
const router = express.Router({ mergeParams: true });
|
|
@@ -14,7 +16,17 @@ router.get('/',
|
|
|
14
16
|
const { botId } = req.params;
|
|
15
17
|
const eventGraphs = await prisma.eventGraph.findMany({
|
|
16
18
|
where: { botId: parseInt(botId) },
|
|
17
|
-
include: {
|
|
19
|
+
include: {
|
|
20
|
+
triggers: true,
|
|
21
|
+
pluginOwner: {
|
|
22
|
+
select: {
|
|
23
|
+
id: true,
|
|
24
|
+
name: true,
|
|
25
|
+
version: true,
|
|
26
|
+
sourceType: true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
18
30
|
orderBy: { createdAt: 'desc' }
|
|
19
31
|
});
|
|
20
32
|
|
|
@@ -46,21 +58,34 @@ router.post('/',
|
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
const { botId } = req.params;
|
|
49
|
-
const { name } = req.body;
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
const { name, graphJson, variables, isEnabled = true } = req.body;
|
|
62
|
+
|
|
63
|
+
let graphJsonString;
|
|
64
|
+
if (graphJson) {
|
|
65
|
+
if (typeof graphJson === 'string') {
|
|
66
|
+
graphJsonString = graphJson;
|
|
67
|
+
} else {
|
|
68
|
+
graphJsonString = JSON.stringify(graphJson);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
graphJsonString = JSON.stringify({
|
|
52
72
|
nodes: [],
|
|
53
73
|
connections: []
|
|
54
|
-
|
|
74
|
+
});
|
|
75
|
+
}
|
|
55
76
|
|
|
56
77
|
try {
|
|
57
78
|
const newGraph = await prisma.eventGraph.create({
|
|
58
79
|
data: {
|
|
59
80
|
botId: parseInt(botId),
|
|
60
|
-
name,
|
|
61
|
-
|
|
81
|
+
name: name.trim(),
|
|
82
|
+
isEnabled: isEnabled,
|
|
83
|
+
graphJson: graphJsonString,
|
|
84
|
+
variables: variables || '[]',
|
|
85
|
+
pluginOwnerId: req.body.pluginOwnerId || null
|
|
62
86
|
}
|
|
63
87
|
});
|
|
88
|
+
|
|
64
89
|
res.status(201).json(newGraph);
|
|
65
90
|
} catch (error) {
|
|
66
91
|
if (error.code === 'P2002') {
|
|
@@ -84,8 +109,20 @@ router.get('/:graphId',
|
|
|
84
109
|
const { graphId } = req.params;
|
|
85
110
|
const eventGraph = await prisma.eventGraph.findUnique({
|
|
86
111
|
where: { id: parseInt(graphId) },
|
|
87
|
-
include: {
|
|
112
|
+
include: {
|
|
113
|
+
triggers: true,
|
|
114
|
+
pluginOwner: {
|
|
115
|
+
select: {
|
|
116
|
+
id: true,
|
|
117
|
+
name: true,
|
|
118
|
+
version: true,
|
|
119
|
+
sourceType: true
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
88
123
|
});
|
|
124
|
+
|
|
125
|
+
|
|
89
126
|
if (!eventGraph) {
|
|
90
127
|
return res.status(404).json({ error: 'Event graph not found' });
|
|
91
128
|
}
|
|
@@ -109,12 +146,15 @@ router.put('/:graphId',
|
|
|
109
146
|
}
|
|
110
147
|
|
|
111
148
|
const { graphId } = req.params;
|
|
112
|
-
const { name, isEnabled, graphJson, variables } = req.body;
|
|
149
|
+
const { name, isEnabled, graphJson, variables, pluginOwnerId } = req.body;
|
|
150
|
+
|
|
151
|
+
|
|
113
152
|
|
|
114
153
|
try {
|
|
115
154
|
const dataToUpdate = {};
|
|
116
155
|
if (name !== undefined) dataToUpdate.name = name;
|
|
117
156
|
if (isEnabled !== undefined) dataToUpdate.isEnabled = isEnabled;
|
|
157
|
+
if (pluginOwnerId !== undefined) dataToUpdate.pluginOwnerId = pluginOwnerId;
|
|
118
158
|
|
|
119
159
|
if (graphJson !== undefined) {
|
|
120
160
|
dataToUpdate.graphJson = graphJson;
|
|
@@ -149,6 +189,25 @@ router.put('/:graphId',
|
|
|
149
189
|
include: { triggers: true },
|
|
150
190
|
});
|
|
151
191
|
|
|
192
|
+
if (graphJson && updatedGraph.pluginOwnerId) {
|
|
193
|
+
try {
|
|
194
|
+
const plugin = await prisma.installedPlugin.findUnique({
|
|
195
|
+
where: { id: updatedGraph.pluginOwnerId }
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (plugin) {
|
|
199
|
+
const graphDir = path.join(plugin.path, 'graph');
|
|
200
|
+
await fse.mkdir(graphDir, { recursive: true });
|
|
201
|
+
|
|
202
|
+
const graphFile = path.join(graphDir, `${updatedGraph.name}.json`);
|
|
203
|
+
await fse.writeJson(graphFile, JSON.parse(graphJson), { spaces: 2 });
|
|
204
|
+
console.log(`[API] Граф события ${updatedGraph.name} сохранен в ${graphFile}`);
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error(`[API] Ошибка сохранения графа события в папку плагина:`, error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
152
211
|
res.json(updatedGraph);
|
|
153
212
|
} catch (error) {
|
|
154
213
|
logger.error(error, "--- ERROR CAUGHT IN EVENT GRAPH UPDATE ---");
|
|
@@ -64,6 +64,89 @@ router.post('/:id/clear-data', authenticate, authorize('plugin:settings:edit'),
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
router.get('/:id/info', authenticate, authorize('plugin:list'), async (req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const pluginId = parseInt(req.params.id);
|
|
70
|
+
|
|
71
|
+
const plugin = await prisma.installedPlugin.findUnique({
|
|
72
|
+
where: { id: pluginId },
|
|
73
|
+
include: {
|
|
74
|
+
commands: {
|
|
75
|
+
select: {
|
|
76
|
+
id: true,
|
|
77
|
+
name: true,
|
|
78
|
+
description: true,
|
|
79
|
+
isEnabled: true,
|
|
80
|
+
isVisual: true,
|
|
81
|
+
owner: true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
eventGraphs: {
|
|
85
|
+
select: {
|
|
86
|
+
id: true,
|
|
87
|
+
name: true,
|
|
88
|
+
isEnabled: true,
|
|
89
|
+
createdAt: true,
|
|
90
|
+
updatedAt: true
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!plugin) {
|
|
97
|
+
return res.status(404).json({ error: 'Плагин не найден.' });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
res.json(plugin);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`[API Error] /plugins/:id/info:`, error);
|
|
103
|
+
res.status(500).json({ error: 'Не удалось получить информацию о плагине.' });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
router.get('/bot/:botId', authenticate, authorize('plugin:list'), async (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const botId = parseInt(req.params.botId);
|
|
110
|
+
|
|
111
|
+
const plugins = await prisma.installedPlugin.findMany({
|
|
112
|
+
where: { botId },
|
|
113
|
+
select: {
|
|
114
|
+
id: true,
|
|
115
|
+
name: true,
|
|
116
|
+
version: true,
|
|
117
|
+
description: true,
|
|
118
|
+
sourceType: true,
|
|
119
|
+
isEnabled: true,
|
|
120
|
+
commands: {
|
|
121
|
+
select: {
|
|
122
|
+
id: true,
|
|
123
|
+
name: true,
|
|
124
|
+
description: true,
|
|
125
|
+
isEnabled: true,
|
|
126
|
+
isVisual: true,
|
|
127
|
+
owner: true
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
eventGraphs: {
|
|
131
|
+
select: {
|
|
132
|
+
id: true,
|
|
133
|
+
name: true,
|
|
134
|
+
isEnabled: true,
|
|
135
|
+
createdAt: true,
|
|
136
|
+
updatedAt: true
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
orderBy: { name: 'asc' }
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
res.json(plugins);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(`[API Error] /plugins/bot/:botId:`, error);
|
|
146
|
+
res.status(500).json({ error: 'Не удалось получить список плагинов.' });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
67
150
|
router.get('/catalog/:name', async (req, res) => {
|
|
68
151
|
try {
|
|
69
152
|
const pluginName = req.params.name;
|
|
@@ -253,6 +253,20 @@ process.on('message', async (message) => {
|
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
let pluginOwnerId = null;
|
|
257
|
+
if (command.owner && command.owner.startsWith('plugin:')) {
|
|
258
|
+
const pluginName = command.owner.replace('plugin:', '');
|
|
259
|
+
const plugin = await prisma.installedPlugin.findFirst({
|
|
260
|
+
where: {
|
|
261
|
+
botId: bot.config.id,
|
|
262
|
+
name: pluginName
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
if (plugin) {
|
|
266
|
+
pluginOwnerId = plugin.id;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
256
270
|
const commandData = {
|
|
257
271
|
botId: bot.config.id,
|
|
258
272
|
name: command.name,
|
|
@@ -264,6 +278,7 @@ process.on('message', async (message) => {
|
|
|
264
278
|
aliases: JSON.stringify(command.aliases || []),
|
|
265
279
|
allowedChatTypes: JSON.stringify(command.allowedChatTypes || ['chat', 'private']),
|
|
266
280
|
argumentsJson: JSON.stringify(command.args || []),
|
|
281
|
+
pluginOwnerId: pluginOwnerId,
|
|
267
282
|
};
|
|
268
283
|
|
|
269
284
|
await prisma.command.upsert({
|
|
@@ -339,6 +354,69 @@ process.on('message', async (message) => {
|
|
|
339
354
|
}, 10000);
|
|
340
355
|
});
|
|
341
356
|
},
|
|
357
|
+
registerEventGraph: async (graphData) => {
|
|
358
|
+
try {
|
|
359
|
+
let pluginOwnerId = null;
|
|
360
|
+
if (graphData.owner && graphData.owner.startsWith('plugin:')) {
|
|
361
|
+
const pluginName = graphData.owner.replace('plugin:', '');
|
|
362
|
+
const plugin = await prisma.installedPlugin.findFirst({
|
|
363
|
+
where: {
|
|
364
|
+
botId: bot.config.id,
|
|
365
|
+
name: pluginName
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
if (plugin) {
|
|
369
|
+
pluginOwnerId = plugin.id;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const graphDataToSave = {
|
|
374
|
+
botId: bot.config.id,
|
|
375
|
+
name: graphData.name,
|
|
376
|
+
isEnabled: graphData.isEnabled !== undefined ? graphData.isEnabled : true,
|
|
377
|
+
graphJson: graphData.graphJson || JSON.stringify({ nodes: [], connections: [] }),
|
|
378
|
+
variables: JSON.stringify(graphData.variables || []),
|
|
379
|
+
pluginOwnerId: pluginOwnerId,
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const eventGraph = await prisma.eventGraph.upsert({
|
|
383
|
+
where: {
|
|
384
|
+
botId_name: {
|
|
385
|
+
botId: bot.config.id,
|
|
386
|
+
name: graphData.name
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
update: {
|
|
390
|
+
isEnabled: graphDataToSave.isEnabled,
|
|
391
|
+
graphJson: graphDataToSave.graphJson,
|
|
392
|
+
variables: graphDataToSave.variables,
|
|
393
|
+
pluginOwnerId: graphDataToSave.pluginOwnerId,
|
|
394
|
+
},
|
|
395
|
+
create: graphDataToSave,
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
if (graphData.triggers && Array.isArray(graphData.triggers)) {
|
|
399
|
+
await prisma.eventTrigger.deleteMany({
|
|
400
|
+
where: { graphId: eventGraph.id }
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
if (graphData.triggers.length > 0) {
|
|
404
|
+
await prisma.eventTrigger.createMany({
|
|
405
|
+
data: graphData.triggers.map(eventType => ({
|
|
406
|
+
graphId: eventGraph.id,
|
|
407
|
+
eventType
|
|
408
|
+
}))
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
sendLog(`[API] Граф события "${graphData.name}" от плагина "${graphData.owner}" зарегистрирован.`);
|
|
414
|
+
return eventGraph;
|
|
415
|
+
} catch (error) {
|
|
416
|
+
sendLog(`[API] Ошибка при регистрации графа события: ${error.message}`);
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
},
|
|
342
420
|
executeCommand: (command) => {
|
|
343
421
|
sendLog(`[Graph] Выполнение серверной команды: ${command}`);
|
|
344
422
|
bot.chat(command);
|
|
@@ -676,7 +754,13 @@ process.on('message', async (message) => {
|
|
|
676
754
|
process.on('unhandledRejection', (reason, promise) => {
|
|
677
755
|
const errorMsg = `[FATAL] Необработанная ошибка процесса: ${reason?.stack || reason}`;
|
|
678
756
|
sendLog(errorMsg);
|
|
679
|
-
process.exit(1);
|
|
757
|
+
setTimeout(() => process.exit(1), 100);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
process.on('uncaughtException', (error) => {
|
|
761
|
+
const errorMsg = `[FATAL] Необработанное исключение: ${error.stack || error.message}`;
|
|
762
|
+
sendLog(errorMsg);
|
|
763
|
+
setTimeout(() => process.exit(1), 100);
|
|
680
764
|
});
|
|
681
765
|
|
|
682
766
|
process.on('SIGTERM', () => {
|
|
@@ -688,14 +772,26 @@ process.on('SIGTERM', () => {
|
|
|
688
772
|
sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
|
|
689
773
|
}
|
|
690
774
|
}
|
|
691
|
-
process.exit(0);
|
|
775
|
+
setTimeout(() => process.exit(0), 100);
|
|
692
776
|
});
|
|
693
777
|
|
|
694
|
-
process.on('
|
|
695
|
-
sendLog('[System] Получен сигнал
|
|
696
|
-
|
|
778
|
+
process.on('SIGINT', () => {
|
|
779
|
+
sendLog('[System] Получен сигнал SIGINT. Завершение работы...');
|
|
780
|
+
if (bot) {
|
|
781
|
+
try {
|
|
782
|
+
bot.quit();
|
|
783
|
+
} catch (error) {
|
|
784
|
+
sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
setTimeout(() => process.exit(0), 100);
|
|
697
788
|
});
|
|
698
789
|
|
|
790
|
+
// process.on('SIGKILL', () => {
|
|
791
|
+
// sendLog('[System] Получен сигнал SIGKILL. Принудительное завершение...');
|
|
792
|
+
// process.exit(0);
|
|
793
|
+
// });
|
|
794
|
+
|
|
699
795
|
function serializeEntity(entity) {
|
|
700
796
|
if (!entity) return null;
|
|
701
797
|
return {
|
|
@@ -266,6 +266,41 @@ class GraphExecutionEngine {
|
|
|
266
266
|
await this.traverse(node, 'exec');
|
|
267
267
|
break;
|
|
268
268
|
}
|
|
269
|
+
case 'array:get_random_element': {
|
|
270
|
+
await this.traverse(node, 'element');
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'array:add_element':
|
|
274
|
+
case 'array:remove_by_index':
|
|
275
|
+
case 'array:get_by_index':
|
|
276
|
+
case 'array:find_index':
|
|
277
|
+
case 'array:contains': {
|
|
278
|
+
await this.traverse(node, 'result');
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case 'data:array_literal':
|
|
282
|
+
case 'data:make_object':
|
|
283
|
+
case 'data:get_variable':
|
|
284
|
+
case 'data:get_argument':
|
|
285
|
+
case 'data:length':
|
|
286
|
+
case 'data:get_entity_field':
|
|
287
|
+
case 'data:cast':
|
|
288
|
+
case 'data:string_literal':
|
|
289
|
+
case 'data:get_user_field':
|
|
290
|
+
case 'data:get_server_players':
|
|
291
|
+
case 'data:get_bot_look':
|
|
292
|
+
case 'math:operation':
|
|
293
|
+
case 'math:random_number':
|
|
294
|
+
case 'logic:operation':
|
|
295
|
+
case 'string:concat':
|
|
296
|
+
case 'object:create':
|
|
297
|
+
case 'object:get':
|
|
298
|
+
case 'object:set':
|
|
299
|
+
case 'object:delete':
|
|
300
|
+
case 'object:has_key': {
|
|
301
|
+
await this.traverse(node, 'value');
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
269
304
|
}
|
|
270
305
|
}
|
|
271
306
|
|
|
@@ -535,7 +570,10 @@ class GraphExecutionEngine {
|
|
|
535
570
|
const numPins = node.data?.pinCount || 0;
|
|
536
571
|
const items = [];
|
|
537
572
|
for (let i = 0; i < numPins; i++) {
|
|
538
|
-
|
|
573
|
+
const value = await this.resolvePinValue(node, `pin_${i}`) ||
|
|
574
|
+
node.data?.[`item_${i}`] ||
|
|
575
|
+
node.data?.[`value_${i}`];
|
|
576
|
+
items.push(value);
|
|
539
577
|
}
|
|
540
578
|
result = items;
|
|
541
579
|
break;
|