blockmine 1.3.21 → 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.
- package/backend/cli.js +4 -1
- package/backend/package.json +2 -1
- package/backend/prisma/migrations/20250617133815_add_panel_users_and_roles/migration.sql +26 -0
- package/backend/prisma/schema.prisma +21 -1
- package/backend/src/api/middleware/auth.js +59 -0
- package/backend/src/api/routes/auth.js +445 -0
- package/backend/src/api/routes/bots.js +55 -34
- package/backend/src/api/routes/panel.js +67 -0
- package/backend/src/api/routes/permissions.js +9 -7
- package/backend/src/api/routes/plugins.js +6 -5
- package/backend/src/api/routes/servers.js +6 -6
- package/backend/src/api/routes/tasks.js +8 -5
- package/backend/src/config.js +78 -0
- package/backend/src/core/BotManager.js +17 -15
- package/backend/src/core/utils/crypto.js +49 -0
- package/backend/src/real-time/socketHandler.js +15 -7
- package/backend/src/server.js +44 -21
- package/frontend/dist/assets/index-BJe5Bwf5.js +8179 -0
- package/frontend/dist/assets/index-BKT73TwN.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-BP4_jhRx.css +0 -1
- package/frontend/dist/assets/index-Bz8G6akb.js +0 -8179
|
@@ -4,11 +4,14 @@ const { getIO } = require('../real-time/socketHandler');
|
|
|
4
4
|
const { PrismaClient } = require('@prisma/client');
|
|
5
5
|
const pidusage = require('pidusage');
|
|
6
6
|
const DependencyService = require('./DependencyService');
|
|
7
|
+
const config = require('../config');
|
|
7
8
|
|
|
8
9
|
const fs = require('fs');
|
|
9
10
|
const os = require('os');
|
|
10
11
|
const { v4: uuidv4 } = require('uuid');
|
|
11
12
|
const crypto = require('crypto');
|
|
13
|
+
const { decrypt } = require('./utils/crypto');
|
|
14
|
+
|
|
12
15
|
|
|
13
16
|
const RealPermissionManager = require('./PermissionManager');
|
|
14
17
|
const RealUserService = require('./UserService');
|
|
@@ -19,23 +22,10 @@ const warningCache = new Map();
|
|
|
19
22
|
const WARNING_COOLDOWN = 10 * 1000;
|
|
20
23
|
|
|
21
24
|
|
|
22
|
-
const TELEMETRY_ENABLED = 'true';
|
|
23
25
|
const STATS_SERVER_URL = 'http://185.65.200.184:3000';
|
|
24
26
|
let instanceId = null;
|
|
25
27
|
const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
26
28
|
|
|
27
|
-
if (TELEMETRY_ENABLED && STATS_SERVER_URL) {
|
|
28
|
-
const idPath = path.join(DATA_DIR, '.instance_id');
|
|
29
|
-
try {
|
|
30
|
-
if (!fs.existsSync(DATA_DIR)) {
|
|
31
|
-
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
instanceId = fs.readFileSync(idPath, 'utf-8');
|
|
34
|
-
} catch (e) {
|
|
35
|
-
instanceId = uuidv4();
|
|
36
|
-
fs.writeFileSync(idPath, instanceId, 'utf-8');
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
29
|
|
|
40
30
|
class BotManager {
|
|
41
31
|
constructor() {
|
|
@@ -47,7 +37,7 @@ class BotManager {
|
|
|
47
37
|
setInterval(() => this.updateAllResourceUsage(), 5000);
|
|
48
38
|
|
|
49
39
|
|
|
50
|
-
if (
|
|
40
|
+
if (config.telemetry?.enabled) {
|
|
51
41
|
setInterval(() => this.sendHeartbeat(), 5 * 60 * 1000);
|
|
52
42
|
}
|
|
53
43
|
}
|
|
@@ -90,6 +80,8 @@ class BotManager {
|
|
|
90
80
|
}
|
|
91
81
|
|
|
92
82
|
triggerHeartbeat() {
|
|
83
|
+
if (!config.telemetry?.enabled) return;
|
|
84
|
+
|
|
93
85
|
if (this.heartbeatDebounceTimer) {
|
|
94
86
|
clearTimeout(this.heartbeatDebounceTimer);
|
|
95
87
|
}
|
|
@@ -112,6 +104,8 @@ class BotManager {
|
|
|
112
104
|
|
|
113
105
|
|
|
114
106
|
async sendHeartbeat() {
|
|
107
|
+
if (!config.telemetry?.enabled) return;
|
|
108
|
+
|
|
115
109
|
if (!instanceId) return;
|
|
116
110
|
|
|
117
111
|
try {
|
|
@@ -325,7 +319,15 @@ class BotManager {
|
|
|
325
319
|
return { success: false, message: 'Критические ошибки в зависимостях плагинов.' };
|
|
326
320
|
}
|
|
327
321
|
|
|
328
|
-
const
|
|
322
|
+
const decryptedConfig = { ...botConfig };
|
|
323
|
+
if (decryptedConfig.password) {
|
|
324
|
+
decryptedConfig.password = decrypt(decryptedConfig.password);
|
|
325
|
+
}
|
|
326
|
+
if (decryptedConfig.proxyPassword) {
|
|
327
|
+
decryptedConfig.proxyPassword = decrypt(decryptedConfig.proxyPassword);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const fullBotConfig = { ...decryptedConfig, plugins: sortedPlugins };
|
|
329
331
|
|
|
330
332
|
const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
|
|
331
333
|
const child = fork(botProcessPath, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
const config = require('../../config');
|
|
4
|
+
|
|
5
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
6
|
+
const IV_LENGTH = 16;
|
|
7
|
+
const AUTH_TAG_LENGTH = 16;
|
|
8
|
+
const KEY = Buffer.from(config.security.encryptionKey, 'hex');
|
|
9
|
+
|
|
10
|
+
function encrypt(text) {
|
|
11
|
+
if (!text) return null;
|
|
12
|
+
try {
|
|
13
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
14
|
+
const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);
|
|
15
|
+
const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
|
|
16
|
+
const authTag = cipher.getAuthTag();
|
|
17
|
+
|
|
18
|
+
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted.toString('hex')}`;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('[Crypto] Ошибка шифрования:', error);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function decrypt(hash) {
|
|
26
|
+
if (!hash) return null;
|
|
27
|
+
try {
|
|
28
|
+
const parts = hash.split(':');
|
|
29
|
+
if (parts.length !== 3) {
|
|
30
|
+
console.error('[Crypto] Неверный формат зашифрованных данных. Возвращаем как есть.');
|
|
31
|
+
return hash;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
35
|
+
const authTag = Buffer.from(parts[1], 'hex');
|
|
36
|
+
const encrypted = Buffer.from(parts[2], 'hex');
|
|
37
|
+
|
|
38
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, KEY, iv);
|
|
39
|
+
decipher.setAuthTag(authTag);
|
|
40
|
+
|
|
41
|
+
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
42
|
+
return decrypted.toString('utf8');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('[Crypto] Ошибка дешифрования. Возможно, ключ был изменен или данные повреждены.', error);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { encrypt, decrypt };
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
const { Server } = require('socket.io');
|
|
2
|
+
const config = require('../config');
|
|
2
3
|
|
|
3
4
|
let io;
|
|
4
5
|
|
|
5
6
|
function initializeSocket(httpServer) {
|
|
7
|
+
const corsOptions = {
|
|
8
|
+
methods: ["GET", "POST"]
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
if (config.server.allowExternalAccess) {
|
|
12
|
+
corsOptions.origin = "*";
|
|
13
|
+
} else {
|
|
14
|
+
corsOptions.origin = "http://localhost:5173";
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
io = new Server(httpServer, {
|
|
7
|
-
cors:
|
|
8
|
-
origin: "http://localhost:5173",
|
|
9
|
-
methods: ["GET", "POST"]
|
|
10
|
-
}
|
|
18
|
+
cors: corsOptions
|
|
11
19
|
});
|
|
12
20
|
|
|
13
21
|
io.on('connection', (socket) => {
|
|
14
|
-
console.log('
|
|
22
|
+
console.log('Socket.IO: Пользователь подключен -', socket.id);
|
|
15
23
|
socket.on('disconnect', () => {
|
|
16
|
-
console.log('
|
|
24
|
+
console.log('Socket.IO: Пользователь отключен -', socket.id);
|
|
17
25
|
});
|
|
18
26
|
});
|
|
19
27
|
|
|
20
|
-
console.log('Socket.IO
|
|
28
|
+
console.log('Socket.IO инициализирован с CORS для:', corsOptions.origin);
|
|
21
29
|
return io;
|
|
22
30
|
}
|
|
23
31
|
|
package/backend/src/server.js
CHANGED
|
@@ -2,49 +2,56 @@ const express = require('express');
|
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
5
6
|
|
|
7
|
+
const config = require('./config');
|
|
6
8
|
const { initializeSocket } = require('./real-time/socketHandler');
|
|
7
9
|
const botRoutes = require('./api/routes/bots');
|
|
8
10
|
const pluginRoutes = require('./api/routes/plugins');
|
|
9
11
|
const serverRoutes = require('./api/routes/servers');
|
|
10
12
|
const permissionsRoutes = require('./api/routes/permissions');
|
|
11
13
|
const taskRoutes = require('./api/routes/tasks');
|
|
14
|
+
const authRoutes = require('./api/routes/auth');
|
|
12
15
|
const BotManager = require('./core/BotManager');
|
|
13
16
|
const TaskScheduler = require('./core/TaskScheduler');
|
|
17
|
+
const panelRoutes = require('./api/routes/panel');
|
|
18
|
+
|
|
14
19
|
|
|
15
20
|
const app = express();
|
|
16
21
|
const server = http.createServer(app);
|
|
17
22
|
|
|
18
|
-
initializeSocket(server);
|
|
23
|
+
initializeSocket(server);
|
|
19
24
|
|
|
20
|
-
const PORT =
|
|
25
|
+
const PORT = config.server.port;
|
|
26
|
+
const HOST = config.server.host;
|
|
21
27
|
|
|
22
28
|
app.use(express.json());
|
|
23
29
|
|
|
24
|
-
|
|
25
30
|
const frontendPath = path.resolve(__dirname, '..', '..', 'frontend', 'dist');
|
|
26
31
|
const rootPath = path.resolve(__dirname, '..', '..');
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
app.use('/api/auth', authRoutes);
|
|
34
|
+
|
|
35
|
+
app.use('/api/version', (req, res, next) => {
|
|
36
|
+
async function getVersion() {
|
|
37
|
+
try {
|
|
38
|
+
const packageJsonPath = path.join(rootPath, 'package.json');
|
|
39
|
+
const packageJsonData = await fs.promises.readFile(packageJsonPath, 'utf-8');
|
|
40
|
+
const { version } = JSON.parse(packageJsonData);
|
|
41
|
+
res.json({ version });
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("Failed to read app version:", error);
|
|
44
|
+
res.status(500).json({ error: 'Could not retrieve app version.' });
|
|
45
|
+
}
|
|
38
46
|
}
|
|
47
|
+
getVersion();
|
|
39
48
|
});
|
|
40
|
-
|
|
41
49
|
app.use('/api/tasks', taskRoutes);
|
|
42
50
|
app.use('/api/bots', botRoutes);
|
|
43
51
|
app.use('/api/plugins', pluginRoutes);
|
|
44
52
|
app.use('/api/servers', serverRoutes);
|
|
45
53
|
app.use('/api/permissions', permissionsRoutes);
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
app.use('/api/panel', panelRoutes);
|
|
48
55
|
|
|
49
56
|
app.use(express.static(frontendPath));
|
|
50
57
|
|
|
@@ -56,23 +63,39 @@ app.get(/^(?!\/api).*/, (req, res) => {
|
|
|
56
63
|
} else {
|
|
57
64
|
console.error(`Критическая ошибка: файл index.html не найден по пути ${indexPath}`);
|
|
58
65
|
res.status(404).send(
|
|
59
|
-
'<h1>Файлы фронтенда не найдены!
|
|
66
|
+
'<h1>Файлы фронтенда не найдены! Соберите фронтенд командой "npm run build --workspace=frontend"</h1>'
|
|
60
67
|
);
|
|
61
68
|
}
|
|
62
69
|
});
|
|
63
70
|
|
|
64
71
|
async function startServer() {
|
|
65
72
|
return new Promise((resolve) => {
|
|
66
|
-
server.listen(PORT, async () => {
|
|
67
|
-
console.log(
|
|
68
|
-
|
|
73
|
+
server.listen(PORT, HOST, async () => {
|
|
74
|
+
console.log(`\nBackend сервер успешно запущен на http://${HOST}:${PORT}`);
|
|
75
|
+
|
|
76
|
+
if (HOST === '0.0.0.0') {
|
|
77
|
+
const networkInterfaces = os.networkInterfaces();
|
|
78
|
+
console.log('Панель управления доступна по следующим адресам:');
|
|
79
|
+
console.log(` - Локально: http://localhost:${PORT}`);
|
|
80
|
+
Object.keys(networkInterfaces).forEach(ifaceName => {
|
|
81
|
+
networkInterfaces[ifaceName].forEach(iface => {
|
|
82
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
83
|
+
console.log(` - В сети: http://${iface.address}:${PORT}`);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
console.log(' - А также по вашему внешнему IP адресу.');
|
|
88
|
+
|
|
89
|
+
} else {
|
|
90
|
+
console.log(`Панель управления доступна по адресу: http://localhost:${PORT}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
69
93
|
await TaskScheduler.initialize();
|
|
70
94
|
resolve(server);
|
|
71
95
|
});
|
|
72
96
|
});
|
|
73
97
|
}
|
|
74
98
|
|
|
75
|
-
|
|
76
99
|
const gracefulShutdown = (signal) => {
|
|
77
100
|
console.log(`[Shutdown] Получен сигнал ${signal}. Начинаем завершение...`);
|
|
78
101
|
|