blockmine 1.17.1 → 1.18.1

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 CHANGED
@@ -1,6 +1,23 @@
1
1
  # История версий
2
2
 
3
3
 
4
+ ### [1.18.1](https://github.com/blockmineJS/blockmine/compare/v1.18.0...v1.18.1) (2025-08-03)
5
+
6
+
7
+ ### 🐛 Исправления
8
+
9
+ * добавлен механизм перезапуска ботов при некоторых противных ошибках ([2eb34e3](https://github.com/blockmineJS/blockmine/commit/2eb34e3e63aaa72e5fb641d7ab155baa92dbde63))
10
+ * копирование кода/графов и всё что дает возможность скопировать в буфер обмена, починено ([dbe4ee6](https://github.com/blockmineJS/blockmine/commit/dbe4ee6bb2e19d99cfaaef33130c27803d5988a8))
11
+ * мелкие фиксы крон паттерна у планировщика. больше логов для наблюдений ([c33e7c8](https://github.com/blockmineJS/blockmine/commit/c33e7c895b3daf60bcde187cf1334ab38bb71592))
12
+
13
+ ## [1.18.0](https://github.com/blockmineJS/blockmine/compare/v1.17.1...v1.18.0) (2025-07-26)
14
+
15
+
16
+ ### ✨ Новые возможности
17
+
18
+ * добавлена новая кнопка - "Предложить улучшение". Поможет адептам составить свой запрос ([d741881](https://github.com/blockmineJS/blockmine/commit/d7418813e53d15fcd16c0517cea033d019ed355b))
19
+ * добавлено глубокое объединение настроек для плагинов и улучшена установка зависимостей ([452af4b](https://github.com/blockmineJS/blockmine/commit/452af4b67325f3faebe12c136a40f77515e805c0))
20
+
4
21
  ### [1.17.1](https://github.com/blockmineJS/blockmine/compare/v1.17.0...v1.17.1) (2025-07-24)
5
22
 
6
23
 
@@ -1,27 +1,27 @@
1
- {
2
- "name": "backend",
3
- "version": "1.0.0",
4
- "description": "",
5
- "main": "src/server.js",
6
- "scripts": {
7
- "start": "node ../backend/cli.js",
8
- "dev": "cross-env NODE_ENV=development nodemon",
9
- "test": "echo \"Error: no test specified\" && exit 1"
10
- },
11
- "prisma": {
12
- "seed": "node prisma/seed.js"
13
- },
14
- "keywords": [],
15
- "author": "",
16
- "license": "ISC",
17
- "devDependencies": {
18
- "nodemon": "^3.1.2",
19
- "cross-env": "^7.0.3"
20
- },
21
- "dependencies": {
22
- "express-validator": "^7.2.1",
23
-
24
- "pino": "^9.7.0",
25
- "pino-pretty": "^13.0.0"
26
- }
27
- }
1
+ {
2
+ "name": "backend",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "src/server.js",
6
+ "scripts": {
7
+ "start": "node ../backend/cli.js",
8
+ "dev": "cross-env NODE_ENV=development nodemon",
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "prisma": {
12
+ "seed": "node prisma/seed.js"
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "devDependencies": {
18
+ "nodemon": "^3.1.2",
19
+ "cross-env": "^7.0.3"
20
+ },
21
+ "dependencies": {
22
+ "express-validator": "^7.2.1",
23
+
24
+ "pino": "^9.7.0",
25
+ "pino-pretty": "^13.0.0"
26
+ }
27
+ }
@@ -12,6 +12,7 @@ const { encrypt } = require('../../core/utils/crypto');
12
12
  const { randomUUID } = require('crypto');
13
13
  const eventGraphsRouter = require('./eventGraphs');
14
14
  const pluginIdeRouter = require('./pluginIde');
15
+ const { deepMergeSettings } = require('../../core/utils/settingsMerger');
15
16
 
16
17
  const multer = require('multer');
17
18
  const archiver = require('archiver');
@@ -522,7 +523,7 @@ router.get('/:botId/plugins/:pluginId/settings', authorize('plugin:settings:view
522
523
  }
523
524
  }
524
525
 
525
- const finalSettings = { ...defaultSettings, ...savedSettings };
526
+ const finalSettings = deepMergeSettings(defaultSettings, savedSettings);
526
527
  res.json(finalSettings);
527
528
  } catch (error) {
528
529
  console.error("[API Error] /settings GET:", error);
@@ -969,7 +970,7 @@ router.get('/:id/settings/all', authorize('bot:update'), async (req, res) => {
969
970
  description: plugin.description,
970
971
  isEnabled: plugin.isEnabled,
971
972
  manifest: manifest,
972
- settings: { ...defaultSettings, ...savedSettings }
973
+ settings: deepMergeSettings(defaultSettings, savedSettings)
973
974
  };
974
975
  });
975
976
 
@@ -12,7 +12,8 @@ router.use(authenticate);
12
12
 
13
13
  const normalizeCronPattern = (pattern) => {
14
14
  if (typeof pattern !== 'string') return '* * * * *';
15
- return pattern.replace(/\*\/1/g, '*').trim();
15
+ // Убираем лишние пробелы и нормализуем паттерн
16
+ return pattern.replace(/\*\/1/g, '*').replace(/\s+/g, ' ').trim();
16
17
  };
17
18
 
18
19
  router.get('/', authorize('task:list'), async (req, res) => {
@@ -528,8 +528,18 @@ class BotManager {
528
528
  this.bots.delete(botId);
529
529
  this.resourceUsage.delete(botId);
530
530
  this.botConfigs.delete(botId);
531
+
531
532
  this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
532
533
  this.updateAllResourceUsage();
534
+
535
+ if (code === 1) {
536
+ console.log(`[BotManager] Обнаружена ошибка с кодом 1 для бота ${botId}. Попытка перезапуска через 5 секунд...`);
537
+ this.appendLog(botId, `[SYSTEM] Обнаружена критическая ошибка, перезапуск через 5 секунд...`);
538
+ setTimeout(() => {
539
+ console.log(`[BotManager] Перезапуск бота ${botId}...`);
540
+ this.startBot(botConfig);
541
+ }, 5000);
542
+ }
533
543
  });
534
544
 
535
545
  this.bots.set(botConfig.id, child);
@@ -627,8 +627,11 @@ process.on('message', async (message) => {
627
627
  clearTimeout(connectionTimeout);
628
628
  connectionTimeout = null;
629
629
  }
630
+ const restartableReasons = ['socketClosed', 'keepAliveError'];
631
+ const exitCode = restartableReasons.includes(reason) ? 1 : 0;
632
+
630
633
  sendLog(`[Event: end] Отключен от сервера. Причина: ${reason}`);
631
- process.exit(0);
634
+ process.exit(exitCode);
632
635
  });
633
636
 
634
637
  bot.on('playerJoined', (player) => {
@@ -1,132 +1,87 @@
1
-
2
- const path = require('path');
3
- const fs = require('fs/promises');
4
- const { execSync } = require('child_process');
5
- const fssync = require('fs');
6
- const PluginStore = require('../plugins/PluginStore');
7
-
8
- const projectRoot = path.resolve(__dirname, '..');
9
-
10
- async function ensurePluginDependencies(pluginPath, pluginName) {
11
- const packageJsonPath = path.join(pluginPath, 'package.json');
12
- try {
13
- if (fssync.existsSync(packageJsonPath)) {
14
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
15
- if (packageJson.dependencies && Object.keys(packageJson.dependencies).length > 0) {
16
- console.log(`[PluginLoader] У плагина ${pluginName} есть зависимости, устанавливаем их...`);
17
- const shell = process.platform === 'win32' ? process.env.ComSpec : '/bin/sh';
18
- try {
19
- execSync('npm install', {
20
- cwd: pluginPath,
21
- stdio: 'pipe',
22
- shell: shell
23
- });
24
- console.log(`[PluginLoader] Зависимости для плагина ${pluginName} установлены`);
25
- } catch (installError) {
26
- console.error(`[PluginLoader] Ошибка установки зависимостей для ${pluginName}:`, installError.message);
27
- }
28
- }
29
- }
30
- } catch (error) {
31
- console.error(`[PluginLoader] Ошибка чтения package.json для ${pluginName}:`, error.message);
32
- }
33
- }
34
-
35
- async function initializePlugins(bot, installedPlugins = [], prisma) {
36
- if (!installedPlugins || installedPlugins.length === 0) return;
37
-
38
- const sendLog = bot.sendLog || console.log;
39
- sendLog(`[PluginLoader] Загрузка ${installedPlugins.length} плагинов...`);
40
-
41
- for (const plugin of installedPlugins) {
42
- if (plugin && plugin.path) {
43
- try {
44
- await ensurePluginDependencies(plugin.path, plugin.name);
45
-
46
- const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
47
- const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
48
- const defaultSettings = {};
49
-
50
- if (manifest.settings) {
51
- for (const key in manifest.settings) {
52
- const config = manifest.settings[key];
53
- if (config.type === 'json_file' && config.defaultPath) {
54
- const configFilePath = path.join(plugin.path, config.defaultPath);
55
- try {
56
- const fileContent = await fs.readFile(configFilePath, 'utf-8');
57
- defaultSettings[key] = JSON.parse(fileContent);
58
- } catch (e) {
59
- sendLog(`[PluginLoader] WARN: Не удалось прочитать defaultPath '${config.defaultPath}' для плагина ${plugin.name}.`);
60
- defaultSettings[key] = (config.type === 'string[]' || config.type === 'json_file') ? [] : {};
61
- }
62
- } else {
63
- try {
64
- defaultSettings[key] = JSON.parse(config.default || 'null');
65
- } catch {
66
- defaultSettings[key] = config.default;
67
- }
68
- }
69
- }
70
- }
71
-
72
- const finalSettings = { ...defaultSettings, ...savedSettings };
73
- const store = new PluginStore(prisma, bot.config.id, plugin.name);
74
-
75
- const mainFile = manifest.main || 'index.js';
76
- const entryPointPath = path.join(plugin.path, mainFile);
77
- const normalizedPath = entryPointPath.replace(/\\/g, '/');
78
-
79
- sendLog(`[PluginLoader] Загрузка: ${plugin.name} (v${plugin.version}) из ${normalizedPath}`);
80
-
81
- try {
82
- const pluginModule = require(normalizedPath);
83
-
84
- if (typeof pluginModule === 'function') {
85
- pluginModule(bot, { settings: finalSettings, store });
86
- } else if (pluginModule && typeof pluginModule.onLoad === 'function') {
87
- pluginModule.onLoad(bot, { settings: finalSettings, store });
88
- } else {
89
- sendLog(`[PluginLoader] [ERROR] ${plugin.name} не экспортирует функцию или объект с методом onLoad.`);
90
- }
91
- } catch (error) {
92
- if (error.message.includes('Cannot find module')) {
93
- const moduleMatch = error.message.match(/Cannot find module '([^']+)'/);
94
- if (moduleMatch) {
95
- const missingModule = moduleMatch[1];
96
- sendLog(`[PluginLoader] Попытка установки недостающего модуля ${missingModule} в папку плагина ${plugin.name}`);
97
- const shell = process.platform === 'win32' ? process.env.ComSpec : '/bin/sh';
98
- try {
99
- execSync(`npm install ${missingModule}`, {
100
- cwd: plugin.path,
101
- stdio: 'pipe',
102
- shell: shell
103
- });
104
- sendLog(`[PluginLoader] Модуль ${missingModule} успешно установлен в папку плагина ${plugin.name}, повторная попытка загрузки`);
105
-
106
- // Повторная попытка загрузки
107
- const pluginModule = require(normalizedPath);
108
- if (typeof pluginModule === 'function') {
109
- pluginModule(bot, { settings: finalSettings, store });
110
- } else if (pluginModule && typeof pluginModule.onLoad === 'function') {
111
- pluginModule.onLoad(bot, { settings: finalSettings, store });
112
- }
113
- } catch (installError) {
114
- sendLog(`[PluginLoader] Не удалось установить модуль ${missingModule} в папку плагина ${plugin.name}: ${installError.message}`);
115
- throw error; // Пробрасываем оригинальную ошибку
116
- }
117
- } else {
118
- throw error;
119
- }
120
- } else {
121
- throw error;
122
- }
123
- }
124
-
125
- } catch (error) {
126
- sendLog(`[PluginLoader] [FATAL] Не удалось загрузить плагин ${plugin.name}: ${error.stack}`);
127
- }
128
- }
129
- }
130
- }
131
-
1
+
2
+ const path = require('path');
3
+ const fs = require('fs/promises');
4
+ const { execSync } = require('child_process');
5
+ const fssync = require('fs');
6
+ const PluginStore = require('../plugins/PluginStore');
7
+ const { deepMergeSettings } = require('./utils/settingsMerger');
8
+
9
+ const projectRoot = path.resolve(__dirname, '..');
10
+
11
+
12
+
13
+ async function initializePlugins(bot, installedPlugins = [], prisma) {
14
+ if (!installedPlugins || installedPlugins.length === 0) return;
15
+
16
+ const sendLog = bot.sendLog || console.log;
17
+ sendLog(`[PluginLoader] Загрузка ${installedPlugins.length} плагинов...`);
18
+
19
+ for (const plugin of installedPlugins) {
20
+ if (plugin && plugin.path) {
21
+ try {
22
+ const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
23
+ const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
24
+ const defaultSettings = {};
25
+
26
+ if (manifest.settings) {
27
+ for (const key in manifest.settings) {
28
+ const config = manifest.settings[key];
29
+ if (config.type === 'json_file' && config.defaultPath) {
30
+ const configFilePath = path.join(plugin.path, config.defaultPath);
31
+ try {
32
+ const fileContent = await fs.readFile(configFilePath, 'utf-8');
33
+ defaultSettings[key] = JSON.parse(fileContent);
34
+ } catch (e) {
35
+ sendLog(`[PluginLoader] WARN: Не удалось прочитать defaultPath '${config.defaultPath}' для плагина ${plugin.name}.`);
36
+ defaultSettings[key] = (config.type === 'string[]' || config.type === 'json_file') ? [] : {};
37
+ }
38
+ } else {
39
+ try {
40
+ defaultSettings[key] = JSON.parse(config.default || 'null');
41
+ } catch {
42
+ defaultSettings[key] = config.default;
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ const finalSettings = deepMergeSettings(defaultSettings, savedSettings);
49
+ const store = new PluginStore(prisma, bot.config.id, plugin.name);
50
+
51
+ const mainFile = manifest.main || 'index.js';
52
+ const entryPointPath = path.join(plugin.path, mainFile);
53
+ const normalizedPath = entryPointPath.replace(/\\/g, '/');
54
+
55
+ sendLog(`[PluginLoader] Загрузка: ${plugin.name} (v${plugin.version}) из ${normalizedPath}`);
56
+
57
+ try {
58
+ const pluginModule = require(normalizedPath);
59
+
60
+ if (typeof pluginModule === 'function') {
61
+ pluginModule(bot, { settings: finalSettings, store });
62
+ } else if (pluginModule && typeof pluginModule.onLoad === 'function') {
63
+ pluginModule.onLoad(bot, { settings: finalSettings, store });
64
+ } else {
65
+ sendLog(`[PluginLoader] [ERROR] ${plugin.name} не экспортирует функцию или объект с методом onLoad.`);
66
+ }
67
+ } catch (error) {
68
+ // Зависимости должны быть установлены заранее в PluginManager
69
+ // Если модуль не найден, это означает, что установка зависимостей не была выполнена корректно
70
+ if (error.message.includes('Cannot find module')) {
71
+ const moduleMatch = error.message.match(/Cannot find module '([^']+)'/);
72
+ if (moduleMatch) {
73
+ const missingModule = moduleMatch[1];
74
+ sendLog(`[PluginLoader] [ERROR] Модуль ${missingModule} не найден для плагина ${plugin.name}. Зависимости должны быть установлены заранее.`);
75
+ }
76
+ }
77
+ throw error;
78
+ }
79
+
80
+ } catch (error) {
81
+ sendLog(`[PluginLoader] [FATAL] Не удалось загрузить плагин ${plugin.name}: ${error.stack}`);
82
+ }
83
+ }
84
+ }
85
+ }
86
+
132
87
  module.exports = { initializePlugins };