blockmine 1.16.2 → 1.17.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.
@@ -22,6 +22,173 @@ const upload = multer({ storage: multer.memoryStorage() });
22
22
 
23
23
  const router = express.Router();
24
24
 
25
+ const conditionalRestartAuth = (req, res, next) => {
26
+ if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
27
+ console.log('[Debug] Роут перезапуска бота доступен без проверки прав');
28
+ return next();
29
+ }
30
+
31
+ return authenticate(req, res, (err) => {
32
+ if (err) return next(err);
33
+ return authorize('bot:start_stop')(req, res, next);
34
+ });
35
+ };
36
+
37
+ const conditionalChatAuth = (req, res, next) => {
38
+ if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
39
+ console.log('[Debug] Роут отправки сообщения боту доступен без проверки прав');
40
+ return next();
41
+ }
42
+
43
+ return authenticate(req, res, (err) => {
44
+ if (err) return next(err);
45
+ return authorize('bot:interact')(req, res, next);
46
+ });
47
+ };
48
+
49
+ const conditionalStartStopAuth = (req, res, next) => {
50
+ if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
51
+ console.log('[Debug] Роут запуска/остановки бота доступен без проверки прав');
52
+ return next();
53
+ }
54
+
55
+ return authenticate(req, res, (err) => {
56
+ if (err) return next(err);
57
+ return authorize('bot:start_stop')(req, res, next);
58
+ });
59
+ };
60
+
61
+ const conditionalListAuth = (req, res, next) => {
62
+ if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
63
+ console.log('[Debug] Роут списка ботов/состояния доступен без проверки прав');
64
+ return next();
65
+ }
66
+
67
+ return authenticate(req, res, (err) => {
68
+ if (err) return next(err);
69
+ return authorize('bot:list')(req, res, next);
70
+ });
71
+ };
72
+
73
+ router.post('/:id/restart', conditionalRestartAuth, async (req, res) => {
74
+ try {
75
+ const botId = parseInt(req.params.id, 10);
76
+ botManager.stopBot(botId);
77
+ setTimeout(async () => {
78
+ const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
79
+ if (!botConfig) {
80
+ return res.status(404).json({ success: false, message: 'Бот не найден' });
81
+ }
82
+ botManager.startBot(botConfig);
83
+ res.status(202).json({ success: true, message: 'Команда на перезапуск отправлена.' });
84
+ }, 1000);
85
+ } catch (error) {
86
+ console.error(`[API] Ошибка перезапуска бота ${req.params.id}:`, error);
87
+ res.status(500).json({ success: false, message: 'Ошибка при перезапуске бота: ' + error.message });
88
+ }
89
+ });
90
+
91
+ router.post('/:id/chat', conditionalChatAuth, (req, res) => {
92
+ try {
93
+ const botId = parseInt(req.params.id, 10);
94
+ const { message } = req.body;
95
+ if (!message) return res.status(400).json({ error: 'Сообщение не может быть пустым' });
96
+ const result = botManager.sendMessageToBot(botId, message);
97
+ if (result.success) res.json({ success: true });
98
+ else res.status(404).json(result);
99
+ } catch (error) { res.status(500).json({ error: 'Внутренняя ошибка сервера: ' + error.message }); }
100
+ });
101
+
102
+ router.post('/:id/start', conditionalStartStopAuth, async (req, res) => {
103
+ try {
104
+ const botId = parseInt(req.params.id, 10);
105
+ const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
106
+ if (!botConfig) {
107
+ return res.status(404).json({ success: false, message: 'Бот не найден' });
108
+ }
109
+ botManager.startBot(botConfig);
110
+ res.status(202).json({ success: true, message: 'Команда на запуск отправлена.' });
111
+ } catch (error) {
112
+ console.error(`[API] Ошибка запуска бота ${req.params.id}:`, error);
113
+ res.status(500).json({ success: false, message: 'Ошибка при запуске бота: ' + error.message });
114
+ }
115
+ });
116
+
117
+ router.post('/:id/stop', conditionalStartStopAuth, (req, res) => {
118
+ try {
119
+ const botId = parseInt(req.params.id, 10);
120
+ botManager.stopBot(botId);
121
+ res.status(202).json({ success: true, message: 'Команда на остановку отправлена.' });
122
+ } catch (error) {
123
+ console.error(`[API] Ошибка остановки бота ${req.params.id}:`, error);
124
+ res.status(500).json({ success: false, message: 'Ошибка при остановке бота: ' + error.message });
125
+ }
126
+ });
127
+
128
+ router.get('/', conditionalListAuth, async (req, res) => {
129
+ try {
130
+ const botsWithoutSortOrder = await prisma.bot.findMany({
131
+ where: { sortOrder: null },
132
+ select: { id: true }
133
+ });
134
+
135
+ if (botsWithoutSortOrder.length > 0) {
136
+ console.log(`[API] Обновляем sortOrder для ${botsWithoutSortOrder.length} ботов`);
137
+ for (const bot of botsWithoutSortOrder) {
138
+ await prisma.bot.update({
139
+ where: { id: bot.id },
140
+ data: { sortOrder: bot.id }
141
+ });
142
+ }
143
+ }
144
+
145
+ const bots = await prisma.bot.findMany({
146
+ include: { server: true },
147
+ orderBy: { sortOrder: 'asc' }
148
+ });
149
+ res.json(bots);
150
+ } catch (error) {
151
+ console.error("[API /api/bots] Ошибка получения списка ботов:", error);
152
+ res.status(500).json({ error: 'Не удалось получить список ботов' });
153
+ }
154
+ });
155
+
156
+ router.get('/state', conditionalListAuth, (req, res) => {
157
+ try {
158
+ const state = botManager.getFullState();
159
+ res.json(state);
160
+ } catch (error) { res.status(500).json({ error: 'Не удалось получить состояние ботов' }); }
161
+ });
162
+
163
+ router.get('/:id/logs', conditionalListAuth, (req, res) => {
164
+ try {
165
+ const botId = parseInt(req.params.id, 10);
166
+ const { limit = 100, offset = 0 } = req.query;
167
+
168
+ const logs = botManager.getBotLogs(botId);
169
+
170
+ const startIndex = parseInt(offset);
171
+ const endIndex = startIndex + parseInt(limit);
172
+ const paginatedLogs = logs.slice(startIndex, endIndex);
173
+
174
+ res.json({
175
+ success: true,
176
+ data: {
177
+ logs: paginatedLogs,
178
+ pagination: {
179
+ total: logs.length,
180
+ limit: parseInt(limit),
181
+ offset: startIndex,
182
+ hasMore: endIndex < logs.length
183
+ }
184
+ }
185
+ });
186
+ } catch (error) {
187
+ console.error(`[API] Ошибка получения логов бота ${req.params.id}:`, error);
188
+ res.status(500).json({ error: 'Не удалось получить логи бота' });
189
+ }
190
+ });
191
+
25
192
  router.use(authenticate);
26
193
  router.use('/:botId/event-graphs', eventGraphsRouter);
27
194
  router.use('/:botId/plugins/ide', pluginIdeRouter);
@@ -62,34 +229,25 @@ async function setupDefaultPermissionsForBot(botId, prismaClient = prisma) {
62
229
  console.log(`[Setup] Для бота ID ${botId} созданы группы и права по умолчанию.`);
63
230
  }
64
231
 
65
- router.get('/', authorize('bot:list'), async (req, res) => {
66
- try {
67
- const bots = await prisma.bot.findMany({ include: { server: true }, orderBy: { createdAt: 'asc' } });
68
- res.json(bots);
69
- } catch (error) {
70
- console.error("[API /api/bots] Ошибка получения списка ботов:", error);
71
- res.status(500).json({ error: 'Не удалось получить список ботов' });
72
- }
73
- });
74
232
 
75
- router.get('/state', authorize('bot:list'), (req, res) => {
76
- try {
77
- const state = botManager.getFullState();
78
- res.json(state);
79
- } catch (error) { res.status(500).json({ error: 'Не удалось получить состояние ботов' }); }
80
- });
81
233
 
82
234
  router.post('/', authorize('bot:create'), async (req, res) => {
83
235
  try {
84
236
  const { username, password, prefix, serverId, note } = req.body;
85
237
  if (!username || !serverId) return res.status(400).json({ error: 'Имя и сервер обязательны' });
86
238
 
239
+ const maxSortOrder = await prisma.bot.aggregate({
240
+ _max: { sortOrder: true }
241
+ });
242
+ const nextSortOrder = (maxSortOrder._max.sortOrder || 0) + 1;
243
+
87
244
  const data = {
88
245
  username,
89
246
  prefix,
90
247
  note,
91
248
  serverId: parseInt(serverId, 10),
92
- password: password ? encrypt(password) : null
249
+ password: password ? encrypt(password) : null,
250
+ sortOrder: nextSortOrder
93
251
  };
94
252
 
95
253
  const newBot = await prisma.bot.create({
@@ -168,98 +326,102 @@ router.put('/:id', authorize('bot:update'), async (req, res) => {
168
326
  const updatedBot = await prisma.bot.update({
169
327
  where: { id: botId },
170
328
  data: dataToUpdate,
171
- include: {
172
- server: true
173
- }
329
+ include: { server: true }
174
330
  });
175
331
 
176
- const botManager = req.app.get('botManager');
177
- botManager.reloadBotConfigInRealTime(botId);
178
-
179
332
  res.json(updatedBot);
180
333
  } catch (error) {
181
- console.error('Error updating bot:', error);
182
- console.error('Error details:', {
183
- code: error.code,
184
- meta: error.meta,
185
- message: error.message
186
- });
187
-
188
- if (error.code === 'P2002' && error.meta?.target?.includes('username')) {
189
- return res.status(400).json({
190
- message: 'Бот с таким именем уже существует. Выберите другое имя.'
191
- });
192
- }
193
-
194
- res.status(500).json({ message: `Не удалось обновить бота: ${error.message}` });
334
+ console.error("[API Error] /bots PUT:", error);
335
+ res.status(500).json({ error: 'Не удалось обновить бота' });
195
336
  }
196
337
  });
197
338
 
198
- router.delete('/:id', authorize('bot:delete'), async (req, res) => {
339
+ router.put('/:id/sort-order', authorize('bot:update'), async (req, res) => {
199
340
  try {
341
+ const { newPosition } = req.body;
200
342
  const botId = parseInt(req.params.id, 10);
201
- if (botManager.bots.has(botId)) return res.status(400).json({ error: 'Нельзя удалить запущенного бота' });
202
- await prisma.bot.delete({ where: { id: botId } });
203
- res.status(204).send();
204
- } catch (error) { res.status(500).json({ error: 'Не удалось удалить бота' }); }
205
- });
343
+
344
+ console.log(`[API] Запрос на изменение порядка бота ${botId} на позицию ${newPosition}`);
345
+
346
+ if (isNaN(botId) || typeof newPosition !== 'number') {
347
+ console.log(`[API] Неверные параметры: botId=${botId}, newPosition=${newPosition}`);
348
+ return res.status(400).json({ error: 'Неверные параметры' });
349
+ }
206
350
 
207
- router.post('/:id/start', authorize('bot:start_stop'), async (req, res) => {
208
- try {
209
- const botId = parseInt(req.params.id, 10);
210
- const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
211
- if (!botConfig) {
212
- return res.status(404).json({ success: false, message: 'Бот не найден' });
351
+ const currentBot = await prisma.bot.findUnique({
352
+ where: { id: botId },
353
+ select: { sortOrder: true }
354
+ });
355
+
356
+ if (!currentBot) {
357
+ console.log(`[API] Бот ${botId} не найден`);
358
+ return res.status(404).json({ error: 'Бот не найден' });
213
359
  }
214
- botManager.startBot(botConfig);
215
- res.status(202).json({ success: true, message: 'Команда на запуск отправлена.' });
216
- } catch (error) {
217
- console.error(`[API] Ошибка запуска бота ${req.params.id}:`, error);
218
- res.status(500).json({ success: false, message: 'Ошибка при запуске бота: ' + error.message });
219
- }
220
- });
221
360
 
222
- router.post('/:id/stop', authorize('bot:start_stop'), (req, res) => {
223
- try {
224
- const botId = parseInt(req.params.id, 10);
225
- botManager.stopBot(botId);
226
- res.status(202).json({ success: true, message: 'Команда на остановку отправлена.' });
227
- } catch (error) {
228
- console.error(`[API] Ошибка остановки бота ${req.params.id}:`, error);
229
- res.status(500).json({ success: false, message: 'Ошибка при остановке бота: ' + error.message });
230
- }
231
- });
361
+ const currentPosition = currentBot.sortOrder;
362
+ console.log(`[API] Текущая позиция бота ${botId}: ${currentPosition}, новая позиция: ${newPosition}`);
363
+
364
+ if (newPosition === currentPosition) {
365
+ console.log(`[API] Позиция не изменилась для бота ${botId}`);
366
+ return res.json({ success: true, message: 'Позиция не изменилась' });
367
+ }
232
368
 
233
- router.post('/:id/restart', authorize('bot:start_stop'), async (req, res) => {
234
- try {
235
- const botId = parseInt(req.params.id, 10);
236
- botManager.stopBot(botId);
237
- setTimeout(async () => {
238
- const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
239
- if (!botConfig) {
240
- return res.status(404).json({ success: false, message: 'Бот не найден' });
241
- }
242
- botManager.startBot(botConfig);
243
- res.status(202).json({ success: true, message: 'Команда на перезапуск отправлена.' });
244
- }, 1000);
369
+ if (newPosition > currentPosition) {
370
+ console.log(`[API] Перемещаем бота ${botId} вниз с позиции ${currentPosition} на ${newPosition}`);
371
+ const updateResult = await prisma.bot.updateMany({
372
+ where: {
373
+ sortOrder: {
374
+ gt: currentPosition,
375
+ lte: newPosition
376
+ }
377
+ },
378
+ data: {
379
+ sortOrder: {
380
+ decrement: 1
381
+ }
382
+ }
383
+ });
384
+ console.log(`[API] Обновлено ${updateResult.count} ботов при перемещении вниз`);
385
+ } else {
386
+ console.log(`[API] Перемещаем бота ${botId} вверх с позиции ${currentPosition} на ${newPosition}`);
387
+ const updateResult = await prisma.bot.updateMany({
388
+ where: {
389
+ sortOrder: {
390
+ gte: newPosition,
391
+ lt: currentPosition
392
+ }
393
+ },
394
+ data: {
395
+ sortOrder: {
396
+ increment: 1
397
+ }
398
+ }
399
+ });
400
+ console.log(`[API] Обновлено ${updateResult.count} ботов при перемещении вверх`);
401
+ }
402
+
403
+ await prisma.bot.update({
404
+ where: { id: botId },
405
+ data: { sortOrder: newPosition }
406
+ });
407
+
408
+ console.log(`[API] Успешно обновлен порядок бота ${botId} на позицию ${newPosition}`);
409
+ res.json({ success: true, message: 'Порядок ботов обновлен' });
245
410
  } catch (error) {
246
- console.error(`[API] Ошибка перезапуска бота ${req.params.id}:`, error);
247
- res.status(500).json({ success: false, message: 'Ошибка при перезапуске бота: ' + error.message });
411
+ console.error("[API Error] /bots sort-order PUT:", error);
412
+ res.status(500).json({ error: 'Не удалось обновить порядок ботов' });
248
413
  }
249
414
  });
250
415
 
251
- router.post('/:id/chat', authorize('bot:interact'), (req, res) => {
416
+ router.delete('/:id', authorize('bot:delete'), async (req, res) => {
252
417
  try {
253
418
  const botId = parseInt(req.params.id, 10);
254
- const { message } = req.body;
255
- if (!message) return res.status(400).json({ error: 'Сообщение не может быть пустым' });
256
- const result = botManager.sendMessageToBot(botId, message);
257
- if (result.success) res.json({ success: true });
258
- else res.status(404).json(result);
259
- } catch (error) { res.status(500).json({ error: 'Внутренняя ошибка сервера: ' + error.message }); }
419
+ if (botManager.bots.has(botId)) return res.status(400).json({ error: 'Нельзя удалить запущенного бота' });
420
+ await prisma.bot.delete({ where: { id: botId } });
421
+ res.status(204).send();
422
+ } catch (error) { res.status(500).json({ error: 'Не удалось удалить бота' }); }
260
423
  });
261
424
 
262
-
263
425
  router.get('/servers', authorize('bot:list'), async (req, res) => {
264
426
  try {
265
427
  const servers = await prisma.server.findMany();
@@ -317,24 +479,49 @@ router.get('/:botId/plugins/:pluginId/settings', authorize('plugin:settings:view
317
479
  if (!plugin) return res.status(404).json({ error: 'Установленный плагин не найден' });
318
480
 
319
481
  const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
320
- let defaultSettings = {};
482
+ const defaultSettings = {};
321
483
  const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
484
+ const manifestSettings = manifest.settings || {};
322
485
 
323
- if (manifest.settings) {
324
- for (const key in manifest.settings) {
325
- const config = manifest.settings[key];
326
- if (config.type === 'json_file' && config.defaultPath) {
327
- const configFilePath = path.join(plugin.path, config.defaultPath);
328
- try {
329
- const fileContent = await fs.readFile(configFilePath, 'utf-8');
330
- defaultSettings[key] = JSON.parse(fileContent);
331
- } catch (e) { defaultSettings[key] = {}; }
332
- } else {
333
- try { defaultSettings[key] = JSON.parse(config.default || 'null'); }
334
- catch { defaultSettings[key] = config.default; }
486
+
487
+ const firstSettingValue = Object.values(manifestSettings)[0];
488
+ const isGrouped = firstSettingValue && typeof firstSettingValue === 'object' && !firstSettingValue.type && firstSettingValue.label;
489
+
490
+ const processSetting = async (settingKey, config) => {
491
+ if (!config || !config.type) return;
492
+
493
+ if (config.type === 'json_file' && config.defaultPath) {
494
+ const configFilePath = path.join(plugin.path, config.defaultPath);
495
+ try {
496
+ const fileContent = await fs.readFile(configFilePath, 'utf-8');
497
+ defaultSettings[settingKey] = JSON.parse(fileContent);
498
+ } catch (e) {
499
+ console.error(`[API Settings] Не удалось прочитать defaultPath ${config.defaultPath} для плагина ${plugin.name}: ${e.message}`);
500
+ defaultSettings[settingKey] = {};
501
+ }
502
+ } else if (config.default !== undefined) {
503
+ try {
504
+ defaultSettings[settingKey] = JSON.parse(config.default);
505
+ } catch {
506
+ defaultSettings[settingKey] = config.default;
335
507
  }
336
508
  }
509
+ };
510
+
511
+ if (isGrouped) {
512
+ for (const categoryKey in manifestSettings) {
513
+ const categoryConfig = manifestSettings[categoryKey];
514
+ for (const settingKey in categoryConfig) {
515
+ if (settingKey === 'label') continue;
516
+ await processSetting(settingKey, categoryConfig[settingKey]);
517
+ }
518
+ }
519
+ } else {
520
+ for (const settingKey in manifestSettings) {
521
+ await processSetting(settingKey, manifestSettings[settingKey]);
522
+ }
337
523
  }
524
+
338
525
  const finalSettings = { ...defaultSettings, ...savedSettings };
339
526
  res.json(finalSettings);
340
527
  } catch (error) {
@@ -1492,6 +1679,25 @@ router.get('/:botId/export', authorize('bot:export'), async (req, res) => {
1492
1679
  const installedPlugins = await prisma.installedPlugin.findMany({ where: { botId } });
1493
1680
  archive.append(JSON.stringify(installedPlugins, null, 2), { name: 'plugins.json' });
1494
1681
 
1682
+ try {
1683
+ const installedPlugins = await prisma.installedPlugin.findMany({ where: { botId } });
1684
+ const pluginSettings = installedPlugins
1685
+ .filter(plugin => plugin.settings && plugin.settings !== '{}')
1686
+ .map(plugin => ({
1687
+ pluginName: plugin.name,
1688
+ settings: plugin.settings
1689
+ }));
1690
+
1691
+ if (pluginSettings.length > 0) {
1692
+ console.log(`[Export] Экспорт настроек плагинов для бота ${botId}: ${pluginSettings.length} настроек`);
1693
+ archive.append(JSON.stringify(pluginSettings, null, 2), { name: 'settings.json' });
1694
+ } else {
1695
+ console.log(`[Export] Нет настроек плагинов для экспорта`);
1696
+ }
1697
+ } catch (error) {
1698
+ console.warn(`[Export] Ошибка при экспорте настроек плагинов:`, error.message);
1699
+ }
1700
+
1495
1701
  if (includePluginFiles === 'true') {
1496
1702
  for (const plugin of installedPlugins) {
1497
1703
  const pluginPath = plugin.path;
@@ -1519,7 +1725,9 @@ router.get('/:botId/export', authorize('bot:export'), async (req, res) => {
1519
1725
 
1520
1726
  } catch (error) {
1521
1727
  console.error('Failed to export bot:', error);
1522
- res.status(500).json({ error: `Failed to export bot: ${error.message}` });
1728
+ if (!res.headersSent) {
1729
+ res.status(500).json({ error: `Failed to export bot: ${error.message}` });
1730
+ }
1523
1731
  }
1524
1732
  });
1525
1733
 
@@ -1716,4 +1924,221 @@ router.post('/import', authorize('bot:create'), upload.single('file'), async (re
1716
1924
  }
1717
1925
  });
1718
1926
 
1927
+ router.post('/import/preview', authorize('bot:create'), upload.single('file'), async (req, res) => {
1928
+ try {
1929
+ if (!req.file) {
1930
+ return res.status(400).json({ error: 'Файл не загружен' });
1931
+ }
1932
+
1933
+ const tempDir = path.join(os.tmpdir(), `import-${Date.now()}`);
1934
+ await fse.ensureDir(tempDir);
1935
+
1936
+ try {
1937
+ const zip = new AdmZip(req.file.buffer);
1938
+ zip.extractAllTo(tempDir, true);
1939
+
1940
+ console.log('[Import] Файлы в архиве:', zip.getEntries().map(entry => entry.entryName));
1941
+
1942
+ const importData = {
1943
+ plugins: [],
1944
+ commands: [],
1945
+ eventGraphs: [],
1946
+ settings: null,
1947
+ bot: null
1948
+ };
1949
+
1950
+ const botConfigPath = path.join(tempDir, 'bot.json');
1951
+ if (await fse.pathExists(botConfigPath)) {
1952
+ console.log('[Import] Найден bot.json');
1953
+ const botConfig = JSON.parse(await fse.readFile(botConfigPath, 'utf8'));
1954
+ delete botConfig.password;
1955
+ delete botConfig.proxyPassword;
1956
+ delete botConfig.id;
1957
+ delete botConfig.createdAt;
1958
+ delete botConfig.updatedAt;
1959
+ importData.bot = botConfig;
1960
+ } else {
1961
+ console.log('[Import] bot.json не найден');
1962
+ }
1963
+
1964
+ const pluginsPath = path.join(tempDir, 'plugins.json');
1965
+ if (await fse.pathExists(pluginsPath)) {
1966
+ console.log('[Import] Найден plugins.json');
1967
+ importData.plugins = JSON.parse(await fse.readFile(pluginsPath, 'utf8'));
1968
+ console.log('[Import] Плагинов:', importData.plugins.length);
1969
+ } else {
1970
+ console.log('[Import] plugins.json не найден');
1971
+ }
1972
+
1973
+ const commandsPath = path.join(tempDir, 'commands.json');
1974
+ if (await fse.pathExists(commandsPath)) {
1975
+ console.log('[Import] Найден commands.json');
1976
+ importData.commands = JSON.parse(await fse.readFile(commandsPath, 'utf8'));
1977
+ console.log('[Import] Команд:', importData.commands.length);
1978
+ } else {
1979
+ console.log('[Import] commands.json не найден');
1980
+ }
1981
+
1982
+ const eventGraphsPath = path.join(tempDir, 'event_graphs.json');
1983
+ if (await fse.pathExists(eventGraphsPath)) {
1984
+ console.log('[Import] Найден event_graphs.json');
1985
+ importData.eventGraphs = JSON.parse(await fse.readFile(eventGraphsPath, 'utf8'));
1986
+ console.log('[Import] Графов событий:', importData.eventGraphs.length);
1987
+ } else {
1988
+ console.log('[Import] event_graphs.json не найден');
1989
+ const eventGraphsPathAlt = path.join(tempDir, 'event-graphs.json');
1990
+ if (await fse.pathExists(eventGraphsPathAlt)) {
1991
+ console.log('[Import] Найден event-graphs.json');
1992
+ importData.eventGraphs = JSON.parse(await fse.readFile(eventGraphsPathAlt, 'utf8'));
1993
+ console.log('[Import] Графов событий:', importData.eventGraphs.length);
1994
+ } else {
1995
+ console.log('[Import] event-graphs.json тоже не найден');
1996
+ }
1997
+ }
1998
+
1999
+ const settingsPath = path.join(tempDir, 'settings.json');
2000
+ if (await fse.pathExists(settingsPath)) {
2001
+ console.log('[Import] Найден settings.json');
2002
+ importData.settings = JSON.parse(await fse.readFile(settingsPath, 'utf8'));
2003
+ } else {
2004
+ console.log('[Import] settings.json не найден');
2005
+ }
2006
+
2007
+ console.log('[Import] Итоговые данные:', {
2008
+ plugins: importData.plugins.length,
2009
+ commands: importData.commands.length,
2010
+ eventGraphs: importData.eventGraphs.length,
2011
+ hasSettings: !!importData.settings,
2012
+ hasBot: !!importData.bot
2013
+ });
2014
+
2015
+ res.json(importData);
2016
+
2017
+ } finally {
2018
+ await fse.remove(tempDir);
2019
+ }
2020
+
2021
+ } catch (error) {
2022
+ console.error('[API Error] /bots/import/preview:', error);
2023
+ res.status(500).json({ error: 'Не удалось обработать архив импорта' });
2024
+ }
2025
+ });
2026
+
2027
+ router.post('/import/create', authorize('bot:create'), async (req, res) => {
2028
+ try {
2029
+ const { username, password, prefix, serverId, note, owners, proxyHost, proxyPort, proxyUsername, proxyPassword, importData } = req.body;
2030
+
2031
+ if (!username || !serverId) {
2032
+ return res.status(400).json({ error: 'Имя и сервер обязательны' });
2033
+ }
2034
+
2035
+ const botData = {
2036
+ username,
2037
+ prefix,
2038
+ note,
2039
+ serverId: parseInt(serverId, 10),
2040
+ password: password ? encrypt(password) : null,
2041
+ owners: owners || '',
2042
+ proxyHost: proxyHost || null,
2043
+ proxyPort: proxyPort ? parseInt(proxyPort, 10) : null,
2044
+ proxyUsername: proxyUsername || null,
2045
+ proxyPassword: proxyPassword ? encrypt(proxyPassword) : null
2046
+ };
2047
+
2048
+ const newBot = await prisma.bot.create({
2049
+ data: botData,
2050
+ include: { server: true }
2051
+ });
2052
+
2053
+ await setupDefaultPermissionsForBot(newBot.id);
2054
+
2055
+ if (importData) {
2056
+ try {
2057
+ if (importData.plugins && Array.isArray(importData.plugins)) {
2058
+ for (const plugin of importData.plugins) {
2059
+ try {
2060
+ await prisma.installedPlugin.create({
2061
+ data: {
2062
+ ...plugin,
2063
+ botId: newBot.id,
2064
+ id: undefined
2065
+ }
2066
+ });
2067
+ console.log(`[Import] Импортирован плагин ${plugin.name}`);
2068
+ } catch (error) {
2069
+ console.warn(`[Import] Не удалось импортировать плагин ${plugin.name}:`, error.message);
2070
+ }
2071
+ }
2072
+ }
2073
+
2074
+ if (importData.commands && Array.isArray(importData.commands)) {
2075
+ for (const command of importData.commands) {
2076
+ try {
2077
+ await prisma.command.create({
2078
+ data: {
2079
+ ...command,
2080
+ botId: newBot.id,
2081
+ id: undefined
2082
+ }
2083
+ });
2084
+ } catch (error) {
2085
+ console.warn(`[Import] Не удалось импортировать команду ${command.name}:`, error.message);
2086
+ }
2087
+ }
2088
+ }
2089
+
2090
+ if (importData.eventGraphs && Array.isArray(importData.eventGraphs)) {
2091
+ for (const graph of importData.eventGraphs) {
2092
+ try {
2093
+ await prisma.eventGraph.create({
2094
+ data: {
2095
+ ...graph,
2096
+ botId: newBot.id,
2097
+ id: undefined
2098
+ }
2099
+ });
2100
+ } catch (error) {
2101
+ console.warn(`[Import] Не удалось импортировать граф событий ${graph.name}:`, error.message);
2102
+ }
2103
+ }
2104
+ }
2105
+
2106
+ if (importData.settings && Array.isArray(importData.settings)) {
2107
+ for (const setting of importData.settings) {
2108
+ try {
2109
+ const updated = await prisma.installedPlugin.updateMany({
2110
+ where: {
2111
+ botId: newBot.id,
2112
+ name: setting.pluginName
2113
+ },
2114
+ data: {
2115
+ settings: setting.settings
2116
+ }
2117
+ });
2118
+ if (updated.count > 0) {
2119
+ console.log(`[Import] Импортированы настройки плагина ${setting.pluginName}`);
2120
+ } else {
2121
+ console.warn(`[Import] Плагин ${setting.pluginName} не найден для применения настроек`);
2122
+ }
2123
+ } catch (error) {
2124
+ console.warn(`[Import] Не удалось импортировать настройки плагина ${setting.pluginName}:`, error.message);
2125
+ }
2126
+ }
2127
+ }
2128
+
2129
+ } catch (error) {
2130
+ console.error('[Import] Ошибка при импорте данных:', error);
2131
+ }
2132
+ }
2133
+
2134
+ res.status(201).json(newBot);
2135
+ } catch (error) {
2136
+ if (error.code === 'P2002') {
2137
+ return res.status(409).json({ error: 'Бот с таким именем уже существует' });
2138
+ }
2139
+ console.error("[API Error] /bots/import/create:", error);
2140
+ res.status(500).json({ error: 'Не удалось создать бота с импортированными данными' });
2141
+ }
2142
+ });
2143
+
1719
2144
  module.exports = router;