first commit

This commit is contained in:
2026-08-08 00:15:48 +03:00
parent ac2bc13da0
commit d98b4c1343
146 changed files with 122827 additions and 0 deletions
+248
View File
@@ -0,0 +1,248 @@
// ============================================
// 📦 Модуль подключения к MySQL базе данных
// ============================================
const mysql = require('mysql2/promise'); // ✅ Используем promise-версию
const path = require('path');
const fs = require('fs-extra');
const { logger } = require('./logger');
// Загружаем переменные окружения
require('dotenv').config({ path: path.join(__dirname, '../../config.env') });
// ============================================
// 🔧 Конфигурация подключения к MySQL
// ============================================
const dbConfig = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'test',
port: process.env.DB_PORT || 3306,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
// Дополнительные настройки
charset: 'utf8mb4',
timezone: '+00:00'
};
// ============================================
// 📊 Глобальные переменные
// ============================================
let pool = null; // Пул соединений
let isDBConnected = false;
let lastError = null;
let connectionAttempts = 0;
let isReconnecting = false;
// ✅ Функция для проверки testMode из config.json
async function checkTestMode() {
try {
const configPath = path.join(__dirname, '../../config.json');
const config = await fs.readJson(configPath);
return config.testMode === true;
} catch (err) {
logger.warn('⚠️ Не удалось прочитать config.json для проверки testMode', { error: err.message });
return false;
}
}
// ============================================
// 🔄 Функция подключения с повторными попытками
// ============================================
async function connectDB(retries = 5, delay = 3000) {
// ✅ Проверяем testMode
const testMode = await checkTestMode();
// 🔧 Если включен тестовый режим - пропускаем подключение
if (testMode) {
logger.info('🔧 ТЕСТОВЫЙ РЕЖИМ: Подключение к БД отключено');
logger.info('📊 Используются mock-данные из config.json');
isDBConnected = false;
lastError = 'Test mode active - database connection disabled';
return false;
}
// Если уже подключены
if (isDBConnected && pool) {
logger.verbose('✅ БД уже подключена');
return true;
}
// Если идет переподключение
if (isReconnecting) {
logger.warn('⏳ Идет переподключение к БД, ожидаем...');
return false;
}
logger.info(`🔄 Подключение к MySQL... (попытка 1/${retries})`);
logger.info(`📊 Хост: ${dbConfig.host}, БД: ${dbConfig.database}, Порт: ${dbConfig.port}`);
// Цикл повторных попыток
for (let i = 0; i < retries; i++) {
try {
connectionAttempts++;
// Создаем пул соединений
pool = mysql.createPool(dbConfig);
// Проверяем подключение
const connection = await pool.getConnection();
await connection.ping();
connection.release();
// ✅ Успешно подключились
isDBConnected = true;
lastError = null;
isReconnecting = false;
logger.info('✅ Успешно подключено к MySQL');
logger.info(`📊 Пул соединений: макс. ${dbConfig.connectionLimit}`);
return true;
} catch (err) {
lastError = err.message;
isDBConnected = false;
pool = null;
logger.warn(`❌ Попытка ${i + 1}/${retries} не удалась: ${err.message}`);
if (i < retries - 1) {
logger.info(`⏳ Повторная попытка через ${delay/1000} секунд...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
logger.error('❌ Все попытки подключения к БД исчерпаны');
startBackgroundReconnection();
}
}
}
return false;
}
// ============================================
// 🔄 Фоновое переподключение
// ============================================
async function reconnectToDatabase() {
if (isReconnecting) {
logger.warn('⏳ Переподключение уже запущено');
return;
}
isReconnecting = true;
logger.info('🔄 Запущено фоновое переподключение к БД...');
const success = await connectDB(10, 5000);
if (success) {
logger.info('✅ Переподключение к БД успешно выполнено');
} else {
logger.error('❌ Переподключение к БД не удалось');
setTimeout(() => {
logger.info('🔄 Запускаем повторную попытку переподключения...');
reconnectToDatabase();
}, 30000);
}
isReconnecting = false;
}
function startBackgroundReconnection() {
if (!isReconnecting) {
logger.info('🔄 Запускаем фоновое переподключение...');
reconnectToDatabase();
}
}
// ============================================
// 📊 Функция выполнения запросов
// ============================================
/**
* Выполняет SQL-запрос с параметрами
* @param {string} sql - SQL-запрос с плейсхолдерами ?
* @param {Array} params - Параметры для подстановки
* @returns {Promise<Array>} - Результат запроса
*/
async function query(sql, params = []) {
if (!isDBConnected || !pool) {
throw new Error('База данных не подключена');
}
try {
const [rows] = await pool.execute(sql, params);
return rows;
} catch (err) {
logger.error('❌ Ошибка выполнения запроса', { sql, error: err.message });
throw err;
}
}
/**
* Выполняет SQL-запрос и возвращает первый результат
*/
async function queryOne(sql, params = []) {
const rows = await query(sql, params);
return rows.length > 0 ? rows[0] : null;
}
// ============================================
// 📊 Функции для получения статуса БД
// ============================================
async function getDBStatus() {
const testMode = await checkTestMode();
return {
connected: isDBConnected,
lastError: lastError,
connectionAttempts: connectionAttempts,
isReconnecting: isReconnecting,
testMode: testMode,
dbType: 'MySQL',
host: dbConfig.host,
database: dbConfig.database
};
}
function isDBConnectedFn() {
return isDBConnected;
}
// ============================================
// 🧹 Закрытие соединения
// ============================================
async function closeDB() {
try {
if (pool) {
await pool.end();
pool = null;
isDBConnected = false;
logger.info('🔒 Соединение с MySQL закрыто');
}
} catch (err) {
logger.error('❌ Ошибка при закрытии соединения с БД', { error: err.message });
}
}
// ============================================
// 📤 Экспорт модуля
// ============================================
module.exports = {
pool, // Пул соединений
query, // Выполнение запросов
queryOne, // Выполнение запроса с одним результатом
connectDB, // Функция подключения
getDBStatus, // Получение статуса
isDBConnected: isDBConnectedFn,
closeDB,
reconnectToDatabase
};