blockmine 1.6.0 → 1.6.2
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/README.md +1 -0
- package/backend/src/api/routes/pluginIde.js +149 -25
- package/backend/src/core/BotProcess.js +6 -46
- package/backend/src/core/EventGraphManager.js +3 -0
- package/backend/src/core/GraphExecutionEngine.js +7 -1
- package/backend/src/core/NodeRegistry.js +17 -1
- package/backend/src/core/PluginManager.js +281 -281
- package/frontend/dist/assets/index-BHk5N684.css +1 -0
- package/frontend/dist/assets/{index-CVKjf72r.js → index-CmhNg8XO.js} +502 -502
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-4mfeN9LH.css +0 -1
package/README.md
CHANGED
|
@@ -21,18 +21,25 @@ const resolvePluginPath = async (req, res, next) => {
|
|
|
21
21
|
return res.status(400).json({ error: 'Имя плагина обязательно в пути.' });
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const plugin = await prisma.installedPlugin.findFirst({
|
|
25
|
+
where: {
|
|
26
|
+
botId: parseInt(botId),
|
|
27
|
+
name: pluginName
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!plugin) {
|
|
32
|
+
return res.status(404).json({ error: 'Плагин не найден в базе данных.' });
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
const pluginPath = plugin.path;
|
|
36
|
+
|
|
31
37
|
if (!await fse.pathExists(pluginPath)) {
|
|
32
|
-
return res.status(404).json({ error: 'Директория плагина не
|
|
38
|
+
return res.status(404).json({ error: 'Директория плагина не найдена в файловой системе.' });
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
req.pluginPath = pluginPath;
|
|
42
|
+
req.pluginData = plugin;
|
|
36
43
|
next();
|
|
37
44
|
} catch (error) {
|
|
38
45
|
console.error('[Plugin IDE Middleware Error]', error);
|
|
@@ -297,19 +304,48 @@ router.post('/:pluginName/file', resolvePluginPath, async (req, res) => {
|
|
|
297
304
|
if (relativePath === 'package.json' || relativePath.endsWith('/package.json')) {
|
|
298
305
|
try {
|
|
299
306
|
const packageJson = JSON.parse(content);
|
|
300
|
-
|
|
307
|
+
|
|
308
|
+
const existingPlugin = await prisma.installedPlugin.findFirst({
|
|
301
309
|
where: {
|
|
302
310
|
botId: parseInt(req.params.botId),
|
|
303
311
|
path: req.pluginPath,
|
|
304
|
-
},
|
|
305
|
-
data: {
|
|
306
|
-
name: packageJson.name || req.params.pluginName,
|
|
307
|
-
version: packageJson.version || '1.0.0',
|
|
308
|
-
description: packageJson.description || '',
|
|
309
|
-
manifest: JSON.stringify(packageJson.botpanel || {}),
|
|
310
312
|
}
|
|
311
313
|
});
|
|
312
|
-
|
|
314
|
+
|
|
315
|
+
if (existingPlugin) {
|
|
316
|
+
const newName = packageJson.name || req.params.pluginName;
|
|
317
|
+
|
|
318
|
+
const conflictingPlugin = await prisma.installedPlugin.findFirst({
|
|
319
|
+
where: {
|
|
320
|
+
botId: parseInt(req.params.botId),
|
|
321
|
+
name: newName,
|
|
322
|
+
id: { not: existingPlugin.id }
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (conflictingPlugin) {
|
|
327
|
+
console.warn(`[Plugin IDE] Конфликт имени плагина: ${newName} уже существует для бота ${req.params.botId}`);
|
|
328
|
+
await prisma.installedPlugin.update({
|
|
329
|
+
where: { id: existingPlugin.id },
|
|
330
|
+
data: {
|
|
331
|
+
version: packageJson.version || '1.0.0',
|
|
332
|
+
description: packageJson.description || '',
|
|
333
|
+
manifest: JSON.stringify(packageJson.botpanel || {}),
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
} else {
|
|
337
|
+
await prisma.installedPlugin.update({
|
|
338
|
+
where: { id: existingPlugin.id },
|
|
339
|
+
data: {
|
|
340
|
+
name: newName,
|
|
341
|
+
version: packageJson.version || '1.0.0',
|
|
342
|
+
description: packageJson.description || '',
|
|
343
|
+
manifest: JSON.stringify(packageJson.botpanel || {}),
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
console.log(`[Plugin IDE] Manifest обновлен для плагина ${req.params.pluginName} после сохранения package.json`);
|
|
348
|
+
}
|
|
313
349
|
} catch (manifestError) {
|
|
314
350
|
console.error(`[Plugin IDE] Ошибка обновления manifest для ${req.params.pluginName}:`, manifestError);
|
|
315
351
|
}
|
|
@@ -366,6 +402,33 @@ router.post('/:pluginName/fs', resolvePluginPath, async (req, res) => {
|
|
|
366
402
|
res.status(200).json({ message: 'Renamed successfully.' });
|
|
367
403
|
break;
|
|
368
404
|
|
|
405
|
+
case 'move':
|
|
406
|
+
if (!newPath) return res.status(400).json({ error: 'New path is required for move operation.' });
|
|
407
|
+
const safeMoveNewPath = path.resolve(req.pluginPath, newPath);
|
|
408
|
+
if (!safeMoveNewPath.startsWith(req.pluginPath)) return res.status(403).json({ error: 'Access denied: New path is outside of plugin directory.' });
|
|
409
|
+
if (!await fse.pathExists(safePath)) return res.status(404).json({ error: 'Source file or folder not found.' });
|
|
410
|
+
|
|
411
|
+
if (safeMoveNewPath.startsWith(safePath + path.sep)) {
|
|
412
|
+
return res.status(400).json({ error: 'Cannot move folder into itself.' });
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
let finalPath = safeMoveNewPath;
|
|
416
|
+
let counter = 1;
|
|
417
|
+
while (await fse.pathExists(finalPath)) {
|
|
418
|
+
const ext = path.extname(safeMoveNewPath);
|
|
419
|
+
const base = path.basename(safeMoveNewPath, ext);
|
|
420
|
+
const dir = path.dirname(safeMoveNewPath);
|
|
421
|
+
finalPath = path.join(dir, `${base} (${counter})${ext}`);
|
|
422
|
+
counter++;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
await fse.move(safePath, finalPath);
|
|
426
|
+
res.status(200).json({
|
|
427
|
+
message: 'Moved successfully.',
|
|
428
|
+
newPath: path.relative(req.pluginPath, finalPath)
|
|
429
|
+
});
|
|
430
|
+
break;
|
|
431
|
+
|
|
369
432
|
default:
|
|
370
433
|
res.status(400).json({ error: 'Invalid operation specified.' });
|
|
371
434
|
}
|
|
@@ -412,23 +475,50 @@ router.post('/:pluginName/manifest', resolvePluginPath, async (req, res) => {
|
|
|
412
475
|
|
|
413
476
|
await fse.writeJson(manifestPath, newManifest, { spaces: 2 });
|
|
414
477
|
|
|
415
|
-
await prisma.installedPlugin.
|
|
478
|
+
const existingPlugin = await prisma.installedPlugin.findFirst({
|
|
416
479
|
where: {
|
|
417
480
|
botId: parseInt(req.params.botId),
|
|
418
481
|
path: req.pluginPath,
|
|
419
|
-
},
|
|
420
|
-
data: {
|
|
421
|
-
name: newManifest.name,
|
|
422
|
-
version: newManifest.version,
|
|
423
|
-
description: newManifest.description,
|
|
424
|
-
manifest: JSON.stringify(newManifest.botpanel || {}),
|
|
425
482
|
}
|
|
426
483
|
});
|
|
484
|
+
|
|
485
|
+
if (existingPlugin) {
|
|
486
|
+
const conflictingPlugin = await prisma.installedPlugin.findFirst({
|
|
487
|
+
where: {
|
|
488
|
+
botId: parseInt(req.params.botId),
|
|
489
|
+
name: newManifest.name,
|
|
490
|
+
id: { not: existingPlugin.id }
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
if (conflictingPlugin) {
|
|
495
|
+
console.warn(`[Plugin IDE] Конфликт имени плагина: ${newManifest.name} уже существует для бота ${req.params.botId}`);
|
|
496
|
+
await prisma.installedPlugin.update({
|
|
497
|
+
where: { id: existingPlugin.id },
|
|
498
|
+
data: {
|
|
499
|
+
version: newManifest.version,
|
|
500
|
+
description: newManifest.description,
|
|
501
|
+
manifest: JSON.stringify(newManifest.botpanel || {}),
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
} else {
|
|
505
|
+
await prisma.installedPlugin.update({
|
|
506
|
+
where: { id: existingPlugin.id },
|
|
507
|
+
data: {
|
|
508
|
+
name: newManifest.name,
|
|
509
|
+
version: newManifest.version,
|
|
510
|
+
description: newManifest.description,
|
|
511
|
+
manifest: JSON.stringify(newManifest.botpanel || {}),
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
427
516
|
|
|
428
517
|
res.status(200).json({ message: 'package.json успешно обновлен.' });
|
|
429
518
|
} catch (error) {
|
|
430
519
|
console.error(`[Plugin IDE Error] /manifest POST for ${req.params.pluginName}:`, error);
|
|
431
|
-
|
|
520
|
+
// Файл уже сохранен, поэтому возвращаем успех даже если есть ошибка с БД
|
|
521
|
+
res.status(200).json({ message: 'package.json обновлен (возможны проблемы с синхронизацией БД).' });
|
|
432
522
|
}
|
|
433
523
|
});
|
|
434
524
|
|
|
@@ -543,7 +633,26 @@ router.post('/:pluginName/create-pr', resolvePluginPath, async (req, res) => {
|
|
|
543
633
|
|
|
544
634
|
process.chdir(tempDir);
|
|
545
635
|
|
|
546
|
-
|
|
636
|
+
let branchExists = false;
|
|
637
|
+
try {
|
|
638
|
+
cp.execSync(`git checkout -b ${branch}`, { stdio: 'pipe' });
|
|
639
|
+
console.log(`[Plugin IDE] Создана новая ветка ${branch}`);
|
|
640
|
+
} catch (e) {
|
|
641
|
+
try {
|
|
642
|
+
cp.execSync(`git checkout ${branch}`, { stdio: 'pipe' });
|
|
643
|
+
console.log(`[Plugin IDE] Переключились на существующую ветку ${branch}`);
|
|
644
|
+
} catch (e2) {
|
|
645
|
+
try {
|
|
646
|
+
cp.execSync(`git fetch origin ${branch}`, { stdio: 'pipe' });
|
|
647
|
+
cp.execSync(`git checkout -b ${branch} origin/${branch}`, { stdio: 'pipe' });
|
|
648
|
+
branchExists = true;
|
|
649
|
+
console.log(`[Plugin IDE] Создана ветка ${branch} из удаленной`);
|
|
650
|
+
} catch (e3) {
|
|
651
|
+
cp.execSync(`git checkout -B ${branch}`, { stdio: 'pipe' });
|
|
652
|
+
console.log(`[Plugin IDE] Принудительно создана ветка ${branch}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
547
656
|
|
|
548
657
|
const files = await fse.readdir(req.pluginPath);
|
|
549
658
|
for (const file of files) {
|
|
@@ -564,11 +673,26 @@ router.post('/:pluginName/create-pr', resolvePluginPath, async (req, res) => {
|
|
|
564
673
|
throw e;
|
|
565
674
|
}
|
|
566
675
|
|
|
567
|
-
|
|
676
|
+
|
|
677
|
+
if (branchExists) {
|
|
678
|
+
cp.execSync(`git push origin ${branch} --force`);
|
|
679
|
+
console.log(`[Plugin IDE] Ветка ${branch} обновлена`);
|
|
680
|
+
} else {
|
|
681
|
+
cp.execSync(`git push -u origin ${branch}`);
|
|
682
|
+
console.log(`[Plugin IDE] Новая ветка ${branch} создана`);
|
|
683
|
+
}
|
|
568
684
|
|
|
569
685
|
const prUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}/pull/new/${branch}`;
|
|
570
686
|
|
|
571
|
-
|
|
687
|
+
const responseData = {
|
|
688
|
+
success: true,
|
|
689
|
+
prUrl: prUrl,
|
|
690
|
+
isUpdate: branchExists,
|
|
691
|
+
message: branchExists ? 'Существующий PR обновлен' : 'Новый PR создан'
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
console.log(`[Plugin IDE] PR ${branchExists ? 'обновлен' : 'создан'} для плагина ${req.params.pluginName}:`, responseData);
|
|
695
|
+
res.json(responseData);
|
|
572
696
|
|
|
573
697
|
} finally {
|
|
574
698
|
try {
|
|
@@ -12,7 +12,7 @@ const { parseArguments } = require('./system/parseArguments');
|
|
|
12
12
|
const GraphExecutionEngine = require('./GraphExecutionEngine');
|
|
13
13
|
const NodeRegistry = require('./NodeRegistry');
|
|
14
14
|
|
|
15
|
-
const UserService = require('./
|
|
15
|
+
const UserService = require('./UserService');
|
|
16
16
|
const PermissionManager = require('./ipc/PermissionManager.stub.js');
|
|
17
17
|
|
|
18
18
|
let bot = null;
|
|
@@ -208,51 +208,7 @@ process.on('message', async (message) => {
|
|
|
208
208
|
sendMessage: (type, message, username) => bot.messageQueue.enqueue(type, message, username),
|
|
209
209
|
sendMessageAndWaitForReply: (command, patterns, timeout) => bot.messageQueue.enqueueAndWait(command, patterns, timeout),
|
|
210
210
|
getUser: async (username) => {
|
|
211
|
-
|
|
212
|
-
if (!userData) return null;
|
|
213
|
-
|
|
214
|
-
const permissions = userData.permissionsSet ? Array.from(userData.permissionsSet) : [];
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
id: userData.id,
|
|
218
|
-
username: userData.username,
|
|
219
|
-
isOwner: userData.isOwner,
|
|
220
|
-
isBlacklisted: userData.isBlacklisted,
|
|
221
|
-
permissions: permissions,
|
|
222
|
-
groups: userData.groups,
|
|
223
|
-
hasPermission: (permissionName) => {
|
|
224
|
-
if (userData.isOwner) return true;
|
|
225
|
-
if (!permissionName) return false;
|
|
226
|
-
|
|
227
|
-
if (permissions.includes(permissionName)) {
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const permissionParts = permissionName.split('.');
|
|
232
|
-
if (permissionParts.length > 1) {
|
|
233
|
-
const domain = permissionParts[0];
|
|
234
|
-
const wildcard = `${domain}.*`;
|
|
235
|
-
if (permissions.includes(wildcard)) {
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (permissions.includes('*')) {
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return false;
|
|
245
|
-
},
|
|
246
|
-
hasGroup: (groupName) => userData.hasGroup(groupName),
|
|
247
|
-
addGroup: (group) => bot.api.performUserAction(username, 'addGroup', { group }),
|
|
248
|
-
removeGroup: (group) => bot.api.performUserAction(username, 'removeGroup', { group }),
|
|
249
|
-
addPermission: (permission) => bot.api.performUserAction(username, 'addPermission', { permission }),
|
|
250
|
-
removePermission: (permission) => bot.api.performUserAction(username, 'removePermission', { permission }),
|
|
251
|
-
getGroups: () => bot.api.performUserAction(username, 'getGroups'),
|
|
252
|
-
getPermissions: () => bot.api.performUserAction(username, 'getPermissions'),
|
|
253
|
-
isBlacklisted: () => bot.api.performUserAction(username, 'isBlacklisted'),
|
|
254
|
-
setBlacklisted: (value) => bot.api.performUserAction(username, 'setBlacklisted', { value }),
|
|
255
|
-
};
|
|
211
|
+
return await UserService.getUser(username, bot.config.id, bot.config);
|
|
256
212
|
},
|
|
257
213
|
registerPermissions: (permissions) => PermissionManager.registerPermissions(bot.config.id, permissions),
|
|
258
214
|
registerGroup: (groupConfig) => PermissionManager.registerGroup(bot.config.id, groupConfig),
|
|
@@ -464,6 +420,10 @@ process.on('message', async (message) => {
|
|
|
464
420
|
messageHandledByCustomParser = false;
|
|
465
421
|
const rawMessageText = jsonMsg.toString();
|
|
466
422
|
bot.events.emit('core:raw_message', rawMessageText, jsonMsg);
|
|
423
|
+
|
|
424
|
+
sendEvent('raw_message', {
|
|
425
|
+
rawText: rawMessageText
|
|
426
|
+
});
|
|
467
427
|
});
|
|
468
428
|
|
|
469
429
|
bot.events.on('chat:message', (data) => {
|
|
@@ -145,6 +145,9 @@ class EventGraphManager {
|
|
|
145
145
|
context.message = args.message;
|
|
146
146
|
context.chat_type = args.chatType;
|
|
147
147
|
break;
|
|
148
|
+
case 'raw_message':
|
|
149
|
+
context.rawText = args.rawText;
|
|
150
|
+
break;
|
|
148
151
|
case 'playerJoined':
|
|
149
152
|
case 'playerLeft':
|
|
150
153
|
context.user = args.user;
|
|
@@ -379,7 +379,13 @@ class GraphExecutionEngine {
|
|
|
379
379
|
else result = this.context[pinId];
|
|
380
380
|
break;
|
|
381
381
|
case 'event:chat':
|
|
382
|
-
if (pinId === '
|
|
382
|
+
if (pinId === 'username') result = this.context.username;
|
|
383
|
+
else if (pinId === 'message') result = this.context.message;
|
|
384
|
+
else if (pinId === 'chatType') result = this.context.chat_type;
|
|
385
|
+
else result = this.context[pinId];
|
|
386
|
+
break;
|
|
387
|
+
case 'event:raw_message':
|
|
388
|
+
if (pinId === 'rawText') result = this.context.rawText;
|
|
383
389
|
else result = this.context[pinId];
|
|
384
390
|
break;
|
|
385
391
|
case 'event:playerJoined':
|
|
@@ -129,7 +129,23 @@ class NodeRegistry {
|
|
|
129
129
|
{ id: 'username', type: 'String', name: 'Игрок' },
|
|
130
130
|
{ id: 'message', type: 'String', name: 'Сообщение' },
|
|
131
131
|
{ id: 'chatType', type: 'String', name: 'Тип чата' },
|
|
132
|
-
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
this.registerNodeType({
|
|
137
|
+
type: 'event:raw_message',
|
|
138
|
+
name: 'Событие: Сырое сообщение',
|
|
139
|
+
label: '📝 Сырое сообщение',
|
|
140
|
+
description: 'Срабатывает при получении любого сообщения в сыром виде (до парсинга).',
|
|
141
|
+
category: 'События',
|
|
142
|
+
graphType: event,
|
|
143
|
+
isEvent: true,
|
|
144
|
+
pins: {
|
|
145
|
+
inputs: [],
|
|
146
|
+
outputs: [
|
|
147
|
+
{ id: 'exec', type: 'Exec', name: 'Выполнить' },
|
|
148
|
+
{ id: 'rawText', type: 'String', name: 'Сырой текст' },
|
|
133
149
|
]
|
|
134
150
|
}
|
|
135
151
|
});
|