blockmine 1.18.4 → 1.19.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.
Files changed (35) hide show
  1. package/.kiro/steering/product.md +27 -0
  2. package/.kiro/steering/structure.md +89 -0
  3. package/.kiro/steering/tech.md +94 -0
  4. package/CHANGELOG.md +147 -112
  5. package/backend/cli.js +59 -57
  6. package/backend/prisma/migrations/20250701190321_add/migration.sql +2 -2
  7. package/backend/prisma/migrations/20250709150611_add_run_on_startup_to_tasks/migration.sql +21 -21
  8. package/backend/prisma/migrations/20250709151124_make_cron_pattern_optional/migration.sql +21 -21
  9. package/backend/prisma/migrations/20250718181335_add_plugin_data_store/migration.sql +14 -14
  10. package/backend/prisma/migrations/20250719115906_add_plugin_owner_to_graphs/migration.sql +45 -45
  11. package/backend/prisma/migrations/20250723160648_add_bot_sort_order/migration.sql +2 -2
  12. package/backend/prisma/migrations/20250816083216_add_panel_user_bot_access/migration.sql +30 -0
  13. package/backend/prisma/migrations/migration_lock.toml +2 -2
  14. package/backend/prisma/schema.prisma +244 -229
  15. package/backend/src/api/middleware/botAccess.js +35 -0
  16. package/backend/src/api/routes/auth.js +633 -595
  17. package/backend/src/api/routes/bots.js +294 -179
  18. package/backend/src/api/routes/eventGraphs.js +459 -459
  19. package/backend/src/api/routes/pluginIde.js +6 -2
  20. package/backend/src/api/routes/servers.js +27 -0
  21. package/backend/src/core/BotManager.js +0 -1
  22. package/backend/src/core/BotProcess.js +1 -2
  23. package/backend/src/core/GraphExecutionEngine.js +917 -917
  24. package/backend/src/core/PluginLoader.js +208 -86
  25. package/backend/src/core/PluginManager.js +465 -427
  26. package/backend/src/core/commands/dev.js +6 -1
  27. package/backend/src/real-time/presence.js +74 -0
  28. package/backend/src/real-time/socketHandler.js +2 -0
  29. package/backend/src/server.js +193 -186
  30. package/frontend/dist/assets/index-BFd7YoAj.css +1 -0
  31. package/frontend/dist/assets/index-DxdxTe6I.js +8352 -0
  32. package/frontend/dist/index.html +2 -2
  33. package/package.json +1 -1
  34. package/frontend/dist/assets/index-CA3XrPPP.css +0 -1
  35. package/frontend/dist/assets/index-CM9ljR30.js +0 -8352
@@ -1,87 +1,209 @@
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
-
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
+ const { createRequire } = require('module');
9
+ const { execSync: execSyncRaw } = require('child_process');
10
+
11
+ const projectRoot = path.resolve(__dirname, '..');
12
+
13
+
14
+
15
+ async function initializePlugins(bot, installedPlugins = [], prisma) {
16
+ if (!installedPlugins || installedPlugins.length === 0) return;
17
+
18
+ const sendLog = bot.sendLog || console.log;
19
+ sendLog(`[PluginLoader] Загрузка ${installedPlugins.length} плагинов...`);
20
+
21
+ for (const plugin of installedPlugins) {
22
+ if (plugin && plugin.path) {
23
+ try {
24
+ const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
25
+ const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
26
+ const defaultSettings = {};
27
+
28
+ if (manifest.settings) {
29
+ for (const key in manifest.settings) {
30
+ const config = manifest.settings[key];
31
+ if (config.type === 'json_file' && config.defaultPath) {
32
+ const configFilePath = path.join(plugin.path, config.defaultPath);
33
+ try {
34
+ const fileContent = await fs.readFile(configFilePath, 'utf-8');
35
+ defaultSettings[key] = JSON.parse(fileContent);
36
+ } catch (e) {
37
+ sendLog(`[PluginLoader] WARN: Не удалось прочитать defaultPath '${config.defaultPath}' для плагина ${plugin.name}.`);
38
+ defaultSettings[key] = (config.type === 'string[]' || config.type === 'json_file') ? [] : {};
39
+ }
40
+ } else {
41
+ try {
42
+ defaultSettings[key] = JSON.parse(config.default || 'null');
43
+ } catch {
44
+ defaultSettings[key] = config.default;
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ const finalSettings = deepMergeSettings(defaultSettings, savedSettings);
51
+ const store = new PluginStore(prisma, bot.config.id, plugin.name);
52
+
53
+ const mainFile = manifest.main || 'index.js';
54
+ const entryPointPath = path.join(plugin.path, mainFile);
55
+ const normalizedPath = entryPointPath.replace(/\\/g, '/');
56
+ const pluginRequire = createRequire(entryPointPath);
57
+ const { pathToFileURL } = require('url');
58
+
59
+ const loadAndInit = async () => {
60
+ let pluginModule;
61
+ try {
62
+ pluginModule = pluginRequire(normalizedPath);
63
+ } catch (e) {
64
+ if (e.code === 'ERR_REQUIRE_ESM' || /Cannot use import statement|Unexpected token 'export'|Unexpected identifier 'import'/.test(e.message)) {
65
+ const moduleUrl = pathToFileURL(entryPointPath).href;
66
+ const esmModule = await import(moduleUrl);
67
+ pluginModule = esmModule && esmModule.default ? esmModule.default : esmModule;
68
+ } else {
69
+ throw e;
70
+ }
71
+ }
72
+
73
+ if (typeof pluginModule === 'function') {
74
+ pluginModule(bot, { settings: finalSettings, store });
75
+ } else if (pluginModule && typeof pluginModule.onLoad === 'function') {
76
+ pluginModule.onLoad(bot, { settings: finalSettings, store });
77
+ } else if (pluginModule && pluginModule.default && typeof pluginModule.default === 'function') {
78
+ pluginModule.default(bot, { settings: finalSettings, store });
79
+ } else if (pluginModule && pluginModule.default && typeof pluginModule.default.onLoad === 'function') {
80
+ pluginModule.default.onLoad(bot, { settings: finalSettings, store });
81
+ } else {
82
+ sendLog(`[PluginLoader] [ERROR] ${plugin.name} не экспортирует функцию или объект с методом onLoad.`);
83
+ }
84
+ };
85
+
86
+ sendLog(`[PluginLoader] Загрузка: ${plugin.name} (v${plugin.version}) из ${normalizedPath}`);
87
+
88
+ try {
89
+ await loadAndInit();
90
+ } catch (error) {
91
+ let handled = false;
92
+ let lastError = error;
93
+ if (error.message.includes('Cannot find module')) {
94
+ const moduleMatch = error.message.match(/Cannot find module '([^']+)'/);
95
+ const missingModule = moduleMatch ? moduleMatch[1] : null;
96
+ if (missingModule) {
97
+ try {
98
+ // Диагностика текущего состояния резолва
99
+ pluginRequire.resolve(missingModule);
100
+ sendLog(`[PluginLoader] [DEBUG] ${missingModule} уже резолвится до установки? Это неожиданно.`);
101
+ } catch {
102
+ sendLog(`[PluginLoader] [DEBUG] ${missingModule} не резолвится до установки.`);
103
+ }
104
+ sendLog(`[PluginLoader] [WARN] Модуль ${missingModule} не найден для плагина ${plugin.name}. Пытаюсь установить автоматически...`);
105
+ try {
106
+ // Сначала пробуем установить все зависимости из package.json плагина
107
+ try {
108
+ execSync('npm install --omit=dev --no-audit --no-fund', { cwd: plugin.path, stdio: 'inherit' });
109
+ } catch (e1) {
110
+ sendLog(`[PluginLoader] [WARN] npm install завершился с ошибкой, пробую с --legacy-peer-deps...`);
111
+ execSync('npm install --omit=dev --legacy-peer-deps --no-audit --no-fund', { cwd: plugin.path, stdio: 'inherit' });
112
+ }
113
+ sendLog(`[PluginLoader] [INFO] Зависимости установлены. Повторная загрузка плагина ${plugin.name}...`);
114
+ // Очистим кэш require для файлов плагина перед повторной загрузкой
115
+ for (const key of Object.keys(require.cache)) {
116
+ if (key.startsWith(plugin.path)) {
117
+ delete require.cache[key];
118
+ }
119
+ }
120
+ // Если конкретный модуль всё ещё не резолвится, попробуем точечно
121
+ let needTargetedInstall = false;
122
+ try {
123
+ pluginRequire.resolve(missingModule);
124
+ sendLog(`[PluginLoader] [DEBUG] ${missingModule} резолвится после общего npm install.`);
125
+ } catch {
126
+ sendLog(`[PluginLoader] [DEBUG] ${missingModule} всё ещё не резолвится после общего npm install.`);
127
+ needTargetedInstall = true;
128
+ }
129
+ if (needTargetedInstall) {
130
+ throw new Error(`Cannot find module '${missingModule}' after npm install`);
131
+ }
132
+ await loadAndInit();
133
+ handled = true;
134
+ } catch (retryErr) {
135
+ lastError = retryErr;
136
+ // Если после общей установки модуль всё ещё отсутствует, пробуем поставить точечно
137
+ if (retryErr.message && retryErr.message.includes('Cannot find module') && missingModule) {
138
+ try {
139
+ sendLog(`[PluginLoader] [WARN] Повторная попытка: устанавливаю ${missingModule} адресно...`);
140
+ // Простая валидация имени пакета для безопасности
141
+ const safeName = /^[a-zA-Z0-9@_/\\.\-]+$/.test(missingModule) ? missingModule : null;
142
+ if (!safeName) {
143
+ throw new Error(`Некорректное имя пакета: ${missingModule}`);
144
+ }
145
+ try {
146
+ execSync(`npm install ${safeName} --omit=dev --no-audit --no-fund`, { cwd: plugin.path, stdio: 'inherit' });
147
+ } catch (e2) {
148
+ sendLog(`[PluginLoader] [WARN] Адресная установка ${missingModule} завершилась с ошибкой, пробую с --legacy-peer-deps...`);
149
+ execSync(`npm install ${safeName} --omit=dev --legacy-peer-deps --no-audit --no-fund`, { cwd: plugin.path, stdio: 'inherit' });
150
+ }
151
+ sendLog(`[PluginLoader] [INFO] Модуль ${missingModule} установлен. Повторная загрузка ${plugin.name}...`);
152
+ for (const key of Object.keys(require.cache)) {
153
+ if (key.startsWith(plugin.path)) {
154
+ delete require.cache[key];
155
+ }
156
+ }
157
+ // Если у пакета есть peerDependencies, установим их
158
+ try {
159
+ const out = execSyncRaw(`npm view ${safeName} peerDependencies --json`, { cwd: plugin.path });
160
+ const text = String(out || '').trim();
161
+ if (text && text !== 'null' && text !== 'undefined') {
162
+ const peersObj = JSON.parse(text);
163
+ const peers = Object.keys(peersObj || {});
164
+ if (peers.length > 0) {
165
+ sendLog(`[PluginLoader] [INFO] Установка peerDependencies для ${missingModule}: ${peers.join(', ')}`);
166
+ const installLine = `npm install ${peers.join(' ')} --omit=dev --legacy-peer-deps --no-audit --no-fund`;
167
+ execSync(installLine, { cwd: plugin.path, stdio: 'inherit' });
168
+ }
169
+ }
170
+ } catch (peerErr) {
171
+ sendLog(`[PluginLoader] [WARN] Не удалось получить/установить peerDependencies для ${missingModule}: ${peerErr.message}`);
172
+ }
173
+ // Проверим, что модуль теперь доступен
174
+ try {
175
+ const paths = pluginRequire.resolve.paths ? pluginRequire.resolve.paths(missingModule) : [];
176
+ sendLog(`[PluginLoader] [DEBUG] Пути резолва для ${missingModule}: ${JSON.stringify(paths)}`);
177
+ pluginRequire.resolve(missingModule);
178
+ } catch (rErr) {
179
+ sendLog(`[PluginLoader] [ERROR] ${missingModule} всё ещё не резолвится после адресной установки: ${rErr.message}`);
180
+ throw rErr;
181
+ }
182
+ sendLog(`[PluginLoader] [DEBUG] ${missingModule} резолвится после адресной установки.`);
183
+ await loadAndInit();
184
+ handled = true;
185
+ } catch (installErr) {
186
+ lastError = installErr;
187
+ sendLog(`[PluginLoader] [ERROR] Автоустановка модуля ${missingModule} для ${plugin.name} не удалась: ${installErr.message}`);
188
+ }
189
+ } else {
190
+ sendLog(`[PluginLoader] [ERROR] Не удалось автоматически установить зависимости для ${plugin.name}: ${retryErr.message}`);
191
+ }
192
+ }
193
+ } else {
194
+ sendLog(`[PluginLoader] [ERROR] Не удалось определить отсутствующий модуль для плагина ${plugin.name}.`);
195
+ }
196
+ }
197
+ if (!handled) {
198
+ throw lastError;
199
+ }
200
+ }
201
+
202
+ } catch (error) {
203
+ sendLog(`[PluginLoader] [FATAL] Не удалось загрузить плагин ${plugin.name}: ${error.stack}`);
204
+ }
205
+ }
206
+ }
207
+ }
208
+
87
209
  module.exports = { initializePlugins };