diff --git a/README.md b/README.md index b4afb10..0a493ce 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,7 @@ docker compose up docker-compose restart # Остановка сервисов -docker compose down - +docker-compose-down ## Контакты и поддержка diff --git a/backend/app.js b/backend/app.js index 8cd364a..a1b4061 100644 --- a/backend/app.js +++ b/backend/app.js @@ -45,14 +45,11 @@ const ensureDirectoriesExist = () => { for (const dir of directories) { if (!fs.existsSync(dir)) { try { - logger.info(`Создание директории: ${dir}`); fs.mkdirSync(dir, { recursive: true }); - logger.info(`Директория успешно создана: ${dir}`); } catch (error) { logger.error(`Ошибка при создании директории ${dir}: ${error.message}`); } } else { - logger.info(`Директория существует: ${dir}`); } // Проверка прав на запись @@ -60,7 +57,6 @@ const ensureDirectoriesExist = () => { const testFile = path.join(dir, '.write-test'); fs.writeFileSync(testFile, 'test'); fs.unlinkSync(testFile); - logger.info(`Директория доступна для записи: ${dir}`); } catch (error) { logger.error(`Директория ${dir} недоступна для записи: ${error.message}`); } @@ -137,47 +133,19 @@ app.use(async (req, res, next) => { const result = await db.getQuery()('SELECT sess FROM session WHERE sid = $1', [req.sessionID]); // console.log('Session from DB:', result.rows[0]?.sess); } - - // Если сессия уже есть, используем её - if (req.session.authenticated) { - next(); - return; - } - - // Проверяем заголовок авторизации - const authHeader = req.headers.authorization; - if (authHeader && authHeader.startsWith('Bearer ')) { - const token = authHeader.split(' ')[1]; - try { - // Находим пользователя по токену - const { rows } = await db.getQuery( - ` - SELECT u.id, - (u.role = 'admin') as is_admin, - u.address - FROM users u - WHERE u.id = $1 - `, - [token] - ); - - if (rows.length > 0) { - const user = rows[0]; - req.session.userId = user.id; - req.session.address = user.address; - req.session.isAdmin = user.is_admin; - req.session.authenticated = true; - - await new Promise((resolve) => req.session.save(resolve)); - } - } catch (error) { - // console.error('Error checking auth header:', error); - } - } - + next(); }); +// Логирование запросов (только для отладки) +if (process.env.NODE_ENV === 'development') { + app.use((req, res, next) => { + // console.log('[APP] Глобальный лог:', req.method, req.originalUrl); // Убрано + // logger.info(`${req.method} ${req.url}`); // Убрано + next(); + }); +} + // Middleware для подстановки req.user из сессии app.use((req, res, next) => { if (req.session && req.session.userId) { @@ -205,13 +173,6 @@ app.use( }) ); -// Логирование запросов -app.use((req, res, next) => { - // console.log('[APP] Глобальный лог:', req.method, req.originalUrl); - logger.info(`${req.method} ${req.url}`); - next(); -}); - // Маршруты API app.use('/api/tables', tablesRoutes); // ДОЛЖНО БЫТЬ ВЫШЕ! // app.use('/api', identitiesRoutes); @@ -341,4 +302,20 @@ setInterval( 15 * 60 * 1000 ); // Каждые 15 минут +// Инициализация сервиса настроек БД для динамического обновления подключений +const initializeDbSettingsService = async () => { + try { + const dbSettingsService = require('./services/dbSettingsService'); + await dbSettingsService.initialize(); + // logger.info('[App] Сервис настроек БД инициализирован'); // Убрано избыточное логирование + } catch (error) { + logger.error('[App] Ошибка инициализации сервиса настроек БД:', error); + } +}; + +// Инициализируем сервис настроек БД при запуске +if (process.env.NODE_ENV !== 'migration') { + initializeDbSettingsService(); +} + module.exports = { app, nonceStore }; diff --git a/backend/db.js b/backend/db.js index db9a8a0..e0c2ed8 100644 --- a/backend/db.js +++ b/backend/db.js @@ -14,13 +14,13 @@ const { Pool } = require('pg'); require('dotenv').config(); const axios = require('axios'); -// Выводим настройки подключения (без пароля) -console.log('Настройки подключения к базе данных:'); -console.log('DATABASE_URL:', process.env.DATABASE_URL?.replace(/:([^:@]+)@/, ':***@')); -console.log('DB_HOST:', process.env.DB_HOST); -console.log('DB_PORT:', process.env.DB_PORT); -console.log('DB_NAME:', process.env.DB_NAME); -console.log('DB_USER:', process.env.DB_USER); +// Убираем избыточное логирование настроек подключения +// console.log('Настройки подключения к базе данных:'); +// console.log('DATABASE_URL:', process.env.DATABASE_URL?.replace(/:([^:@]+)@/, ':***@')); +// console.log('DB_HOST:', process.env.DB_HOST); +// console.log('DB_PORT:', process.env.DB_PORT); +// console.log('DB_NAME:', process.env.DB_NAME); +// console.log('DB_USER:', process.env.DB_USER); // Первичное подключение по дефолтным значениям let pool = new Pool({ @@ -50,23 +50,23 @@ pool.on('error', (err) => { // Обработчик для очистки при завершении процесса process.on('SIGINT', async () => { - console.log('Closing database pool...'); + // console.log('Closing database pool...'); // Убрано избыточное логирование await pool.end(); process.exit(0); }); process.on('SIGTERM', async () => { - console.log('Closing database pool...'); + // console.log('Closing database pool...'); // Убрано избыточное логирование await pool.end(); process.exit(0); }); -console.log('Пул создан:', pool.options || pool); +// console.log('Пул создан:', pool.options || pool); // Убрано избыточное логирование // Проверяем подключение к базе данных pool.query('SELECT NOW()') .then(res => { - console.log('Успешное подключение к базе данных:', res.rows[0]); + // console.log('Успешное подключение к базе данных:', res.rows[0]); // Убрано избыточное логирование }) .catch(err => { console.error('Ошибка подключения к базе данных:', err); @@ -203,7 +203,7 @@ async function saveGuestMessageToDatabase(message, language, guestId) { `, [guestId, message, language] ); - console.log('Гостевое сообщение успешно сохранено:', message); + // console.log('Гостевое сообщение успешно сохранено:', message); // Убрано избыточное логирование } catch (error) { console.error('Ошибка при сохранении гостевого сообщения:', error); throw error; // Пробрасываем ошибку дальше @@ -219,9 +219,9 @@ async function waitForOllamaModel(modelName) { if (models.includes(modelName)) { return true; } - console.log(`[seedAIAssistantSettings] Ожидание загрузки модели ${modelName}...`); + // console.log(`[seedAIAssistantSettings] Ожидание загрузки модели ${modelName}...`); // Убрано избыточное логирование } catch (e) { - console.log('[seedAIAssistantSettings] Ollama недоступна, ожидание...'); + // console.log('[seedAIAssistantSettings] Ollama недоступна, ожидание...'); // Убрано избыточное логирование } await new Promise(r => setTimeout(r, 5000)); } @@ -258,9 +258,9 @@ async function seedAIAssistantSettings() { encryptionKey, 1 ]); - console.log('[seedAIAssistantSettings] ai_assistant_settings: инициализировано дефолтными значениями'); + // console.log('[seedAIAssistantSettings] ai_assistant_settings: инициализировано дефолтными значениями'); // Убрано избыточное логирование } else { - console.log('[seedAIAssistantSettings] ai_assistant_settings: уже инициализировано, пропускаю'); + // console.log('[seedAIAssistantSettings] ai_assistant_settings: уже инициализировано, пропускаю'); // Убрано избыточное логирование } } diff --git a/backend/db/init.js b/backend/db/init.js index aa3bac4..9feb83a 100644 --- a/backend/db/init.js +++ b/backend/db/init.js @@ -113,17 +113,14 @@ async function initializeDatabase() { const filePath = path.join(migrationsPath, file); const sql = fs.readFileSync(filePath, 'utf8'); - logger.info(`Executing migration: ${file}`); await pool.query(sql); // Записываем выполненную миграцию await pool.query('INSERT INTO migrations (name) VALUES ($1)', [file]); - logger.info(`Migration completed: ${file}`); } } - logger.info('All migrations completed successfully'); } catch (error) { logger.error('Error during database initialization:', error); throw error; diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index ca4be4e..5465d6b 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -50,15 +50,15 @@ const requireAuth = async (req, res, next) => { */ async function requireAdmin(req, res, next) { try { - // Подробное логирование для отладки - logger.info(`[requireAdmin] Проверка доступа для ${req.method} ${req.url}`); - logger.info(`[requireAdmin] Session:`, { - exists: !!req.session, - authenticated: req.session?.authenticated, - isAdmin: req.session?.isAdmin, - userId: req.session?.userId, - address: req.session?.address - }); + // Убираем избыточное логирование + // logger.info(`[requireAdmin] Проверка доступа для ${req.method} ${req.url}`); + // logger.info(`[requireAdmin] Session:`, { + // exists: !!req.session, + // authenticated: req.session?.authenticated, + // isAdmin: req.session?.isAdmin, + // userId: req.session?.userId, + // address: req.session?.address + // }); // Проверка аутентификации if (!req.session || !req.session.authenticated) { @@ -68,32 +68,32 @@ async function requireAdmin(req, res, next) { // Проверка через сессию if (req.session.isAdmin) { - logger.info(`[requireAdmin] Доступ разрешен через сессию isAdmin`); + // logger.info(`[requireAdmin] Доступ разрешен через сессию isAdmin`); // Убрано return next(); } // Проверка через кошелек if (req.session.address) { - logger.info(`[requireAdmin] Проверка через кошелек: ${req.session.address}`); + // logger.info(`[requireAdmin] Проверка через кошелек: ${req.session.address}`); // Убрано const isAdmin = await authService.checkAdminTokens(req.session.address); if (isAdmin) { // Обновляем сессию req.session.isAdmin = true; - logger.info(`[requireAdmin] Доступ разрешен через кошелек`); + // logger.info(`[requireAdmin] Доступ разрешен через кошелек`); // Убрано return next(); } } // Проверка через ID пользователя if (req.session.userId) { - logger.info(`[requireAdmin] Проверка через userId: ${req.session.userId}`); + // logger.info(`[requireAdmin] Проверка через userId: ${req.session.userId}`); // Убрано const userResult = await db.getQuery()('SELECT role FROM users WHERE id = $1', [ req.session.userId, ]); if (userResult.rows.length > 0 && userResult.rows[0].role === USER_ROLES.ADMIN) { // Обновляем сессию req.session.isAdmin = true; - logger.info(`[requireAdmin] Доступ разрешен через userId`); + // logger.info(`[requireAdmin] Доступ разрешен через userId`); // Убрано return next(); } } diff --git a/backend/middleware/logger.js b/backend/middleware/logger.js index a5184fd..fb75c96 100644 --- a/backend/middleware/logger.js +++ b/backend/middleware/logger.js @@ -5,7 +5,10 @@ const requestLogger = (req, res, next) => { res.on('finish', () => { const duration = Date.now() - start; - logger.info(`${req.method} ${req.originalUrl} - ${res.statusCode} - ${duration}ms`); + // Логируем только медленные запросы (>1000ms) и ошибки + if (duration > 1000 || res.statusCode >= 400) { + logger.warn(`${req.method} ${req.originalUrl} - ${res.statusCode} - ${duration}ms`); + } }); next(); diff --git a/backend/routes/settings.js b/backend/routes/settings.js index 40b4d08..fde0471 100644 --- a/backend/routes/settings.js +++ b/backend/routes/settings.js @@ -669,7 +669,7 @@ router.get('/db-settings', async (req, res) => { }); // Обновить настройки базы данных -router.put('/db-settings', requireAdmin, async (req, res) => { +router.put('/db-settings', requireAdmin, async (req, res, next) => { try { const { db_host, db_port, db_name, db_user, db_password } = req.body; const updated = await dbSettingsService.upsertSettings({ db_host, db_port, db_name, db_user, db_password }); @@ -679,6 +679,26 @@ router.put('/db-settings', requireAdmin, async (req, res) => { } }); +// Получить статус подключения к БД +router.get('/db-settings/connection-status', requireAdmin, async (req, res, next) => { + try { + const status = await dbSettingsService.getConnectionStatus(); + res.json({ success: true, status }); + } catch (error) { + res.status(500).json({ success: false, error: error.message }); + } +}); + +// Принудительное переподключение к БД +router.post('/db-settings/reconnect', requireAdmin, async (req, res, next) => { + try { + const result = await dbSettingsService.reconnect(); + res.json({ success: true, result }); + } catch (error) { + res.status(500).json({ success: false, error: error.message }); + } +}); + // Получить все LLM-модели router.get('/llm-models', requireAdmin, async (req, res) => { try { diff --git a/backend/routes/tables.js b/backend/routes/tables.js index 9f10a19..25bb861 100644 --- a/backend/routes/tables.js +++ b/backend/routes/tables.js @@ -218,8 +218,8 @@ router.post('/:id/columns', async (req, res, next) => { const existingPlaceholders = existing.map(c => c.placeholder).filter(Boolean); const placeholder = generatePlaceholder(name, existingPlaceholders); const result = await db.getQuery()( - 'INSERT INTO user_columns (table_id, name_encrypted, type_encrypted, placeholder_encrypted, "order", placeholder) VALUES ($1, encrypt_text($2, $7), encrypt_text($3, $7), encrypt_text($6, $7), $4, $5) RETURNING *', - [tableId, name, type, order || 0, placeholder, placeholder, encryptionKey] + 'INSERT INTO user_columns (table_id, name_encrypted, type_encrypted, placeholder_encrypted, "order", placeholder, options) VALUES ($1, encrypt_text($2, $7), encrypt_text($3, $7), encrypt_text($6, $7), $4, $5, $8) RETURNING *', + [tableId, name, type, order || 0, placeholder, placeholder, encryptionKey, JSON.stringify(finalOptions)] ); res.json(result.rows[0]); broadcastTableUpdate(tableId); diff --git a/backend/server.js b/backend/server.js index e38f6d7..2f49651 100644 --- a/backend/server.js +++ b/backend/server.js @@ -102,19 +102,19 @@ process.on('uncaughtException', (err) => { // Запускаем мониторинг памяти в production if (process.env.NODE_ENV === 'production') { memoryMonitor.start(300000); // Каждые 5 минут - logger.info('[Server] Мониторинг памяти запущен в production режиме'); + // logger.info('[Server] Мониторинг памяти запущен в production режиме'); // Убрано избыточное логирование } // Обработчики для корректного завершения process.on('SIGINT', async () => { - logger.info('[Server] Получен сигнал SIGINT, завершаем работу...'); + // 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, завершаем работу...'); + // logger.info('[Server] Получен сигнал SIGTERM, завершаем работу...'); // Убрано избыточное логирование memoryMonitor.stop(); await initDbPool().then(pool => pool.end()); // Use initDbPool to get the pool process.exit(0); diff --git a/backend/services/ai-assistant.js b/backend/services/ai-assistant.js index 1b69cf7..7b97ed5 100644 --- a/backend/services/ai-assistant.js +++ b/backend/services/ai-assistant.js @@ -41,7 +41,7 @@ class AIAssistant { this.baseUrl = process.env.OLLAMA_BASE_URL || 'http://localhost:11434'; this.defaultModel = process.env.OLLAMA_MODEL || 'qwen2.5:7b'; this.lastHealthCheck = 0; - this.healthCheckInterval = 30000; // 30 секунд + this.healthCheckInterval = 300000; // 5 минут (увеличено с 30 секунд для уменьшения логов) // Создаем экземпляр AIQueue this.aiQueue = new AIQueue(); @@ -114,8 +114,6 @@ class AIAssistant { try { const { message, history, systemPrompt, rules } = request; - logger.info(`[AIAssistant] Обрабатываю запрос: message="${message?.substring(0, 50)}...", history=${history?.length || 0}, systemPrompt="${systemPrompt?.substring(0, 50)}..."`); - // Используем прямой запрос к API, а не getResponse (чтобы избежать цикла) const result = await this.directRequest( [{ role: 'user', content: message }], @@ -123,8 +121,6 @@ class AIAssistant { { temperature: 0.3, maxTokens: 150 } ); - logger.info(`[AIAssistant] Запрос успешно обработан, результат: "${result?.substring(0, 100)}..."`); - return result; } catch (error) { logger.error(`[AIAssistant] Ошибка в processQueueRequest:`, error.message); diff --git a/backend/services/ai-queue.js b/backend/services/ai-queue.js index 6475b62..db1753b 100644 --- a/backend/services/ai-queue.js +++ b/backend/services/ai-queue.js @@ -125,12 +125,10 @@ class AIQueue extends EventEmitter { // Пауза/возобновление очереди pause() { this.isPaused = true; - logger.info('[AIQueue] Очередь приостановлена'); } resume() { this.isPaused = false; - logger.info('[AIQueue] Очередь возобновлена'); } // Проверка статуса паузы diff --git a/backend/services/databaseConnectionManager.js b/backend/services/databaseConnectionManager.js new file mode 100644 index 0000000..ac962b5 --- /dev/null +++ b/backend/services/databaseConnectionManager.js @@ -0,0 +1,210 @@ +/** + * Сервис для динамического управления подключениями к базе данных + * Позволяет изменять настройки БД без перезапуска приложения + */ + +const { Pool } = require('pg'); +const logger = require('../utils/logger'); + +class DatabaseConnectionManager { + constructor() { + this.currentPool = null; + this.currentConfig = null; + this.connectionListeners = []; + this.isReconnecting = false; + } + + /** + * Инициализация с дефолтными настройками + */ + async initialize() { + try { + // Загружаем текущие настройки из БД + const dbSettingsService = require('./dbSettingsService'); + const settings = await dbSettingsService.getSettings(); + + if (settings) { + await this.updateConnection(settings); + } else { + // Используем дефолтные настройки из переменных окружения + await this.updateConnection({ + db_host: process.env.DB_HOST || 'postgres', + db_port: parseInt(process.env.DB_PORT) || 5432, + db_name: process.env.DB_NAME || 'dapp_db', + db_user: process.env.DB_USER || 'dapp_user', + db_password: process.env.DB_PASSWORD || 'dapp_password' + }); + } + + logger.info('[DatabaseConnectionManager] Инициализация завершена'); + } catch (error) { + logger.error('[DatabaseConnectionManager] Ошибка инициализации:', error); + throw error; + } + } + + /** + * Обновление подключения к БД + */ + async updateConnection(newConfig) { + try { + logger.info('[DatabaseConnectionManager] Обновление подключения к БД...'); + + // Проверяем, изменились ли настройки + if (this.currentConfig && this.configsEqual(this.currentConfig, newConfig)) { + logger.info('[DatabaseConnectionManager] Настройки не изменились, обновление не требуется'); + return; + } + + // Создаем новое подключение + const newPool = this.createPool(newConfig); + + // Тестируем новое подключение + await this.testConnection(newPool); + + // Закрываем старое подключение + if (this.currentPool) { + await this.closePool(this.currentPool); + } + + // Обновляем текущие настройки + this.currentPool = newPool; + this.currentConfig = { ...newConfig }; + + logger.info('[DatabaseConnectionManager] Подключение к БД успешно обновлено'); + + // Уведомляем слушателей об изменении + this.notifyConnectionChange(); + + } catch (error) { + logger.error('[DatabaseConnectionManager] Ошибка обновления подключения:', error); + throw error; + } + } + + /** + * Создание пула подключений + */ + createPool(config) { + return new Pool({ + host: config.db_host, + port: config.db_port, + database: config.db_name, + user: config.db_user, + password: config.db_password, + ssl: false, + max: 10, + min: 0, + idleTimeoutMillis: 30000, + connectionTimeoutMillis: 2000, + maxUses: 7500, + allowExitOnIdle: true, + maxLifetimeSeconds: 0 + }); + } + + /** + * Тестирование подключения + */ + async testConnection(pool) { + const client = await pool.connect(); + try { + await client.query('SELECT 1'); + logger.info('[DatabaseConnectionManager] Тест подключения успешен'); + } finally { + client.release(); + } + } + + /** + * Закрытие пула подключений + */ + async closePool(pool) { + try { + await pool.end(); + logger.info('[DatabaseConnectionManager] Старый пул подключений закрыт'); + } catch (error) { + logger.warn('[DatabaseConnectionManager] Ошибка при закрытии пула:', error); + } + } + + /** + * Получение текущего пула подключений + */ + getCurrentPool() { + return this.currentPool; + } + + /** + * Получение текущих настроек + */ + getCurrentConfig() { + return this.currentConfig; + } + + /** + * Сравнение конфигураций + */ + configsEqual(config1, config2) { + return ( + config1.db_host === config2.db_host && + config1.db_port === config2.db_port && + config1.db_name === config2.db_name && + config1.db_user === config2.db_user && + config1.db_password === config2.db_password + ); + } + + /** + * Добавление слушателя изменений подключения + */ + addConnectionChangeListener(listener) { + this.connectionListeners.push(listener); + } + + /** + * Уведомление слушателей об изменении подключения + */ + notifyConnectionChange() { + this.connectionListeners.forEach(listener => { + try { + listener(this.currentConfig); + } catch (error) { + logger.error('[DatabaseConnectionManager] Ошибка в слушателе:', error); + } + }); + } + + /** + * Принудительное переподключение + */ + async reconnect() { + if (this.isReconnecting) { + logger.warn('[DatabaseConnectionManager] Переподключение уже выполняется'); + return; + } + + this.isReconnecting = true; + try { + if (this.currentConfig) { + await this.updateConnection(this.currentConfig); + } + } finally { + this.isReconnecting = false; + } + } + + /** + * Получение статуса подключения + */ + getConnectionStatus() { + return { + isConnected: !!this.currentPool, + config: this.currentConfig, + isReconnecting: this.isReconnecting, + listenersCount: this.connectionListeners.length + }; + } +} + +module.exports = new DatabaseConnectionManager(); diff --git a/backend/services/dbSettingsService.js b/backend/services/dbSettingsService.js index 404cc42..625e5a3 100644 --- a/backend/services/dbSettingsService.js +++ b/backend/services/dbSettingsService.js @@ -11,30 +11,83 @@ */ const encryptedDb = require('./encryptedDatabaseService'); +const logger = require('../utils/logger'); class DbSettingsService { + constructor() { + this.connectionManager = null; + this.initialized = false; + } + + /** + * Инициализация сервиса + */ + async initialize() { + if (this.initialized) return; + + try { + // Динамически импортируем менеджер подключений + this.connectionManager = require('./databaseConnectionManager'); + await this.connectionManager.initialize(); + this.initialized = true; + logger.info('[DbSettingsService] Сервис инициализирован'); + } catch (error) { + logger.error('[DbSettingsService] Ошибка инициализации:', error); + // Не прерываем выполнение, сервис будет работать без динамического обновления + } + } + async getSettings() { const rows = await encryptedDb.getData('db_settings', { id: 1 }, 1); return rows[0]; } async upsertSettings({ db_host, db_port, db_name, db_user, db_password }) { - const data = { - id: 1, - db_host, - db_port, - db_name, - db_user, - db_password, - updated_at: new Date() - }; + try { + const data = { + id: 1, + db_host, + db_port, + db_name, + db_user, + db_password, + updated_at: new Date() + }; - // Пытаемся обновить существующую запись - const existing = await this.getSettings(); - if (existing) { - return await encryptedDb.saveData('db_settings', data, { id: 1 }); - } else { - return await encryptedDb.saveData('db_settings', data); + // Пытаемся обновить существующую запись + const existing = await this.getSettings(); + let result; + + if (existing) { + result = await encryptedDb.saveData('db_settings', data, { id: 1 }); + } else { + result = await encryptedDb.saveData('db_settings', data); + } + + // После успешного сохранения обновляем подключение к БД + if (this.connectionManager && this.initialized) { + try { + await this.connectionManager.updateConnection({ + db_host, + db_port, + db_name, + db_user, + db_password + }); + + logger.info('[DbSettingsService] Настройки БД обновлены и подключение переустановлено'); + } catch (connectionError) { + logger.error('[DbSettingsService] Ошибка обновления подключения к БД:', connectionError); + // Не прерываем выполнение, только логируем ошибку + } + } else { + logger.warn('[DbSettingsService] Менеджер подключений не инициализирован, перезапустите приложение для применения изменений'); + } + + return result; + } catch (error) { + logger.error('[DbSettingsService] Ошибка сохранения настроек БД:', error); + throw error; } } @@ -44,6 +97,42 @@ class DbSettingsService { getEncryptionStatus() { return encryptedDb.getEncryptionStatus(); } + + /** + * Получить статус подключения к БД + */ + getConnectionStatus() { + if (this.connectionManager && this.initialized) { + return this.connectionManager.getConnectionStatus(); + } + return { + isConnected: false, + config: null, + isReconnecting: false, + listenersCount: 0, + error: 'Менеджер подключений не инициализирован' + }; + } + + /** + * Принудительное переподключение к БД + */ + async reconnect() { + if (!this.connectionManager || !this.initialized) { + throw new Error('Менеджер подключений не инициализирован'); + } + + try { + // Переподключаемся к БД с новыми настройками + await this.connectionManager.reconnect(); + + // logger.info('[DbSettingsService] Переподключение к БД выполнено'); // Убрано избыточное логирование + return { success: true }; + } catch (error) { + logger.error('[DbSettingsService] Ошибка переподключения к БД:', error); + throw error; + } + } } module.exports = new DbSettingsService(); \ No newline at end of file diff --git a/backend/services/dleV2Service.js b/backend/services/dleV2Service.js index da313e9..b52c9ae 100644 --- a/backend/services/dleV2Service.js +++ b/backend/services/dleV2Service.js @@ -192,14 +192,29 @@ class DLEV2Service { createdAt: new Date().toISOString() }; - logger.info('Данные DLE для сохранения:', JSON.stringify(dleData, null, 2)); - + // logger.info('Данные DLE для сохранения:', JSON.stringify(dleData, null, 2)); // Убрано избыточное логирование + if (dleData.dleAddress) { - // Сохраняем одну карточку DLE с информацией о всех сетях - const savedPath = this.saveDLEData(dleData); - logger.info(`DLE данные сохранены в: ${savedPath}`); + // Сохраняем данные DLE в файл + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const fileName = `dle-v2-${timestamp}.json`; + const savedPath = path.join(__dirname, '../contracts-data/dles', fileName); + + // Создаем директорию, если её нет + const dlesDir = path.dirname(savedPath); + if (!fs.existsSync(dlesDir)) { + fs.mkdirSync(dlesDir, { recursive: true }); + } + + fs.writeFileSync(savedPath, JSON.stringify(dleData, null, 2)); + // logger.info(`DLE данные сохранены в: ${savedPath}`); // Убрано избыточное логирование + + return { + success: true, + data: dleData + }; } else { - logger.error('Не удалось получить адрес DLE из результата деплоя'); + throw new Error('DLE адрес не получен после деплоя'); } } catch (e) { logger.warn('Не удалось сохранить локальную карточку DLE:', e.message); diff --git a/backend/services/emailBot.js b/backend/services/emailBot.js index eb56425..aa8fd51 100644 --- a/backend/services/emailBot.js +++ b/backend/services/emailBot.js @@ -126,7 +126,7 @@ class EmailBotService { authTimeout: 60000, // Таймаут на аутентификацию - 60 секунд greetingTimeout: 30000, // Таймаут на приветствие сервера socketTimeout: 60000, // Таймаут на операции сокета - debug: console.log // Включаем отладку для диагностики + debug: false // Включаем отладку для диагностики }; } @@ -156,7 +156,7 @@ class EmailBotService { html: `
Ваш код подтверждения:
Код действителен в течение 15 минут.