blockmine 1.3.22 → 1.4.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.
@@ -1,5 +1,4 @@
1
1
  const express = require('express');
2
- const router = express.Router();
3
2
  const { PrismaClient } = require('@prisma/client');
4
3
  const path = require('path');
5
4
  const fs = require('fs/promises');
@@ -7,7 +6,8 @@ const BotManager = require('../../core/BotManager');
7
6
  const PluginManager = require('../../core/PluginManager');
8
7
  const UserService = require('../../core/UserService');
9
8
  const commandManager = require('../../core/system/CommandManager');
10
-
9
+ const { authenticate, authorize } = require('../middleware/auth');
10
+ const { encrypt } = require('../../core/utils/crypto');
11
11
 
12
12
  const multer = require('multer');
13
13
  const archiver = require('archiver');
@@ -16,6 +16,12 @@ const AdmZip = require('adm-zip');
16
16
  const prisma = new PrismaClient();
17
17
  const upload = multer({ storage: multer.memoryStorage() });
18
18
 
19
+
20
+ const router = express.Router();
21
+
22
+
23
+ router.use(authenticate);
24
+
19
25
  async function setupDefaultPermissionsForBot(botId, prismaClient = prisma) {
20
26
  const initialData = {
21
27
  groups: ["User", "Admin"],
@@ -53,7 +59,7 @@ async function setupDefaultPermissionsForBot(botId, prismaClient = prisma) {
53
59
  }
54
60
 
55
61
 
56
- router.get('/', async (req, res) => {
62
+ router.get('/', authorize('bot:list'), async (req, res) => {
57
63
  try {
58
64
  const bots = await prisma.bot.findMany({ include: { server: true }, orderBy: { createdAt: 'asc' } });
59
65
  res.json(bots);
@@ -63,19 +69,28 @@ router.get('/', async (req, res) => {
63
69
  }
64
70
  });
65
71
 
66
- router.get('/state', (req, res) => {
72
+ router.get('/state', authorize('bot:list'), (req, res) => {
67
73
  try {
68
74
  const state = BotManager.getFullState();
69
75
  res.json(state);
70
76
  } catch (error) { res.status(500).json({ error: 'Не удалось получить состояние ботов' }); }
71
77
  });
72
78
 
73
- router.post('/', async (req, res) => {
79
+ router.post('/', authorize('bot:create'), async (req, res) => {
74
80
  try {
75
81
  const { username, password, prefix, serverId, note } = req.body;
76
82
  if (!username || !serverId) return res.status(400).json({ error: 'Имя и сервер обязательны' });
83
+
84
+ const data = {
85
+ username,
86
+ prefix,
87
+ note,
88
+ serverId: parseInt(serverId, 10),
89
+ password: password ? encrypt(password) : null
90
+ };
91
+
77
92
  const newBot = await prisma.bot.create({
78
- data: { username, password, prefix, note, serverId: parseInt(serverId, 10) },
93
+ data: data,
79
94
  include: { server: true }
80
95
  });
81
96
  await setupDefaultPermissionsForBot(newBot.id);
@@ -87,7 +102,7 @@ router.post('/', async (req, res) => {
87
102
  }
88
103
  });
89
104
 
90
- router.put('/:id', async (req, res) => {
105
+ router.put('/:id', authorize('bot:update'), async (req, res) => {
91
106
  try {
92
107
  const botId = parseInt(req.params.id, 10);
93
108
 
@@ -106,8 +121,12 @@ router.put('/:id', async (req, res) => {
106
121
  };
107
122
 
108
123
  if (password) {
109
- dataToUpdate.password = password;
124
+ dataToUpdate.password = encrypt(password);
110
125
  }
126
+ if (proxyPassword) {
127
+ dataToUpdate.proxyPassword = encrypt(proxyPassword);
128
+ }
129
+
111
130
  if (proxyPassword) {
112
131
  dataToUpdate.proxyPassword = proxyPassword;
113
132
  }
@@ -135,7 +154,7 @@ router.put('/:id', async (req, res) => {
135
154
  }
136
155
  });
137
156
 
138
- router.delete('/:id', async (req, res) => {
157
+ router.delete('/:id', authorize('bot:delete'), async (req, res) => {
139
158
  try {
140
159
  const botId = parseInt(req.params.id, 10);
141
160
  if (BotManager.bots.has(botId)) return res.status(400).json({ error: 'Нельзя удалить запущенного бота' });
@@ -145,7 +164,7 @@ router.delete('/:id', async (req, res) => {
145
164
  });
146
165
 
147
166
 
148
- router.post('/:id/start', async (req, res) => {
167
+ router.post('/:id/start', authorize('bot:start_stop'), async (req, res) => {
149
168
  try {
150
169
  const botId = parseInt(req.params.id, 10);
151
170
  const botConfig = await prisma.bot.findUnique({ where: { id: botId }, include: { server: true } });
@@ -155,13 +174,13 @@ router.post('/:id/start', async (req, res) => {
155
174
  } catch (error) { res.status(500).json({ error: 'Ошибка при запуске бота: ' + error.message }); }
156
175
  });
157
176
 
158
- router.post('/:id/stop', (req, res) => {
177
+ router.post('/:id/stop', authorize('bot:start_stop'), (req, res) => {
159
178
  const botId = parseInt(req.params.id, 10);
160
179
  const result = BotManager.stopBot(botId);
161
180
  res.json(result);
162
181
  });
163
182
 
164
- router.post('/:id/chat', (req, res) => {
183
+ router.post('/:id/chat', authorize('bot:interact'), (req, res) => {
165
184
  try {
166
185
  const botId = parseInt(req.params.id, 10);
167
186
  const { message } = req.body;
@@ -172,7 +191,7 @@ router.post('/:id/chat', (req, res) => {
172
191
  } catch (error) { res.status(500).json({ error: 'Внутренняя ошибка сервера: ' + error.message }); }
173
192
  });
174
193
 
175
- router.get('/:id/export', async (req, res) => {
194
+ router.get('/:id/export', authorize('bot:export'), async (req, res) => {
176
195
  try {
177
196
  const botId = parseInt(req.params.id, 10);
178
197
  const {
@@ -231,7 +250,7 @@ router.get('/:id/export', async (req, res) => {
231
250
  await fs.access(plugin.path);
232
251
  archive.directory(plugin.path, `plugins/${pluginFolderName}`);
233
252
  } catch (error) {
234
- console.warn(`[Export] Папка плагина ${plugin.name} по пути ${plugin.path} не найдена, пропускаем.`);
253
+ console.warn(`[Export] Директория плагина ${plugin.name} (${plugin.path}) не найдена, пропускаем.`);
235
254
  }
236
255
  }
237
256
  }
@@ -239,13 +258,15 @@ router.get('/:id/export', async (req, res) => {
239
258
  await archive.finalize();
240
259
 
241
260
  } catch (error) {
242
- console.error("[API Error] /export GET:", error);
243
- res.status(500).json({ error: 'Не удалось экспортировать бота' });
261
+ console.error("Bot export error:", error);
262
+ res.status(500).json({ error: 'Не удалось экспортировать бота.' });
244
263
  }
245
264
  });
246
265
 
247
- router.post('/import', upload.single('botFile'), async (req, res) => {
248
- if (!req.file) return res.status(400).json({ error: 'Файл не был загружен' });
266
+ router.post('/import', upload.single('file'), authorize('bot:import'), async (req, res) => {
267
+ if (!req.file) {
268
+ return res.status(400).send('Файл не загружен.');
269
+ }
249
270
  if (path.extname(req.file.originalname).toLowerCase() !== '.zip') return res.status(400).json({ error: 'Неверный формат файла. Ожидался ZIP-архив.' });
250
271
 
251
272
  const zip = new AdmZip(req.file.buffer);
@@ -358,7 +379,7 @@ router.post('/import', upload.single('botFile'), async (req, res) => {
358
379
  });
359
380
 
360
381
 
361
- router.get('/:botId/plugins', async (req, res) => {
382
+ router.get('/:botId/plugins', authorize('plugin:list'), async (req, res) => {
362
383
  try {
363
384
  const botId = parseInt(req.params.botId);
364
385
  const plugins = await prisma.installedPlugin.findMany({ where: { botId } });
@@ -366,7 +387,7 @@ router.get('/:botId/plugins', async (req, res) => {
366
387
  } catch (error) { res.status(500).json({ error: 'Не удалось получить плагины бота' }); }
367
388
  });
368
389
 
369
- router.post('/:botId/plugins/install/github', async (req, res) => {
390
+ router.post('/:botId/plugins/install/github', authorize('plugin:install'), async (req, res) => {
370
391
  try {
371
392
  const botId = parseInt(req.params.botId);
372
393
  const { repoUrl } = req.body;
@@ -375,7 +396,7 @@ router.post('/:botId/plugins/install/github', async (req, res) => {
375
396
  } catch (error) { res.status(500).json({ error: error.message }); }
376
397
  });
377
398
 
378
- router.post('/:botId/plugins/register/local', async (req, res) => {
399
+ router.post('/:botId/plugins/register/local', authorize('plugin:install'), async (req, res) => {
379
400
  try {
380
401
  const botId = parseInt(req.params.botId);
381
402
  const { path } = req.body;
@@ -384,7 +405,7 @@ router.post('/:botId/plugins/register/local', async (req, res) => {
384
405
  } catch (error) { res.status(500).json({ error: error.message }); }
385
406
  });
386
407
 
387
- router.delete('/:botId/plugins/:pluginId', async (req, res) => {
408
+ router.delete('/:botId/plugins/:pluginId', authorize('plugin:delete'), async (req, res) => {
388
409
  try {
389
410
  const pluginId = parseInt(req.params.pluginId);
390
411
  await PluginManager.deletePlugin(pluginId);
@@ -392,7 +413,7 @@ router.delete('/:botId/plugins/:pluginId', async (req, res) => {
392
413
  } catch (error) { res.status(500).json({ error: error.message }); }
393
414
  });
394
415
 
395
- router.get('/:botId/plugins/:pluginId/settings', async (req, res) => {
416
+ router.get('/:botId/plugins/:pluginId/settings', authorize('plugin:settings:view'), async (req, res) => {
396
417
  try {
397
418
  const pluginId = parseInt(req.params.pluginId);
398
419
  const plugin = await prisma.installedPlugin.findUnique({ where: { id: pluginId } });
@@ -425,7 +446,7 @@ router.get('/:botId/plugins/:pluginId/settings', async (req, res) => {
425
446
  }
426
447
  });
427
448
 
428
- router.put('/:botId/plugins/:pluginId', async (req, res) => {
449
+ router.put('/:botId/plugins/:pluginId', authorize('plugin:settings:edit'), async (req, res) => {
429
450
  try {
430
451
  const pluginId = parseInt(req.params.pluginId);
431
452
  const { isEnabled, settings } = req.body;
@@ -439,7 +460,7 @@ router.put('/:botId/plugins/:pluginId', async (req, res) => {
439
460
  });
440
461
 
441
462
 
442
- router.get('/:botId/management-data', async (req, res) => {
463
+ router.get('/:botId/management-data', authorize('management:view'), async (req, res) => {
443
464
  try {
444
465
  const botId = parseInt(req.params.botId);
445
466
 
@@ -511,7 +532,7 @@ router.get('/:botId/management-data', async (req, res) => {
511
532
  }
512
533
  });
513
534
 
514
- router.put('/:botId/commands/:commandId', async (req, res) => {
535
+ router.put('/:botId/commands/:commandId', authorize('management:edit'), async (req, res) => {
515
536
  try {
516
537
  const commandId = parseInt(req.params.commandId, 10);
517
538
  const botId = parseInt(req.params.botId, 10);
@@ -546,7 +567,7 @@ router.put('/:botId/commands/:commandId', async (req, res) => {
546
567
  }
547
568
  });
548
569
 
549
- router.post('/:botId/groups', async (req, res) => {
570
+ router.post('/:botId/groups', authorize('management:edit'), async (req, res) => {
550
571
  try {
551
572
  const botId = parseInt(req.params.botId);
552
573
  const { name, permissionIds } = req.body;
@@ -571,7 +592,7 @@ router.post('/:botId/groups', async (req, res) => {
571
592
  }
572
593
  });
573
594
 
574
- router.put('/:botId/groups/:groupId', async (req, res) => {
595
+ router.put('/:botId/groups/:groupId', authorize('management:edit'), async (req, res) => {
575
596
  try {
576
597
  const botId = parseInt(req.params.botId, 10);
577
598
  const groupId = parseInt(req.params.groupId);
@@ -606,7 +627,7 @@ router.put('/:botId/groups/:groupId', async (req, res) => {
606
627
  }
607
628
  });
608
629
 
609
- router.delete('/:botId/groups/:groupId', async (req, res) => {
630
+ router.delete('/:botId/groups/:groupId', authorize('management:edit'), async (req, res) => {
610
631
  try {
611
632
  const botId = parseInt(req.params.botId, 10);
612
633
  const groupId = parseInt(req.params.groupId);
@@ -622,7 +643,7 @@ router.delete('/:botId/groups/:groupId', async (req, res) => {
622
643
  } catch (error) { res.status(500).json({ error: 'Не удалось удалить группу.' }); }
623
644
  });
624
645
 
625
- router.post('/:botId/permissions', async (req, res) => {
646
+ router.post('/:botId/permissions', authorize('management:edit'), async (req, res) => {
626
647
  try {
627
648
  const botId = parseInt(req.params.botId);
628
649
  const { name, description } = req.body;
@@ -640,7 +661,7 @@ router.post('/:botId/permissions', async (req, res) => {
640
661
  }
641
662
  });
642
663
 
643
- router.put('/:botId/users/:userId', async (req, res) => {
664
+ router.put('/:botId/users/:userId', authorize('management:edit'), async (req, res) => {
644
665
  try {
645
666
  const botId = parseInt(req.params.botId, 10);
646
667
  const userId = parseInt(req.params.userId, 10);
@@ -677,7 +698,7 @@ router.put('/:botId/users/:userId', async (req, res) => {
677
698
  });
678
699
 
679
700
 
680
- router.post('/start-all', async (req, res) => {
701
+ router.post('/start-all', authorize('bot:start_stop'), async (req, res) => {
681
702
  try {
682
703
  console.log('[API] Получен запрос на запуск всех ботов.');
683
704
  const allBots = await prisma.bot.findMany({ include: { server: true } });
@@ -695,7 +716,7 @@ router.post('/start-all', async (req, res) => {
695
716
  }
696
717
  });
697
718
 
698
- router.post('/stop-all', (req, res) => {
719
+ router.post('/stop-all', authorize('bot:start_stop'), (req, res) => {
699
720
  try {
700
721
  console.log('[API] Получен запрос на остановку всех ботов.');
701
722
  const botIds = Array.from(BotManager.bots.keys());
@@ -712,7 +733,7 @@ router.post('/stop-all', (req, res) => {
712
733
  });
713
734
 
714
735
 
715
- router.get('/:id/settings/all', async (req, res) => {
736
+ router.get('/:id/settings/all', authorize('bot:update'), async (req, res) => {
716
737
  try {
717
738
  const botId = parseInt(req.params.id, 10);
718
739
 
@@ -0,0 +1,67 @@
1
+ const express = require('express');
2
+ const fs = require('fs/promises');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { authenticate, authorize } = require('../middleware/auth');
6
+ const config = require('../../config');
7
+
8
+ const router = express.Router();
9
+
10
+ const DATA_DIR = path.join(os.homedir(), '.blockmine');
11
+ const CONFIG_PATH = path.join(DATA_DIR, 'config.json');
12
+
13
+ /**
14
+ * @route GET /api/panel/settings
15
+ * @desc Получить текущие глобальные настройки
16
+ * @access Private (Admin only)
17
+ */
18
+ router.get('/settings', authenticate, authorize('panel:settings:view'), (req, res) => {
19
+ const { server, telemetry } = config;
20
+ res.json({
21
+ server: {
22
+ allowExternalAccess: server.allowExternalAccess
23
+ },
24
+ telemetry: {
25
+ enabled: telemetry?.enabled ?? true
26
+ }
27
+ });
28
+ });
29
+
30
+ /**
31
+ * @route PUT /api/panel/settings
32
+ * @desc Обновить глобальные настройки
33
+ * @access Private (Admin only)
34
+ */
35
+ router.put('/settings', authenticate, authorize('panel:settings:edit'), async (req, res) => {
36
+ const { allowExternalAccess, telemetryEnabled } = req.body;
37
+
38
+ try {
39
+ const currentConfig = JSON.parse(await fs.readFile(CONFIG_PATH, 'utf-8'));
40
+
41
+ if (typeof allowExternalAccess === 'boolean') {
42
+ currentConfig.server.allowExternalAccess = allowExternalAccess;
43
+ currentConfig.server.host = allowExternalAccess ? '0.0.0.0' : '127.0.0.1';
44
+ console.log(`[Config Update] Внешний доступ ${allowExternalAccess ? 'ВКЛЮЧЕН' : 'ВЫКЛЮЧЕН'}. Хост изменен на ${currentConfig.server.host}`);
45
+ }
46
+
47
+ if (typeof telemetryEnabled === 'boolean') {
48
+ if (!currentConfig.telemetry) {
49
+ currentConfig.telemetry = {};
50
+ }
51
+ currentConfig.telemetry.enabled = telemetryEnabled;
52
+ }
53
+
54
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(currentConfig, null, 2), 'utf-8');
55
+
56
+ res.json({
57
+ message: 'Настройки сохранены. Для применения требуется перезапуск панели.',
58
+ requiresRestart: true
59
+ });
60
+
61
+ } catch (error) {
62
+ console.error("Ошибка обновления файла конфигурации:", error);
63
+ res.status(500).json({ error: 'Не удалось сохранить настройки.' });
64
+ }
65
+ });
66
+
67
+ module.exports = router;
@@ -1,15 +1,17 @@
1
1
  const express = require('express');
2
2
  const router = express.Router();
3
3
  const { PrismaClient } = require('@prisma/client');
4
+ const { authenticate, authorize } = require('../middleware/auth');
4
5
  const prisma = new PrismaClient();
5
6
 
7
+ router.use(authenticate);
6
8
 
7
- router.get('/groups', async (req, res) => {
9
+ router.get('/groups', authorize('panel:role:view'), async (req, res) => {
8
10
  const groups = await prisma.group.findMany({ include: { permissions: { include: { permission: true } } } });
9
11
  res.json(groups);
10
12
  });
11
13
 
12
- router.post('/groups', async (req, res) => {
14
+ router.post('/groups', authorize('panel:role:edit'), async (req, res) => {
13
15
  const { name, permissionIds } = req.body;
14
16
  const newGroup = await prisma.group.create({
15
17
  data: {
@@ -24,7 +26,7 @@ router.post('/groups', async (req, res) => {
24
26
 
25
27
 
26
28
 
27
- router.put('/groups/:id', async (req, res) => {
29
+ router.put('/groups/:id', authorize('panel:role:edit'), async (req, res) => {
28
30
  const groupId = parseInt(req.params.id);
29
31
  const { name, permissionIds } = req.body;
30
32
  try {
@@ -40,20 +42,20 @@ router.put('/groups/:id', async (req, res) => {
40
42
  res.status(200).send();
41
43
  } catch (error) { res.status(500).json({ error: 'Не удалось обновить группу' }); }
42
44
  });
43
- router.delete('/groups/:id', async (req, res) => {
45
+ router.delete('/groups/:id', authorize('panel:role:edit'), async (req, res) => {
44
46
  try {
45
47
  await prisma.group.delete({ where: { id: parseInt(req.params.id) } });
46
48
  res.status(204).send();
47
49
  } catch (error) { res.status(500).json({ error: 'Не удалось удалить группу' }); }
48
50
  });
49
51
 
50
- router.get('/all', async (req, res) => {
52
+ router.get('/all', authorize('panel:role:view'), async (req, res) => {
51
53
  const permissions = await prisma.permission.findMany({ orderBy: { name: 'asc' } });
52
54
  res.json(permissions);
53
55
  });
54
56
 
55
57
 
56
- router.get('/users/:username', async (req, res) => {
58
+ router.get('/users/:username', authorize('panel:user:view'), async (req, res) => {
57
59
  const user = await prisma.user.findUnique({
58
60
  where: { username: req.params.username },
59
61
  include: { groups: { include: { group: true } } }
@@ -62,7 +64,7 @@ router.get('/users/:username', async (req, res) => {
62
64
  res.json(user);
63
65
  });
64
66
 
65
- router.post('/users/:username/groups', async (req, res) => {
67
+ router.post('/users/:username/groups', authorize('panel:user:edit'), async (req, res) => {
66
68
  const { groupId } = req.body;
67
69
  const user = await prisma.user.upsert({
68
70
  where: { username: req.params.username },
@@ -1,15 +1,16 @@
1
-
2
1
  const express = require('express');
2
+ const { authenticate, authorize } = require('../middleware/auth');
3
3
  const router = express.Router();
4
4
  const PluginManager = require('../../core/PluginManager');
5
5
 
6
6
  const OFFICIAL_CATALOG_URL = "https://raw.githubusercontent.com/blockmineJS/official-plugins-list/main/index.json";
7
7
 
8
+ router.use(authenticate);
8
9
 
9
10
  const getCacheBustedUrl = (url) => `${url}?t=${new Date().getTime()}`;
10
11
 
11
12
 
12
- router.get('/catalog', async (req, res) => {
13
+ router.get('/catalog', authorize('plugin:browse'), async (req, res) => {
13
14
  try {
14
15
  const response = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
15
16
 
@@ -26,7 +27,7 @@ router.get('/catalog', async (req, res) => {
26
27
  }
27
28
  });
28
29
 
29
- router.post('/check-updates/:botId', async (req, res) => {
30
+ router.post('/check-updates/:botId', authorize('plugin:update'), async (req, res) => {
30
31
  try {
31
32
  const botId = parseInt(req.params.botId);
32
33
 
@@ -42,7 +43,7 @@ router.post('/check-updates/:botId', async (req, res) => {
42
43
  }
43
44
  });
44
45
 
45
- router.post('/update/:pluginId', async (req, res) => {
46
+ router.post('/update/:pluginId', authorize('plugin:update'), async (req, res) => {
46
47
  try {
47
48
  const pluginId = parseInt(req.params.pluginId);
48
49
  const updatedPlugin = await PluginManager.updatePlugin(pluginId);
@@ -52,7 +53,7 @@ router.post('/update/:pluginId', async (req, res) => {
52
53
  }
53
54
  });
54
55
 
55
- router.get('/catalog/:name', async (req, res) => {
56
+ router.get('/catalog/:name', authorize('plugin:browse'), async (req, res) => {
56
57
  try {
57
58
  const pluginName = req.params.name;
58
59
 
@@ -1,9 +1,12 @@
1
1
  const express = require('express');
2
2
  const router = express.Router();
3
3
  const { PrismaClient } = require('@prisma/client');
4
+ const { authenticate, authorize } = require('../middleware/auth');
4
5
  const prisma = new PrismaClient();
5
6
 
6
- router.get('/', async (req, res) => {
7
+ router.use(authenticate);
8
+
9
+ router.get('/', authorize('server:list'), async (req, res) => {
7
10
  try {
8
11
  const servers = await prisma.server.findMany({ orderBy: { name: 'asc' } });
9
12
  res.json(servers);
@@ -13,7 +16,7 @@ router.get('/', async (req, res) => {
13
16
  }
14
17
  });
15
18
 
16
- router.post('/', async (req, res) => {
19
+ router.post('/', authorize('server:create'), async (req, res) => {
17
20
  try {
18
21
  const { name, host, port, version } = req.body;
19
22
  if (!name || !host || !version) {
@@ -29,10 +32,7 @@ router.post('/', async (req, res) => {
29
32
  }
30
33
  });
31
34
 
32
-
33
-
34
-
35
- router.delete('/:id', async (req, res) => {
35
+ router.delete('/:id', authorize('server:delete'), async (req, res) => {
36
36
  try {
37
37
  const serverId = parseInt(req.params.id, 10);
38
38
 
@@ -2,17 +2,20 @@ const express = require('express');
2
2
  const router = express.Router();
3
3
  const { PrismaClient } = require('@prisma/client');
4
4
  const TaskScheduler = require('../../core/TaskScheduler');
5
+ const { authenticate, authorize } = require('../middleware/auth');
5
6
 
6
7
  const { CronExpressionParser } = require('cron-parser');
7
8
 
8
9
  const prisma = new PrismaClient();
9
10
 
11
+ router.use(authenticate);
12
+
10
13
  const normalizeCronPattern = (pattern) => {
11
14
  if (typeof pattern !== 'string') return '* * * * *';
12
15
  return pattern.replace(/\*\/1/g, '*').trim();
13
16
  };
14
17
 
15
- router.get('/', async (req, res) => {
18
+ router.get('/', authorize('task:list'), async (req, res) => {
16
19
  try {
17
20
  const tasks = await prisma.scheduledTask.findMany({ orderBy: { createdAt: 'desc' } });
18
21
  const normalizedTasks = tasks.map(task => ({
@@ -26,7 +29,7 @@ router.get('/', async (req, res) => {
26
29
  }
27
30
  });
28
31
 
29
- router.post('/', async (req, res) => {
32
+ router.post('/', authorize('task:create'), async (req, res) => {
30
33
  try {
31
34
  const taskData = { ...req.body, cronPattern: normalizeCronPattern(req.body.cronPattern) };
32
35
  const newTask = await prisma.scheduledTask.create({ data: taskData });
@@ -38,7 +41,7 @@ router.post('/', async (req, res) => {
38
41
  }
39
42
  });
40
43
 
41
- router.put('/:id', async (req, res) => {
44
+ router.put('/:id', authorize('task:edit'), async (req, res) => {
42
45
  const taskId = parseInt(req.params.id, 10);
43
46
  try {
44
47
  const { id, createdAt, updatedAt, lastRun, ...dataToUpdate } = req.body;
@@ -59,7 +62,7 @@ router.put('/:id', async (req, res) => {
59
62
  }
60
63
  });
61
64
 
62
- router.delete('/:id', async (req, res) => {
65
+ router.delete('/:id', authorize('task:delete'), async (req, res) => {
63
66
  const taskId = parseInt(req.params.id, 10);
64
67
  try {
65
68
  await prisma.scheduledTask.delete({ where: { id: taskId } });
@@ -72,7 +75,7 @@ router.delete('/:id', async (req, res) => {
72
75
  });
73
76
 
74
77
 
75
- router.post('/describe', (req, res) => {
78
+ router.post('/describe', authorize('task:list'), (req, res) => {
76
79
  const { pattern } = req.body;
77
80
 
78
81
  if (!pattern) {
@@ -0,0 +1,78 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const crypto = require('crypto');
5
+
6
+ const DATA_DIR = path.join(os.homedir(), '.blockmine');
7
+ const CONFIG_PATH = path.join(DATA_DIR, 'config.json');
8
+
9
+ let config = null;
10
+
11
+ function generateInitialConfig() {
12
+ console.log('[Config] Файл конфигурации не найден. Генерируем новый...');
13
+
14
+ const isLinux = process.platform === 'linux';
15
+
16
+ const newConfig = {
17
+ server: {
18
+ host: isLinux ? '0.0.0.0' : '127.0.0.1',
19
+ port: 3001,
20
+ allowExternalAccess: isLinux,
21
+ },
22
+ security: {
23
+ jwtSecret: crypto.randomBytes(64).toString('hex'),
24
+ encryptionKey: crypto.randomBytes(32).toString('hex'),
25
+ adminRecoveryCode: `bmr-${crypto.randomBytes(12).toString('hex')}`
26
+ },
27
+ telemetry: {
28
+ enabled: true
29
+ }
30
+ };
31
+
32
+ if (!fs.existsSync(DATA_DIR)) {
33
+ fs.mkdirSync(DATA_DIR, { recursive: true });
34
+ }
35
+
36
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(newConfig, null, 2), 'utf-8');
37
+
38
+ console.log('================================================================');
39
+ console.log('ВАЖНО: Конфигурация сгенерирована!');
40
+ console.log(`Файл сохранен в: ${CONFIG_PATH}`);
41
+
42
+ if (isLinux) {
43
+ console.log('\n[Linux] Обнаружена система Linux. Внешний доступ к панели включен по умолчанию.');
44
+ console.log('Просмотр панели будет доступен с внешнего IP адреса вашего сервера.');
45
+ console.log(`Чтобы отключить это, измените "allowExternalAccess" на false в файле конфигурации:`);
46
+ console.log(CONFIG_PATH);
47
+ }
48
+
49
+ console.log('\nПожалуйста, сохраните этот код восстановления в безопасном месте.');
50
+ console.log('Он понадобится для сброса пароля администратора.');
51
+ console.log(`\n Код восстановления: ${newConfig.security.adminRecoveryCode}\n`);
52
+ console.log('================================================================');
53
+
54
+ return newConfig;
55
+ }
56
+
57
+ function loadConfig() {
58
+ if (config) {
59
+ return config;
60
+ }
61
+
62
+ try {
63
+ if (fs.existsSync(CONFIG_PATH)) {
64
+ const fileContent = fs.readFileSync(CONFIG_PATH, 'utf-8');
65
+ config = JSON.parse(fileContent);
66
+ console.log(`[Config] Конфигурация успешно загружена из ${CONFIG_PATH}`);
67
+ } else {
68
+ config = generateInitialConfig();
69
+ }
70
+ } catch (error) {
71
+ console.error('[Config] КРИТИЧЕСКАЯ ОШИБКА: Не удалось загрузить или создать файл конфигурации.', error);
72
+ process.exit(1);
73
+ }
74
+
75
+ return config;
76
+ }
77
+
78
+ module.exports = loadConfig();