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.
@@ -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;
@@ -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