blockmine 1.22.0 → 1.23.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/.claude/settings.json +5 -1
- package/.claude/settings.local.json +10 -1
- package/CHANGELOG.md +27 -3
- package/CLAUDE.md +284 -0
- package/README.md +302 -152
- package/backend/package-lock.json +681 -9
- package/backend/package.json +8 -0
- package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
- package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
- package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
- package/backend/prisma/migrations/migration_lock.toml +2 -2
- package/backend/prisma/schema.prisma +70 -1
- package/backend/src/__tests__/services/BotLifecycleService.test.js +9 -4
- package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
- package/backend/src/api/middleware/auth.js +27 -0
- package/backend/src/api/middleware/botAccess.js +7 -3
- package/backend/src/api/middleware/panelApiAuth.js +135 -0
- package/backend/src/api/routes/aiAssistant.js +995 -0
- package/backend/src/api/routes/auth.js +669 -633
- package/backend/src/api/routes/botCommands.js +107 -0
- package/backend/src/api/routes/botGroups.js +165 -0
- package/backend/src/api/routes/botHistory.js +108 -0
- package/backend/src/api/routes/botPermissions.js +99 -0
- package/backend/src/api/routes/botStatus.js +36 -0
- package/backend/src/api/routes/botUsers.js +162 -0
- package/backend/src/api/routes/bots.js +2451 -2402
- package/backend/src/api/routes/eventGraphs.js +4 -1
- package/backend/src/api/routes/logs.js +13 -3
- package/backend/src/api/routes/panel.js +66 -66
- package/backend/src/api/routes/panelApiKeys.js +179 -0
- package/backend/src/api/routes/pluginIde.js +1715 -135
- package/backend/src/api/routes/plugins.js +376 -219
- package/backend/src/api/routes/proxies.js +130 -0
- package/backend/src/api/routes/search.js +4 -0
- package/backend/src/api/routes/servers.js +20 -3
- package/backend/src/api/routes/settings.js +5 -0
- package/backend/src/api/routes/system.js +174 -174
- package/backend/src/api/routes/traces.js +131 -0
- package/backend/src/config/debug.config.js +36 -0
- package/backend/src/core/BotHistoryStore.js +180 -0
- package/backend/src/core/BotManager.js +14 -4
- package/backend/src/core/BotProcess.js +1517 -1092
- package/backend/src/core/EventGraphManager.js +37 -123
- package/backend/src/core/GraphExecutionEngine.js +977 -321
- package/backend/src/core/MessageQueue.js +12 -6
- package/backend/src/core/PluginLoader.js +99 -5
- package/backend/src/core/PluginManager.js +74 -13
- package/backend/src/core/TaskScheduler.js +1 -1
- package/backend/src/core/commands/whois.js +1 -1
- package/backend/src/core/node-registries/actions.js +70 -0
- package/backend/src/core/node-registries/arrays.js +18 -0
- package/backend/src/core/node-registries/data.js +1 -1
- package/backend/src/core/node-registries/events.js +14 -0
- package/backend/src/core/node-registries/logic.js +17 -0
- package/backend/src/core/node-registries/strings.js +34 -0
- package/backend/src/core/node-registries/type.js +25 -0
- package/backend/src/core/nodes/actions/bot_look_at.js +1 -1
- package/backend/src/core/nodes/actions/create_command.js +189 -0
- package/backend/src/core/nodes/actions/delete_command.js +92 -0
- package/backend/src/core/nodes/actions/update_command.js +133 -0
- package/backend/src/core/nodes/arrays/join.js +28 -0
- package/backend/src/core/nodes/data/cast.js +2 -1
- package/backend/src/core/nodes/logic/not.js +22 -0
- package/backend/src/core/nodes/strings/starts_with.js +1 -1
- package/backend/src/core/nodes/strings/to_lower.js +22 -0
- package/backend/src/core/nodes/strings/to_upper.js +22 -0
- package/backend/src/core/nodes/type/to_string.js +32 -0
- package/backend/src/core/services/BotLifecycleService.js +255 -16
- package/backend/src/core/services/CommandExecutionService.js +430 -351
- package/backend/src/core/services/DebugSessionManager.js +347 -0
- package/backend/src/core/services/GraphCollaborationManager.js +501 -0
- package/backend/src/core/services/MinecraftBotManager.js +259 -0
- package/backend/src/core/services/MinecraftViewerService.js +216 -0
- package/backend/src/core/services/TraceCollectorService.js +545 -0
- package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
- package/backend/src/core/system/Transport.js +0 -4
- package/backend/src/core/validation/nodeSchemas.js +6 -6
- package/backend/src/real-time/botApi/handlers/graphHandlers.js +2 -2
- package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +1 -1
- package/backend/src/real-time/botApi/utils.js +11 -0
- package/backend/src/real-time/panelNamespace.js +387 -0
- package/backend/src/real-time/presence.js +7 -2
- package/backend/src/real-time/socketHandler.js +395 -4
- package/backend/src/server.js +18 -0
- package/frontend/dist/assets/index-B1serztM.js +11210 -0
- package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package-lock.json +9437 -0
- package/frontend/package.json +8 -0
- package/package.json +2 -2
- package/screen/console.png +0 -0
- package/screen/dashboard.png +0 -0
- package/screen/graph_collabe.png +0 -0
- package/screen/graph_live_debug.png +0 -0
- package/screen/management_command.png +0 -0
- package/screen/node_debug_trace.png +0 -0
- package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
- package/screen/websocket.png +0 -0
- package/screen//320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270_/320/276/321/202/320/264/320/265/320/273/321/214/320/275/321/213/321/205_/320/272/320/276/320/274/320/260/320/275/320/264_/320/272/320/260/320/266/320/264/321/203_/320/272/320/276/320/274/320/260/320/275/320/273/320/264/321/203_/320/274/320/276/320/266/320/275/320/276_/320/275/320/260/321/201/321/202/321/200/320/260/320/270/320/262/320/260/321/202/321/214.png +0 -0
- package/screen//320/277/320/273/320/260/320/275/320/270/321/200/320/276/320/262/321/211/320/270/320/272_/320/274/320/276/320/266/320/275/320/276_/320/267/320/260/320/264/320/260/320/262/320/260/321/202/321/214_/320/264/320/265/320/271/321/201/321/202/320/262/320/270/321/217_/320/277/320/276_/320/262/321/200/320/265/320/274/320/265/320/275/320/270.png +0 -0
- package/frontend/dist/assets/index-CfTo92bP.css +0 -1
- package/frontend/dist/assets/index-CiFD5X9Z.js +0 -8344
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { PrismaClient } = require('@prisma/client');
|
|
4
|
+
const { authenticateUniversal, authorize } = require('../middleware/auth');
|
|
5
|
+
const prisma = new PrismaClient();
|
|
6
|
+
|
|
7
|
+
router.use(authenticateUniversal);
|
|
8
|
+
|
|
9
|
+
router.get('/', authorize('proxy:list'), async (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const proxies = await prisma.proxy.findMany({
|
|
12
|
+
orderBy: { name: 'asc' },
|
|
13
|
+
include: {
|
|
14
|
+
_count: {
|
|
15
|
+
select: { bots: true }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
res.json({
|
|
20
|
+
items: proxies,
|
|
21
|
+
total: proxies.length
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("[API /api/proxies] Ошибка получения списка прокси:", error);
|
|
25
|
+
res.status(500).json({ error: 'Не удалось получить список прокси' });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
router.get('/:id', authorize('proxy:list'), async (req, res) => {
|
|
30
|
+
try {
|
|
31
|
+
const proxyId = parseInt(req.params.id, 10);
|
|
32
|
+
const proxy = await prisma.proxy.findUnique({
|
|
33
|
+
where: { id: proxyId },
|
|
34
|
+
include: {
|
|
35
|
+
_count: {
|
|
36
|
+
select: { bots: true }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
if (!proxy) {
|
|
41
|
+
return res.status(404).json({ error: 'Прокси не найден' });
|
|
42
|
+
}
|
|
43
|
+
res.json(proxy);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error("[API /api/proxies/:id] Ошибка получения прокси:", error);
|
|
46
|
+
res.status(500).json({ error: 'Не удалось получить прокси' });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
router.post('/', authorize('proxy:create'), async (req, res) => {
|
|
51
|
+
try {
|
|
52
|
+
const { name, type, host, port, username, password, note } = req.body;
|
|
53
|
+
if (!name || !host || !port) {
|
|
54
|
+
return res.status(400).json({ error: 'Имя, хост и порт прокси обязательны' });
|
|
55
|
+
}
|
|
56
|
+
if (type && !['socks5', 'http'].includes(type)) {
|
|
57
|
+
return res.status(400).json({ error: 'Тип прокси должен быть socks5 или http' });
|
|
58
|
+
}
|
|
59
|
+
const newProxy = await prisma.proxy.create({
|
|
60
|
+
data: {
|
|
61
|
+
name,
|
|
62
|
+
type: type || 'socks5',
|
|
63
|
+
host,
|
|
64
|
+
port: parseInt(port, 10),
|
|
65
|
+
username: username || null,
|
|
66
|
+
password: password || null,
|
|
67
|
+
note: note || null
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
res.status(201).json(newProxy);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error.code === 'P2002') return res.status(409).json({ error: 'Прокси с таким именем уже существует' });
|
|
73
|
+
console.error("[API /api/proxies] Ошибка создания прокси:", error);
|
|
74
|
+
res.status(500).json({ error: 'Не удалось создать прокси' });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
router.put('/:id', authorize('proxy:create'), async (req, res) => {
|
|
79
|
+
try {
|
|
80
|
+
const proxyId = parseInt(req.params.id, 10);
|
|
81
|
+
if (isNaN(proxyId)) return res.status(400).json({ error: 'Некорректный ID прокси' });
|
|
82
|
+
|
|
83
|
+
const { name, type, host, port, username, password, note } = req.body;
|
|
84
|
+
|
|
85
|
+
if (type && !['socks5', 'http'].includes(type)) {
|
|
86
|
+
return res.status(400).json({ error: 'Тип прокси должен быть socks5 или http' });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const dataToUpdate = {};
|
|
90
|
+
if (name !== undefined) dataToUpdate.name = name;
|
|
91
|
+
if (type !== undefined) dataToUpdate.type = type;
|
|
92
|
+
if (host !== undefined) dataToUpdate.host = host;
|
|
93
|
+
if (port !== undefined && port !== '') dataToUpdate.port = parseInt(port, 10);
|
|
94
|
+
if (username !== undefined) dataToUpdate.username = username || null;
|
|
95
|
+
if (password !== undefined) dataToUpdate.password = password || null;
|
|
96
|
+
if (note !== undefined) dataToUpdate.note = note || null;
|
|
97
|
+
|
|
98
|
+
Object.keys(dataToUpdate).forEach(k => { if (dataToUpdate[k] === undefined) delete dataToUpdate[k]; });
|
|
99
|
+
|
|
100
|
+
if (dataToUpdate.name) {
|
|
101
|
+
const existing = await prisma.proxy.findFirst({ where: { name: dataToUpdate.name, id: { not: proxyId } } });
|
|
102
|
+
if (existing) return res.status(409).json({ error: 'Прокси с таким именем уже существует' });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const updated = await prisma.proxy.update({ where: { id: proxyId }, data: dataToUpdate });
|
|
106
|
+
res.json(updated);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error('[API /api/proxies] Ошибка обновления прокси:', error);
|
|
109
|
+
res.status(500).json({ error: 'Не удалось обновить прокси' });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
router.delete('/:id', authorize('proxy:delete'), async (req, res) => {
|
|
114
|
+
try {
|
|
115
|
+
const proxyId = parseInt(req.params.id, 10);
|
|
116
|
+
|
|
117
|
+
const botsUsingProxy = await prisma.bot.count({ where: { proxyId } });
|
|
118
|
+
if (botsUsingProxy > 0) {
|
|
119
|
+
return res.status(400).json({ error: `Нельзя удалить прокси, так как он используется ${botsUsingProxy} ботом(ами).` });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await prisma.proxy.delete({ where: { id: proxyId } });
|
|
123
|
+
res.status(204).send();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('[API /api/proxies] Ошибка удаления прокси:', error);
|
|
126
|
+
res.status(500).json({ error: 'Не удалось удалить прокси' });
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
module.exports = router;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
|
+
const { authenticate } = require('../middleware/auth');
|
|
2
3
|
const router = express.Router();
|
|
3
4
|
const { PrismaClient } = require('@prisma/client');
|
|
4
5
|
const prisma = new PrismaClient();
|
|
5
6
|
|
|
7
|
+
// Все роуты требуют аутентификации
|
|
8
|
+
router.use(authenticate);
|
|
9
|
+
|
|
6
10
|
router.get('/', async (req, res) => {
|
|
7
11
|
const { query } = req.query;
|
|
8
12
|
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const router = express.Router();
|
|
3
3
|
const { PrismaClient } = require('@prisma/client');
|
|
4
|
-
const {
|
|
4
|
+
const { authenticateUniversal, authorize } = require('../middleware/auth');
|
|
5
5
|
const prisma = new PrismaClient();
|
|
6
6
|
|
|
7
|
-
router.use(
|
|
7
|
+
router.use(authenticateUniversal);
|
|
8
8
|
|
|
9
9
|
router.get('/', authorize('server:list'), async (req, res) => {
|
|
10
10
|
try {
|
|
11
11
|
const servers = await prisma.server.findMany({ orderBy: { name: 'asc' } });
|
|
12
|
-
res.json(
|
|
12
|
+
res.json({
|
|
13
|
+
items: servers,
|
|
14
|
+
total: servers.length
|
|
15
|
+
});
|
|
13
16
|
} catch (error) {
|
|
14
17
|
console.error("[API /api/servers] Ошибка получения списка серверов:", error);
|
|
15
18
|
res.status(500).json({ error: 'Не удалось получить список серверов' });
|
|
16
19
|
}
|
|
17
20
|
});
|
|
18
21
|
|
|
22
|
+
router.get('/:id', authorize('server:list'), async (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const serverId = parseInt(req.params.id, 10);
|
|
25
|
+
const server = await prisma.server.findUnique({ where: { id: serverId } });
|
|
26
|
+
if (!server) {
|
|
27
|
+
return res.status(404).json({ error: 'Сервер не найден' });
|
|
28
|
+
}
|
|
29
|
+
res.json(server);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("[API /api/servers/:id] Ошибка получения сервера:", error);
|
|
32
|
+
res.status(500).json({ error: 'Не удалось получить сервер' });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
19
36
|
router.post('/', authorize('server:create'), async (req, res) => {
|
|
20
37
|
try {
|
|
21
38
|
const { name, host, port, version } = req.body;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
|
+
const { authenticate, authorize } = require('../middleware/auth');
|
|
2
3
|
const router = express.Router();
|
|
3
4
|
const { PrismaClient } = require('@prisma/client');
|
|
4
5
|
const PluginService = require('../../core/PluginService');
|
|
5
6
|
|
|
6
7
|
const prisma = new PrismaClient();
|
|
7
8
|
|
|
9
|
+
// Все роуты требуют аутентификации и прав администратора
|
|
10
|
+
router.use(authenticate);
|
|
11
|
+
router.use(authorize('system:settings'));
|
|
12
|
+
|
|
8
13
|
router.get('/plugin-directories', async (req, res) => {
|
|
9
14
|
try {
|
|
10
15
|
const setting = await prisma.setting.findUnique({
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const { authenticate } = require('../middleware/auth');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const pidusage = require('pidusage');
|
|
5
|
-
const rateLimit = require('express-rate-limit');
|
|
6
|
-
|
|
7
|
-
const router = express.Router();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const serverStartTime = Date.now();
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Вычисляет системное использование CPU на основе средней загрузки
|
|
14
|
-
* Избегает состояния гонки, не используя глобальное состояние
|
|
15
|
-
*/
|
|
16
|
-
function getSystemCpuUsage() {
|
|
17
|
-
// Используем среднюю загрузку системы за 1 минуту
|
|
18
|
-
const loadAvg = os.loadavg()[0]; // 1-минутное среднее
|
|
19
|
-
const cpuCount = os.cpus().length;
|
|
20
|
-
// Конвертируем в процент (loadAvg / cpuCount * 100)
|
|
21
|
-
const cpuPercentage = Math.round((loadAvg / cpuCount) * 100);
|
|
22
|
-
return Math.max(0, Math.min(100, cpuPercentage));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @route GET /api/system/health
|
|
27
|
-
* @desc Получить информацию о здоровье системы
|
|
28
|
-
* @access Требуется авторизация
|
|
29
|
-
*/
|
|
30
|
-
router.get('/health',
|
|
31
|
-
try {
|
|
32
|
-
const uptime = process.uptime();
|
|
33
|
-
const serverUptime = (Date.now() - serverStartTime) / 1000;
|
|
34
|
-
|
|
35
|
-
const totalMemory = os.totalmem();
|
|
36
|
-
const freeMemory = os.freemem();
|
|
37
|
-
const usedMemory = totalMemory - freeMemory;
|
|
38
|
-
|
|
39
|
-
const cpus = os.cpus();
|
|
40
|
-
|
|
41
|
-
const systemCpuUsage = getSystemCpuUsage();
|
|
42
|
-
|
|
43
|
-
let panelCpu = 0;
|
|
44
|
-
let panelMemory = 0;
|
|
45
|
-
try {
|
|
46
|
-
const stats = await pidusage(process.pid);
|
|
47
|
-
panelCpu = parseFloat(stats.cpu.toFixed(1));
|
|
48
|
-
panelMemory = parseFloat((stats.memory / 1024 / 1024).toFixed(1)); // MB
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error('Ошибка получения статистики процесса:', error);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const platform = process.platform;
|
|
54
|
-
const arch = process.arch;
|
|
55
|
-
|
|
56
|
-
const platformName = {
|
|
57
|
-
'win32': 'Windows',
|
|
58
|
-
'linux': 'Linux',
|
|
59
|
-
'darwin': 'macOS',
|
|
60
|
-
'freebsd': 'FreeBSD',
|
|
61
|
-
'openbsd': 'OpenBSD',
|
|
62
|
-
'aix': 'AIX'
|
|
63
|
-
}[platform] || platform;
|
|
64
|
-
|
|
65
|
-
// Форматируем uptime
|
|
66
|
-
const formatUptime = (seconds) => {
|
|
67
|
-
const days = Math.floor(seconds / 86400);
|
|
68
|
-
const hours = Math.floor((seconds % 86400) / 3600);
|
|
69
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
70
|
-
|
|
71
|
-
if (days > 0) return `${days}д ${hours}ч`;
|
|
72
|
-
if (hours > 0) return `${hours}ч ${minutes}м`;
|
|
73
|
-
return `${minutes}м`;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
let websocketStatus = true;
|
|
77
|
-
let databaseStatus = true;
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const prisma = req.app.get('prisma') || require('../../lib/prisma');
|
|
81
|
-
await prisma.$queryRaw`SELECT 1`;
|
|
82
|
-
} catch (error) {
|
|
83
|
-
databaseStatus = false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const botManager = req.app.get('botManager');
|
|
88
|
-
websocketStatus = botManager !== null && botManager !== undefined;
|
|
89
|
-
} catch (error) {
|
|
90
|
-
websocketStatus = false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
res.json({
|
|
94
|
-
status: 'ok',
|
|
95
|
-
uptime: formatUptime(serverUptime),
|
|
96
|
-
uptimeSeconds: Math.floor(serverUptime),
|
|
97
|
-
processUptime: formatUptime(uptime),
|
|
98
|
-
memory: {
|
|
99
|
-
total: Math.round(totalMemory / 1024 / 1024), // MB
|
|
100
|
-
free: Math.round(freeMemory / 1024 / 1024), // MB
|
|
101
|
-
used: Math.round(usedMemory / 1024 / 1024), // MB
|
|
102
|
-
usedPercent: Math.round((usedMemory / totalMemory) * 100),
|
|
103
|
-
panel: panelMemory // Память используемая панелью
|
|
104
|
-
},
|
|
105
|
-
cpu: {
|
|
106
|
-
cores: cpus.length,
|
|
107
|
-
model: cpus[0]?.model || 'Unknown',
|
|
108
|
-
usage: systemCpuUsage,
|
|
109
|
-
panelUsage: panelCpu,
|
|
110
|
-
loadAverage: os.loadavg()
|
|
111
|
-
},
|
|
112
|
-
platform: platformName,
|
|
113
|
-
platformRaw: platform,
|
|
114
|
-
arch: arch,
|
|
115
|
-
osRelease: os.release(),
|
|
116
|
-
hostname: os.hostname(),
|
|
117
|
-
nodeVersion: process.version,
|
|
118
|
-
services: {
|
|
119
|
-
panel: true,
|
|
120
|
-
websocket: websocketStatus,
|
|
121
|
-
database: databaseStatus
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.error('Error getting system health:', error);
|
|
126
|
-
res.status(500).json({
|
|
127
|
-
error: 'Failed to get system health',
|
|
128
|
-
message: error.message
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @route GET /api/system/stats
|
|
135
|
-
* @desc Получить статистику системы
|
|
136
|
-
* @access Требуется авторизация
|
|
137
|
-
*/
|
|
138
|
-
router.get('/stats',
|
|
139
|
-
try {
|
|
140
|
-
const prisma = req.app.get('prisma') || require('../../lib/prisma');
|
|
141
|
-
|
|
142
|
-
const totalBots = await prisma.bot.count();
|
|
143
|
-
const totalServers = await prisma.server.count();
|
|
144
|
-
const totalUsers = await prisma.panelUser.count();
|
|
145
|
-
|
|
146
|
-
const botManager = req.app.get('botManager');
|
|
147
|
-
let runningBots = 0;
|
|
148
|
-
|
|
149
|
-
if (botManager && botManager.bots) {
|
|
150
|
-
runningBots = Array.from(botManager.bots.values())
|
|
151
|
-
.filter(bot => typeof bot.isRunning === 'function' && bot.isRunning())
|
|
152
|
-
.length;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
res.json({
|
|
156
|
-
bots: {
|
|
157
|
-
total: totalBots,
|
|
158
|
-
running: runningBots,
|
|
159
|
-
stopped: totalBots - runningBots
|
|
160
|
-
},
|
|
161
|
-
servers: totalServers,
|
|
162
|
-
users: totalUsers
|
|
163
|
-
});
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error('Error getting system stats:', error);
|
|
166
|
-
res.status(500).json({
|
|
167
|
-
error: 'Failed to get system stats',
|
|
168
|
-
message: error.message
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
module.exports = router;
|
|
174
|
-
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const { authenticate, authenticateUniversal } = require('../middleware/auth');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const pidusage = require('pidusage');
|
|
5
|
+
const rateLimit = require('express-rate-limit');
|
|
6
|
+
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const serverStartTime = Date.now();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Вычисляет системное использование CPU на основе средней загрузки
|
|
14
|
+
* Избегает состояния гонки, не используя глобальное состояние
|
|
15
|
+
*/
|
|
16
|
+
function getSystemCpuUsage() {
|
|
17
|
+
// Используем среднюю загрузку системы за 1 минуту
|
|
18
|
+
const loadAvg = os.loadavg()[0]; // 1-минутное среднее
|
|
19
|
+
const cpuCount = os.cpus().length;
|
|
20
|
+
// Конвертируем в процент (loadAvg / cpuCount * 100)
|
|
21
|
+
const cpuPercentage = Math.round((loadAvg / cpuCount) * 100);
|
|
22
|
+
return Math.max(0, Math.min(100, cpuPercentage));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @route GET /api/system/health
|
|
27
|
+
* @desc Получить информацию о здоровье системы
|
|
28
|
+
* @access Требуется авторизация
|
|
29
|
+
*/
|
|
30
|
+
router.get('/health', authenticateUniversal, async (req, res) => {
|
|
31
|
+
try {
|
|
32
|
+
const uptime = process.uptime();
|
|
33
|
+
const serverUptime = (Date.now() - serverStartTime) / 1000;
|
|
34
|
+
|
|
35
|
+
const totalMemory = os.totalmem();
|
|
36
|
+
const freeMemory = os.freemem();
|
|
37
|
+
const usedMemory = totalMemory - freeMemory;
|
|
38
|
+
|
|
39
|
+
const cpus = os.cpus();
|
|
40
|
+
|
|
41
|
+
const systemCpuUsage = getSystemCpuUsage();
|
|
42
|
+
|
|
43
|
+
let panelCpu = 0;
|
|
44
|
+
let panelMemory = 0;
|
|
45
|
+
try {
|
|
46
|
+
const stats = await pidusage(process.pid);
|
|
47
|
+
panelCpu = parseFloat(stats.cpu.toFixed(1));
|
|
48
|
+
panelMemory = parseFloat((stats.memory / 1024 / 1024).toFixed(1)); // MB
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Ошибка получения статистики процесса:', error);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const platform = process.platform;
|
|
54
|
+
const arch = process.arch;
|
|
55
|
+
|
|
56
|
+
const platformName = {
|
|
57
|
+
'win32': 'Windows',
|
|
58
|
+
'linux': 'Linux',
|
|
59
|
+
'darwin': 'macOS',
|
|
60
|
+
'freebsd': 'FreeBSD',
|
|
61
|
+
'openbsd': 'OpenBSD',
|
|
62
|
+
'aix': 'AIX'
|
|
63
|
+
}[platform] || platform;
|
|
64
|
+
|
|
65
|
+
// Форматируем uptime
|
|
66
|
+
const formatUptime = (seconds) => {
|
|
67
|
+
const days = Math.floor(seconds / 86400);
|
|
68
|
+
const hours = Math.floor((seconds % 86400) / 3600);
|
|
69
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
70
|
+
|
|
71
|
+
if (days > 0) return `${days}д ${hours}ч`;
|
|
72
|
+
if (hours > 0) return `${hours}ч ${minutes}м`;
|
|
73
|
+
return `${minutes}м`;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let websocketStatus = true;
|
|
77
|
+
let databaseStatus = true;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const prisma = req.app.get('prisma') || require('../../lib/prisma');
|
|
81
|
+
await prisma.$queryRaw`SELECT 1`;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
databaseStatus = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const botManager = req.app.get('botManager');
|
|
88
|
+
websocketStatus = botManager !== null && botManager !== undefined;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
websocketStatus = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
res.json({
|
|
94
|
+
status: 'ok',
|
|
95
|
+
uptime: formatUptime(serverUptime),
|
|
96
|
+
uptimeSeconds: Math.floor(serverUptime),
|
|
97
|
+
processUptime: formatUptime(uptime),
|
|
98
|
+
memory: {
|
|
99
|
+
total: Math.round(totalMemory / 1024 / 1024), // MB
|
|
100
|
+
free: Math.round(freeMemory / 1024 / 1024), // MB
|
|
101
|
+
used: Math.round(usedMemory / 1024 / 1024), // MB
|
|
102
|
+
usedPercent: Math.round((usedMemory / totalMemory) * 100),
|
|
103
|
+
panel: panelMemory // Память используемая панелью
|
|
104
|
+
},
|
|
105
|
+
cpu: {
|
|
106
|
+
cores: cpus.length,
|
|
107
|
+
model: cpus[0]?.model || 'Unknown',
|
|
108
|
+
usage: systemCpuUsage,
|
|
109
|
+
panelUsage: panelCpu,
|
|
110
|
+
loadAverage: os.loadavg()
|
|
111
|
+
},
|
|
112
|
+
platform: platformName,
|
|
113
|
+
platformRaw: platform,
|
|
114
|
+
arch: arch,
|
|
115
|
+
osRelease: os.release(),
|
|
116
|
+
hostname: os.hostname(),
|
|
117
|
+
nodeVersion: process.version,
|
|
118
|
+
services: {
|
|
119
|
+
panel: true,
|
|
120
|
+
websocket: websocketStatus,
|
|
121
|
+
database: databaseStatus
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('Error getting system health:', error);
|
|
126
|
+
res.status(500).json({
|
|
127
|
+
error: 'Failed to get system health',
|
|
128
|
+
message: error.message
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @route GET /api/system/stats
|
|
135
|
+
* @desc Получить статистику системы
|
|
136
|
+
* @access Требуется авторизация
|
|
137
|
+
*/
|
|
138
|
+
router.get('/stats', authenticateUniversal, async (req, res) => {
|
|
139
|
+
try {
|
|
140
|
+
const prisma = req.app.get('prisma') || require('../../lib/prisma');
|
|
141
|
+
|
|
142
|
+
const totalBots = await prisma.bot.count();
|
|
143
|
+
const totalServers = await prisma.server.count();
|
|
144
|
+
const totalUsers = await prisma.panelUser.count();
|
|
145
|
+
|
|
146
|
+
const botManager = req.app.get('botManager');
|
|
147
|
+
let runningBots = 0;
|
|
148
|
+
|
|
149
|
+
if (botManager && botManager.bots) {
|
|
150
|
+
runningBots = Array.from(botManager.bots.values())
|
|
151
|
+
.filter(bot => typeof bot.isRunning === 'function' && bot.isRunning())
|
|
152
|
+
.length;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
res.json({
|
|
156
|
+
bots: {
|
|
157
|
+
total: totalBots,
|
|
158
|
+
running: runningBots,
|
|
159
|
+
stopped: totalBots - runningBots
|
|
160
|
+
},
|
|
161
|
+
servers: totalServers,
|
|
162
|
+
users: totalUsers
|
|
163
|
+
});
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('Error getting system stats:', error);
|
|
166
|
+
res.status(500).json({
|
|
167
|
+
error: 'Failed to get system stats',
|
|
168
|
+
message: error.message
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
module.exports = router;
|
|
174
|
+
|