blockmine 1.16.1 → 1.16.3
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/CHANGELOG.md +21 -0
- package/backend/cli.js +57 -52
- package/backend/package.json +27 -26
- package/backend/src/api/routes/bots.js +1801 -1719
- package/backend/src/api/routes/logs.js +245 -0
- package/backend/src/core/BotManager.js +22 -0
- package/backend/src/core/BotProcess.js +9 -4
- package/backend/src/server.js +2 -0
- package/frontend/dist/assets/{index-DcruEfMZ.js → index-CxCbyhFB.js} +75 -75
- package/frontend/dist/index.html +1 -1
- package/package.json +82 -82
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const debugOnly = (req, res, next) => {
|
|
8
|
+
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
|
9
|
+
next();
|
|
10
|
+
} else {
|
|
11
|
+
res.status(403).json({
|
|
12
|
+
success: false,
|
|
13
|
+
error: 'Доступ к логам разрешен только в режиме отладки'
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let logBuffer = [];
|
|
19
|
+
const MAX_LOG_BUFFER = 1000;
|
|
20
|
+
|
|
21
|
+
const originalConsoleLog = console.log;
|
|
22
|
+
const originalConsoleError = console.error;
|
|
23
|
+
const originalConsoleWarn = console.warn;
|
|
24
|
+
const originalConsoleInfo = console.info;
|
|
25
|
+
|
|
26
|
+
function addToLogBuffer(level, ...args) {
|
|
27
|
+
const timestamp = new Date().toISOString();
|
|
28
|
+
const message = args.map(arg =>
|
|
29
|
+
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
|
|
30
|
+
).join(' ');
|
|
31
|
+
|
|
32
|
+
const logEntry = {
|
|
33
|
+
timestamp,
|
|
34
|
+
level,
|
|
35
|
+
message,
|
|
36
|
+
pid: process.pid
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
logBuffer.push(logEntry);
|
|
40
|
+
|
|
41
|
+
if (logBuffer.length > MAX_LOG_BUFFER) {
|
|
42
|
+
logBuffer = logBuffer.slice(-MAX_LOG_BUFFER);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log = (...args) => {
|
|
47
|
+
addToLogBuffer('log', ...args);
|
|
48
|
+
originalConsoleLog.apply(console, args);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
console.error = (...args) => {
|
|
52
|
+
addToLogBuffer('error', ...args);
|
|
53
|
+
originalConsoleError.apply(console, args);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
console.warn = (...args) => {
|
|
57
|
+
addToLogBuffer('warn', ...args);
|
|
58
|
+
originalConsoleWarn.apply(console, args);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
console.info = (...args) => {
|
|
62
|
+
addToLogBuffer('info', ...args);
|
|
63
|
+
originalConsoleInfo.apply(console, args);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
router.get('/', debugOnly, (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const {
|
|
69
|
+
level,
|
|
70
|
+
limit = 100,
|
|
71
|
+
offset = 0,
|
|
72
|
+
search,
|
|
73
|
+
from,
|
|
74
|
+
to
|
|
75
|
+
} = req.query;
|
|
76
|
+
|
|
77
|
+
let filteredLogs = [...logBuffer];
|
|
78
|
+
|
|
79
|
+
if (level && level !== 'all') {
|
|
80
|
+
filteredLogs = filteredLogs.filter(log => log.level === level);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (search) {
|
|
84
|
+
const searchLower = search.toLowerCase();
|
|
85
|
+
filteredLogs = filteredLogs.filter(log =>
|
|
86
|
+
log.message.toLowerCase().includes(searchLower)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (from) {
|
|
91
|
+
const fromDate = new Date(from);
|
|
92
|
+
filteredLogs = filteredLogs.filter(log =>
|
|
93
|
+
new Date(log.timestamp) >= fromDate
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (to) {
|
|
98
|
+
const toDate = new Date(to);
|
|
99
|
+
filteredLogs = filteredLogs.filter(log =>
|
|
100
|
+
new Date(log.timestamp) <= toDate
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const total = filteredLogs.length;
|
|
105
|
+
const logs = filteredLogs
|
|
106
|
+
.slice(parseInt(offset), parseInt(offset) + parseInt(limit))
|
|
107
|
+
.reverse();
|
|
108
|
+
|
|
109
|
+
res.json({
|
|
110
|
+
success: true,
|
|
111
|
+
data: {
|
|
112
|
+
logs,
|
|
113
|
+
pagination: {
|
|
114
|
+
total,
|
|
115
|
+
limit: parseInt(limit),
|
|
116
|
+
offset: parseInt(offset),
|
|
117
|
+
hasMore: parseInt(offset) + parseInt(limit) < total
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error('Ошибка при получении логов:', error);
|
|
124
|
+
res.status(500).json({
|
|
125
|
+
success: false,
|
|
126
|
+
error: 'Ошибка при получении логов'
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
router.delete('/', debugOnly, (req, res) => {
|
|
132
|
+
try {
|
|
133
|
+
logBuffer = [];
|
|
134
|
+
res.json({
|
|
135
|
+
success: true,
|
|
136
|
+
message: 'Логи очищены'
|
|
137
|
+
});
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('Ошибка при очистке логов:', error);
|
|
140
|
+
res.status(500).json({
|
|
141
|
+
success: false,
|
|
142
|
+
error: 'Ошибка при очистке логов'
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
router.get('/stats', debugOnly, (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
const stats = {
|
|
150
|
+
total: logBuffer.length,
|
|
151
|
+
byLevel: {
|
|
152
|
+
log: logBuffer.filter(log => log.level === 'log').length,
|
|
153
|
+
error: logBuffer.filter(log => log.level === 'error').length,
|
|
154
|
+
warn: logBuffer.filter(log => log.level === 'warn').length,
|
|
155
|
+
info: logBuffer.filter(log => log.level === 'info').length
|
|
156
|
+
},
|
|
157
|
+
timeRange: {
|
|
158
|
+
oldest: logBuffer.length > 0 ? logBuffer[0].timestamp : null,
|
|
159
|
+
newest: logBuffer.length > 0 ? logBuffer[logBuffer.length - 1].timestamp : null
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
res.json({
|
|
164
|
+
success: true,
|
|
165
|
+
data: stats
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error('Ошибка при получении статистики логов:', error);
|
|
170
|
+
res.status(500).json({
|
|
171
|
+
success: false,
|
|
172
|
+
error: 'Ошибка при получении статистики логов'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
router.get('/stream', debugOnly, (req, res) => {
|
|
178
|
+
res.writeHead(200, {
|
|
179
|
+
'Content-Type': 'text/event-stream',
|
|
180
|
+
'Cache-Control': 'no-cache',
|
|
181
|
+
'Connection': 'keep-alive',
|
|
182
|
+
'Access-Control-Allow-Origin': '*',
|
|
183
|
+
'Access-Control-Allow-Headers': 'Cache-Control'
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const sendLog = (log) => {
|
|
187
|
+
res.write(`data: ${JSON.stringify(log)}\n\n`);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const recentLogs = logBuffer.slice(-10);
|
|
191
|
+
recentLogs.forEach(sendLog);
|
|
192
|
+
|
|
193
|
+
const logHandler = (log) => {
|
|
194
|
+
sendLog(log);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
if (!global.logStreamHandlers) {
|
|
198
|
+
global.logStreamHandlers = [];
|
|
199
|
+
}
|
|
200
|
+
global.logStreamHandlers.push(logHandler);
|
|
201
|
+
|
|
202
|
+
req.on('close', () => {
|
|
203
|
+
const index = global.logStreamHandlers.indexOf(logHandler);
|
|
204
|
+
if (index > -1) {
|
|
205
|
+
global.logStreamHandlers.splice(index, 1);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
function broadcastLog(log) {
|
|
211
|
+
if (global.logStreamHandlers) {
|
|
212
|
+
global.logStreamHandlers.forEach(handler => {
|
|
213
|
+
try {
|
|
214
|
+
handler(log);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('Ошибка при отправке лога клиенту:', error);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const originalAddToLogBuffer = addToLogBuffer;
|
|
223
|
+
addToLogBuffer = function(level, ...args) {
|
|
224
|
+
const timestamp = new Date().toISOString();
|
|
225
|
+
const message = args.map(arg =>
|
|
226
|
+
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
|
|
227
|
+
).join(' ');
|
|
228
|
+
|
|
229
|
+
const logEntry = {
|
|
230
|
+
timestamp,
|
|
231
|
+
level,
|
|
232
|
+
message,
|
|
233
|
+
pid: process.pid
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
logBuffer.push(logEntry);
|
|
237
|
+
|
|
238
|
+
if (logBuffer.length > MAX_LOG_BUFFER) {
|
|
239
|
+
logBuffer = logBuffer.slice(-MAX_LOG_BUFFER);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
broadcastLog(logEntry);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
module.exports = router;
|
|
@@ -335,6 +335,10 @@ class BotManager {
|
|
|
335
335
|
getIO().emit('bot:log', { botId, log: logEntry });
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
getBotLogs(botId) {
|
|
339
|
+
return this.logCache.get(botId) || [];
|
|
340
|
+
}
|
|
341
|
+
|
|
338
342
|
async startBot(botConfig) {
|
|
339
343
|
if (this.bots.has(botConfig.id) && !this.bots.get(botConfig.id).killed) {
|
|
340
344
|
this.appendLog(botConfig.id, `[SYSTEM-ERROR] Попытка повторного запуска. Запуск отменен.`);
|
|
@@ -765,6 +769,23 @@ class BotManager {
|
|
|
765
769
|
return { success: true };
|
|
766
770
|
}
|
|
767
771
|
|
|
772
|
+
invalidateAllUserCache(botId) {
|
|
773
|
+
for (const [cacheKey, user] of UserService.cache.entries()) {
|
|
774
|
+
if (cacheKey.startsWith(`${botId}:`)) {
|
|
775
|
+
UserService.cache.delete(cacheKey);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
console.log(`[BotManager] Кэш пользователей очищен для бота ${botId}`);
|
|
779
|
+
|
|
780
|
+
const child = this.bots.get(botId);
|
|
781
|
+
if (child && !child.killed) {
|
|
782
|
+
child.send({ type: 'invalidate_all_user_cache' });
|
|
783
|
+
console.log(`[BotManager] Отправлено сообщение об очистке кэша в процесс бота ${botId}`);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return { success: true };
|
|
787
|
+
}
|
|
788
|
+
|
|
768
789
|
async getPlayerList(botId) {
|
|
769
790
|
const PLAYER_LIST_CACHE_TTL = 2000;
|
|
770
791
|
|
|
@@ -837,6 +858,7 @@ class BotManager {
|
|
|
837
858
|
if (child && !child.killed) {
|
|
838
859
|
child.send({ type: 'plugins:reload' });
|
|
839
860
|
console.log(`[BotManager] Sent plugins:reload to bot process ${botId}`);
|
|
861
|
+
const { getIO } = require('../real-time/socketHandler');
|
|
840
862
|
getIO().emit('bot:plugins_reloaded', { botId });
|
|
841
863
|
return { success: true, message: 'Команда на перезагрузку плагинов отправлена.' };
|
|
842
864
|
}
|
|
@@ -699,6 +699,15 @@ process.on('message', async (message) => {
|
|
|
699
699
|
if (message.username && bot && bot.config) {
|
|
700
700
|
UserService.clearCache(message.username, bot.config.id);
|
|
701
701
|
}
|
|
702
|
+
} else if (message.type === 'invalidate_all_user_cache') {
|
|
703
|
+
if (bot && bot.config) {
|
|
704
|
+
for (const [cacheKey, user] of UserService.cache.entries()) {
|
|
705
|
+
if (cacheKey.startsWith(`${bot.config.id}:`)) {
|
|
706
|
+
UserService.cache.delete(cacheKey);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
sendLog(`[BotProcess] Кэш пользователей очищен для бота ${bot.config.id}`);
|
|
710
|
+
}
|
|
702
711
|
} else if (message.type === 'handle_permission_error') {
|
|
703
712
|
const { commandName, username, typeChat } = message;
|
|
704
713
|
const commandInstance = bot.commands.get(commandName);
|
|
@@ -787,10 +796,6 @@ process.on('SIGINT', () => {
|
|
|
787
796
|
setTimeout(() => process.exit(0), 100);
|
|
788
797
|
});
|
|
789
798
|
|
|
790
|
-
// process.on('SIGKILL', () => {
|
|
791
|
-
// sendLog('[System] Получен сигнал SIGKILL. Принудительное завершение...');
|
|
792
|
-
// process.exit(0);
|
|
793
|
-
// });
|
|
794
799
|
|
|
795
800
|
function serializeEntity(entity) {
|
|
796
801
|
if (!entity) return null;
|
package/backend/src/server.js
CHANGED
|
@@ -20,6 +20,7 @@ const eventGraphsRouter = require('./api/routes/eventGraphs');
|
|
|
20
20
|
const TaskScheduler = require('./core/TaskScheduler');
|
|
21
21
|
const panelRoutes = require('./api/routes/panel');
|
|
22
22
|
const changelogRoutes = require('./api/routes/changelog');
|
|
23
|
+
const logsRoutes = require('./api/routes/logs');
|
|
23
24
|
|
|
24
25
|
const app = express();
|
|
25
26
|
const server = http.createServer(app);
|
|
@@ -61,6 +62,7 @@ app.use('/api/permissions', permissionsRoutes);
|
|
|
61
62
|
app.use('/api/search', searchRoutes);
|
|
62
63
|
app.use('/api/panel', panelRoutes);
|
|
63
64
|
app.use('/api/changelog', changelogRoutes);
|
|
65
|
+
app.use('/api/logs', logsRoutes);
|
|
64
66
|
|
|
65
67
|
app.use(express.static(frontendPath));
|
|
66
68
|
|