ваше сообщение коммита
This commit is contained in:
@@ -95,6 +95,7 @@ const dleTokensRoutes = require('./routes/dleTokens'); // Функции ток
|
||||
const dleAnalyticsRoutes = require('./routes/dleAnalytics'); // Аналитика и история
|
||||
const dleMultichainRoutes = require('./routes/dleMultichain'); // Мультичейн функции
|
||||
const dleHistoryRoutes = require('./routes/dleHistory'); // Расширенная история
|
||||
const systemRoutes = require('./routes/system'); // Добавляем импорт маршрутов системного мониторинга
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -233,6 +234,7 @@ app.use('/api/identities', identitiesRoutes);
|
||||
app.use('/api/rag', ragRoutes); // Подключаем роут
|
||||
app.use('/api/monitoring', monitoringRoutes);
|
||||
app.use('/api/pages', pagesRoutes); // Подключаем роутер страниц
|
||||
app.use('/api/system', systemRoutes); // Добавляем маршрут системного мониторинга
|
||||
|
||||
const nonceStore = new Map(); // или любая другая реализация хранилища nonce
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"_format": "hh-sol-dbg-1",
|
||||
"buildInfo": "../../build-info/ab387c71734b3d3e5e7817d328027586.json"
|
||||
"buildInfo": "../../build-info/aa0034b410e4fbe1d1ff90369d480540.json"
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
4
backend/cache/solidity-files-cache.json
vendored
4
backend/cache/solidity-files-cache.json
vendored
@@ -2,8 +2,8 @@
|
||||
"_format": "hh-sol-cache-2",
|
||||
"files": {
|
||||
"/home/alex/Digital_Legal_Entity(DLE)/backend/contracts/DLE.sol": {
|
||||
"lastModificationDate": 1755280436490,
|
||||
"contentHash": "f676e9964a39b0fccdc62a9114266863",
|
||||
"lastModificationDate": 1755366617069,
|
||||
"contentHash": "47d6b51ed0025b36c50649b175745512",
|
||||
"sourceName": "contracts/DLE.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.20",
|
||||
|
||||
@@ -20,6 +20,12 @@ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
* @title DLE (Digital Legal Entity)
|
||||
* @dev Основной контракт DLE с модульной архитектурой, Single-Chain Governance
|
||||
* и безопасной мульти-чейн синхронизацией без сторонних мостов (через подписи холдеров).
|
||||
*
|
||||
* КЛЮЧЕВЫЕ ОСОБЕННОСТИ:
|
||||
* - Прямые переводы токенов ЗАБЛОКИРОВАНЫ (transfer, transferFrom, approve)
|
||||
* - Перевод токенов возможен ТОЛЬКО через governance предложения
|
||||
* - Токены служат только для голосования и управления DLE
|
||||
* - Все операции с токенами требуют коллективного решения
|
||||
*/
|
||||
contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
|
||||
using ECDSA for bytes32;
|
||||
@@ -112,6 +118,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
|
||||
event DLEInfoUpdated(string name, string symbol, string location, string coordinates, uint256 jurisdiction, string[] okvedCodes, uint256 kpp);
|
||||
event QuorumPercentageUpdated(uint256 oldQuorumPercentage, uint256 newQuorumPercentage);
|
||||
event CurrentChainIdUpdated(uint256 oldChainId, uint256 newChainId);
|
||||
event TokensTransferredByGovernance(address indexed recipient, uint256 amount);
|
||||
|
||||
// EIP712 typehash для подписи одобрения исполнения предложения в целевой сети
|
||||
// ExecutionApproval(uint256 proposalId, bytes32 operationHash, uint256 chainId, uint256 snapshotTimepoint)
|
||||
@@ -531,6 +538,10 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
|
||||
} else if (selector == bytes4(keccak256("_removeSupportedChain(uint256)"))) {
|
||||
(uint256 chainIdToRemove) = abi.decode(data, (uint256));
|
||||
_removeSupportedChain(chainIdToRemove);
|
||||
} else if (selector == bytes4(keccak256("_transferTokens(address,uint256)"))) {
|
||||
// Операция перевода токенов через governance
|
||||
(address recipient, uint256 amount) = abi.decode(data, (address, uint256));
|
||||
_transferTokens(recipient, amount);
|
||||
} else if (selector == bytes4(keccak256("offchainAction(bytes32,string,bytes32)"))) {
|
||||
// Оффчейн операция для приложения: идентификатор, тип, хеш полезной нагрузки
|
||||
// (bytes32 actionId, string memory kind, bytes32 payloadHash) = abi.decode(data, (bytes32, string, bytes32));
|
||||
@@ -604,6 +615,22 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
|
||||
emit CurrentChainIdUpdated(oldChainId, _newChainId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Перевести токены через governance (от имени DLE)
|
||||
* @param _recipient Адрес получателя
|
||||
* @param _amount Количество токенов для перевода
|
||||
*/
|
||||
function _transferTokens(address _recipient, uint256 _amount) internal {
|
||||
require(_recipient != address(0), "Cannot transfer to zero address");
|
||||
require(_amount > 0, "Amount must be positive");
|
||||
require(balanceOf(address(this)) >= _amount, "Insufficient DLE balance");
|
||||
|
||||
// Переводим токены от имени DLE (address(this))
|
||||
_transfer(address(this), _recipient, _amount);
|
||||
|
||||
emit TokensTransferredByGovernance(_recipient, _amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Создать предложение о добавлении модуля
|
||||
* @param _description Описание предложения
|
||||
@@ -896,4 +923,38 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
|
||||
require(delegator == delegatee, "Delegation disabled");
|
||||
super._delegate(delegator, delegatee);
|
||||
}
|
||||
|
||||
// ===== Блокировка прямых переводов токенов =====
|
||||
// Токены DLE могут быть переведены только через governance
|
||||
|
||||
/**
|
||||
* @dev Блокирует прямые переводы токенов
|
||||
* @param to Адрес получателя (не используется)
|
||||
* @param amount Количество токенов (не используется)
|
||||
* @return Всегда возвращает false
|
||||
*/
|
||||
function transfer(address to, uint256 amount) public override returns (bool) {
|
||||
revert("Direct transfers disabled. Use governance proposals for token transfers.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Блокирует прямые переводы токенов через approve/transferFrom
|
||||
* @param from Адрес отправителя (не используется)
|
||||
* @param to Адрес получателя (не используется)
|
||||
* @param amount Количество токенов (не используется)
|
||||
* @return Всегда возвращает false
|
||||
*/
|
||||
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
|
||||
revert("Direct transfers disabled. Use governance proposals for token transfers.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Блокирует прямые разрешения на перевод токенов
|
||||
* @param spender Адрес, которому разрешается тратить токены (не используется)
|
||||
* @param amount Количество токенов (не используется)
|
||||
* @return Всегда возвращает false
|
||||
*/
|
||||
function approve(address spender, uint256 amount) public override returns (bool) {
|
||||
revert("Direct approvals disabled. Use governance proposals for token transfers.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,39 @@ let pool = new Pool({
|
||||
user: process.env.DB_USER || 'dapp_user',
|
||||
password: process.env.DB_PASSWORD,
|
||||
ssl: false,
|
||||
// Настройки для предотвращения утечек памяти
|
||||
max: 10, // Максимальное количество клиентов в пуле
|
||||
min: 0, // Минимальное количество клиентов в пуле
|
||||
idleTimeoutMillis: 30000, // Время жизни неактивного клиента (30 сек)
|
||||
connectionTimeoutMillis: 2000, // Таймаут подключения (2 сек)
|
||||
maxUses: 7500, // Максимальное количество использований клиента
|
||||
allowExitOnIdle: true, // Разрешить выход при отсутствии активных клиентов
|
||||
});
|
||||
|
||||
// Увеличиваем лимит обработчиков событий для предотвращения предупреждений
|
||||
pool.setMaxListeners(20);
|
||||
|
||||
// Добавляем обработчики для правильного закрытия пула
|
||||
pool.on('error', (err) => {
|
||||
console.error('Unexpected error on idle client', err);
|
||||
process.exit(-1);
|
||||
});
|
||||
|
||||
// Обработчик для очистки при завершении процесса
|
||||
process.on('SIGINT', async () => {
|
||||
console.log('Closing database pool...');
|
||||
await pool.end();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', async () => {
|
||||
console.log('Closing database pool...');
|
||||
await pool.end();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
console.log('Пул создан:', pool.options || pool);
|
||||
|
||||
// Проверяем подключение к базе данных
|
||||
pool.query('SELECT NOW()')
|
||||
.then(res => {
|
||||
@@ -41,8 +72,6 @@ pool.query('SELECT NOW()')
|
||||
console.error('Ошибка подключения к базе данных:', err);
|
||||
});
|
||||
|
||||
console.log('Пул создан:', pool.options || pool);
|
||||
|
||||
function getPool() {
|
||||
return pool;
|
||||
}
|
||||
@@ -69,10 +98,11 @@ async function reinitPoolFromDbSettings() {
|
||||
if (!res.rows.length) throw new Error('DB settings not found');
|
||||
const dbSettings = res.rows[0];
|
||||
|
||||
// Закрываем старый пул
|
||||
// Закрываем старый пул правильно
|
||||
console.log('Закрываем старый пул подключений...');
|
||||
await pool.end();
|
||||
|
||||
// Создаём новый пул с расшифрованными настройками
|
||||
// Создаём новый пул с расшифрованными настройками и теми же параметрами для предотвращения утечек
|
||||
pool = new Pool({
|
||||
host: dbSettings.db_host_encrypted ? await decryptValue(dbSettings.db_host_encrypted) : process.env.DB_HOST || 'postgres',
|
||||
port: parseInt(dbSettings.db_port || process.env.DB_PORT || '5432'),
|
||||
@@ -80,6 +110,22 @@ async function reinitPoolFromDbSettings() {
|
||||
user: dbSettings.db_user_encrypted ? await decryptValue(dbSettings.db_user_encrypted) : process.env.DB_USER || 'dapp_user',
|
||||
password: dbSettings.db_password_encrypted ? await decryptValue(dbSettings.db_password_encrypted) : process.env.DB_PASSWORD,
|
||||
ssl: false,
|
||||
// Те же настройки для предотвращения утечек
|
||||
max: 10,
|
||||
min: 0,
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: 2000,
|
||||
maxUses: 7500,
|
||||
allowExitOnIdle: true,
|
||||
});
|
||||
|
||||
// Устанавливаем лимит обработчиков для нового пула
|
||||
pool.setMaxListeners(20);
|
||||
|
||||
// Добавляем обработчики ошибок для нового пула
|
||||
pool.on('error', (err) => {
|
||||
console.error('Unexpected error on idle client', err);
|
||||
process.exit(-1);
|
||||
});
|
||||
|
||||
// Пересоздаём session middleware
|
||||
|
||||
99
backend/routes/system.js
Normal file
99
backend/routes/system.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is proprietary and confidential.
|
||||
* Unauthorized copying, modification, or distribution is prohibited.
|
||||
*
|
||||
* For licensing inquiries: info@hb3-accelerator.com
|
||||
* Website: https://hb3-accelerator.com
|
||||
* GitHub: https://github.com/HB3-ACCELERATOR
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const memoryMonitor = require('../utils/memoryMonitor');
|
||||
const logger = require('../utils/logger');
|
||||
const { checkAdminRole } = require('../services/admin-role');
|
||||
|
||||
// Middleware для проверки прав администратора
|
||||
const requireAdmin = async (req, res, next) => {
|
||||
try {
|
||||
if (!req.session || !req.session.userId) {
|
||||
return res.status(401).json({ success: false, error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const isAdmin = await checkAdminRole(req.session.userId);
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ success: false, error: 'Admin access required' });
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
logger.error('Error checking admin role:', error);
|
||||
res.status(500).json({ success: false, error: 'Internal server error' });
|
||||
}
|
||||
};
|
||||
|
||||
// GET /api/system/memory - Получить информацию о памяти
|
||||
router.get('/memory', requireAdmin, (req, res) => {
|
||||
try {
|
||||
const memoryUsage = memoryMonitor.getMemoryUsage();
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
memory: memoryUsage,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error getting memory usage:', error);
|
||||
res.status(500).json({ success: false, error: 'Failed to get memory usage' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/system/memory/start - Запустить мониторинг памяти
|
||||
router.post('/memory/start', requireAdmin, (req, res) => {
|
||||
try {
|
||||
const { interval } = req.body;
|
||||
memoryMonitor.start(interval || 60000);
|
||||
res.json({ success: true, message: 'Memory monitoring started' });
|
||||
} catch (error) {
|
||||
logger.error('Error starting memory monitoring:', error);
|
||||
res.status(500).json({ success: false, error: 'Failed to start memory monitoring' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/system/memory/stop - Остановить мониторинг памяти
|
||||
router.post('/memory/stop', requireAdmin, (req, res) => {
|
||||
try {
|
||||
memoryMonitor.stop();
|
||||
res.json({ success: true, message: 'Memory monitoring stopped' });
|
||||
} catch (error) {
|
||||
logger.error('Error stopping memory monitoring:', error);
|
||||
res.status(500).json({ success: false, error: 'Failed to stop memory monitoring' });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/system/health - Проверка здоровья системы
|
||||
router.get('/health', (req, res) => {
|
||||
try {
|
||||
const memoryUsage = memoryMonitor.getMemoryUsage();
|
||||
const uptime = process.uptime();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
status: 'healthy',
|
||||
uptime: Math.round(uptime),
|
||||
memory: memoryUsage,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error getting system health:', error);
|
||||
res.status(500).json({ success: false, error: 'Failed to get system health' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -19,6 +19,7 @@ const { getBot } = require('./services/telegramBot');
|
||||
const EmailBotService = require('./services/emailBot');
|
||||
const { initDbPool, seedAIAssistantSettings } = require('./db');
|
||||
const { warmupModel } = require('./scripts/warmup-model'); // Добавляем импорт разогрева модели
|
||||
const memoryMonitor = require('./utils/memoryMonitor');
|
||||
|
||||
const PORT = process.env.PORT || 8000;
|
||||
|
||||
@@ -98,4 +99,25 @@ process.on('uncaughtException', (err) => {
|
||||
logger.error('Uncaught Exception:', err);
|
||||
});
|
||||
|
||||
// Запускаем мониторинг памяти в production
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
memoryMonitor.start(300000); // Каждые 5 минут
|
||||
logger.info('[Server] Мониторинг памяти запущен в production режиме');
|
||||
}
|
||||
|
||||
// Обработчики для корректного завершения
|
||||
process.on('SIGINT', async () => {
|
||||
logger.info('[Server] Получен сигнал SIGINT, завершаем работу...');
|
||||
memoryMonitor.stop();
|
||||
await initDbPool().then(pool => pool.end()); // Use initDbPool to get the pool
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', async () => {
|
||||
logger.info('[Server] Получен сигнал SIGTERM, завершаем работу...');
|
||||
memoryMonitor.stop();
|
||||
await initDbPool().then(pool => pool.end()); // Use initDbPool to get the pool
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
|
||||
@@ -29,6 +29,32 @@ const { isUserBlocked } = require('../utils/userUtils');
|
||||
class EmailBotService {
|
||||
constructor() {
|
||||
// console.log('[EmailBot] constructor called');
|
||||
this.imap = null;
|
||||
this.isChecking = false;
|
||||
this.reconnectAttempts = 0;
|
||||
this.maxReconnectAttempts = 3;
|
||||
}
|
||||
|
||||
// Метод для очистки IMAP соединения
|
||||
cleanupImapConnection() {
|
||||
if (this.imap) {
|
||||
try {
|
||||
// Удаляем все обработчики событий
|
||||
this.imap.removeAllListeners('error');
|
||||
this.imap.removeAllListeners('ready');
|
||||
this.imap.removeAllListeners('end');
|
||||
this.imap.removeAllListeners('close');
|
||||
|
||||
// Закрываем соединение
|
||||
if (this.imap.state !== 'disconnected') {
|
||||
this.imap.end();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[EmailBot] Error cleaning up IMAP connection:', error);
|
||||
} finally {
|
||||
this.imap = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getSettingsFromDb() {
|
||||
@@ -533,56 +559,50 @@ class EmailBotService {
|
||||
try {
|
||||
// console.log('[EmailBot] start() called');
|
||||
logger.info('[EmailBot] start() called');
|
||||
const imapConfig = await this.getImapConfig();
|
||||
// Логируем IMAP-конфиг (без пароля)
|
||||
const safeConfig = { ...imapConfig };
|
||||
if (safeConfig.password) safeConfig.password = '***';
|
||||
logger.info('[EmailBot] IMAP config:', safeConfig);
|
||||
|
||||
// Очищаем предыдущее соединение если есть
|
||||
this.cleanupImapConnection();
|
||||
|
||||
let attempt = 0;
|
||||
const maxAttempts = 3;
|
||||
this.isChecking = false;
|
||||
const tryConnect = () => {
|
||||
|
||||
const tryConnect = async () => {
|
||||
attempt++;
|
||||
logger.info(`[EmailBot] IMAP connect attempt ${attempt}`);
|
||||
this.imap = new Imap(imapConfig);
|
||||
this.imap = new Imap(await this.getImapConfig());
|
||||
|
||||
// Устанавливаем обработчики событий
|
||||
this.imap.once('ready', () => {
|
||||
logger.info('[EmailBot] IMAP connection ready');
|
||||
this.imap.openBox('INBOX', false, (err, box) => {
|
||||
if (err) {
|
||||
logger.error(`[EmailBot] Error opening INBOX: ${err.message}`);
|
||||
this.imap.end();
|
||||
return;
|
||||
}
|
||||
logger.info('[EmailBot] INBOX opened successfully');
|
||||
});
|
||||
// После успешного подключения — обычная логика
|
||||
this.reconnectAttempts = 0; // Сбрасываем счетчик при успешном подключении
|
||||
this.checkEmails();
|
||||
logger.info('[EmailBot] Email bot started and IMAP connection initiated');
|
||||
// Периодическая проверка почты
|
||||
setInterval(async () => {
|
||||
if (this.isChecking) return;
|
||||
this.isChecking = true;
|
||||
try {
|
||||
await this.checkEmails();
|
||||
} catch (e) {
|
||||
logger.error('[EmailBot] Error in periodic checkEmails:', e);
|
||||
}
|
||||
this.isChecking = false;
|
||||
}, 60000); // 60 секунд
|
||||
});
|
||||
|
||||
this.imap.once('end', () => {
|
||||
logger.info('[EmailBot] IMAP connection ended');
|
||||
this.cleanupImapConnection();
|
||||
});
|
||||
|
||||
this.imap.once('close', () => {
|
||||
logger.info('[EmailBot] IMAP connection closed');
|
||||
this.cleanupImapConnection();
|
||||
});
|
||||
|
||||
this.imap.once('error', (err) => {
|
||||
logger.error(`[EmailBot] IMAP connection error: ${err.message}`);
|
||||
this.cleanupImapConnection();
|
||||
|
||||
if (err.message && err.message.toLowerCase().includes('timed out') && attempt < maxAttempts) {
|
||||
logger.warn(`[EmailBot] IMAP reconnecting in 10 seconds (attempt ${attempt + 1})...`);
|
||||
setTimeout(tryConnect, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
this.imap.connect();
|
||||
};
|
||||
tryConnect();
|
||||
} catch (err) {
|
||||
// console.error('[EmailBot] Ошибка при старте:', err);
|
||||
logger.error('[EmailBot] Ошибка при старте:', err);
|
||||
this.cleanupImapConnection();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
99
backend/utils/memoryMonitor.js
Normal file
99
backend/utils/memoryMonitor.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is proprietary and confidential.
|
||||
* Unauthorized copying, modification, or distribution is prohibited.
|
||||
*
|
||||
* For licensing inquiries: info@hb3-accelerator.com
|
||||
* Website: https://hb3-accelerator.com
|
||||
* GitHub: https://github.com/HB3-ACCELERATOR
|
||||
*/
|
||||
|
||||
const logger = require('./logger');
|
||||
|
||||
class MemoryMonitor {
|
||||
constructor() {
|
||||
this.monitoring = false;
|
||||
this.interval = null;
|
||||
this.lastMemoryUsage = null;
|
||||
}
|
||||
|
||||
start(intervalMs = 60000) { // По умолчанию каждую минуту
|
||||
if (this.monitoring) {
|
||||
logger.warn('[MemoryMonitor] Мониторинг уже запущен');
|
||||
return;
|
||||
}
|
||||
|
||||
this.monitoring = true;
|
||||
this.interval = setInterval(() => {
|
||||
this.checkMemoryUsage();
|
||||
}, intervalMs);
|
||||
|
||||
logger.info('[MemoryMonitor] Мониторинг памяти запущен');
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
}
|
||||
this.monitoring = false;
|
||||
logger.info('[MemoryMonitor] Мониторинг памяти остановлен');
|
||||
}
|
||||
|
||||
checkMemoryUsage() {
|
||||
const memUsage = process.memoryUsage();
|
||||
const memUsageMB = {
|
||||
rss: Math.round(memUsage.rss / 1024 / 1024),
|
||||
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
|
||||
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
|
||||
external: Math.round(memUsage.external / 1024 / 1024),
|
||||
arrayBuffers: Math.round(memUsage.arrayBuffers / 1024 / 1024)
|
||||
};
|
||||
|
||||
// Проверяем рост памяти
|
||||
if (this.lastMemoryUsage) {
|
||||
const growth = {
|
||||
rss: memUsageMB.rss - this.lastMemoryUsage.rss,
|
||||
heapUsed: memUsageMB.heapUsed - this.lastMemoryUsage.heapUsed
|
||||
};
|
||||
|
||||
// Логируем если есть значительный рост
|
||||
if (growth.rss > 50 || growth.heapUsed > 20) {
|
||||
logger.warn('[MemoryMonitor] Обнаружен рост памяти:', {
|
||||
current: memUsageMB,
|
||||
growth: growth
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.lastMemoryUsage = memUsageMB;
|
||||
|
||||
// Логируем текущее использование памяти
|
||||
logger.info('[MemoryMonitor] Использование памяти:', memUsageMB);
|
||||
}
|
||||
|
||||
getMemoryUsage() {
|
||||
const memUsage = process.memoryUsage();
|
||||
return {
|
||||
rss: Math.round(memUsage.rss / 1024 / 1024),
|
||||
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
|
||||
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
|
||||
external: Math.round(memUsage.external / 1024 / 1024),
|
||||
arrayBuffers: Math.round(memUsage.arrayBuffers / 1024 / 1024)
|
||||
};
|
||||
}
|
||||
|
||||
// Проверка утечек в EventEmitter
|
||||
checkEventEmitterLeaks() {
|
||||
const eventEmitter = require('events');
|
||||
const defaultMaxListeners = eventEmitter.defaultMaxListeners;
|
||||
|
||||
logger.info('[MemoryMonitor] EventEmitter defaultMaxListeners:', defaultMaxListeners);
|
||||
|
||||
// Можно добавить дополнительную логику для проверки конкретных EventEmitter'ов
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new MemoryMonitor();
|
||||
Reference in New Issue
Block a user