Files
DLE/backend/middleware/errorHandler.js
2026-03-01 22:03:48 +03:00

131 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (c) 2024-2026 Тарабанов Александр Викторович
* 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/VC-HB3-Accelerator
*/
const { AppError, ErrorTypes } = require('../utils/error');
const logger = require('../utils/logger');
const { ERROR_CODES } = require('../utils/constants');
/**
* Middleware для обработки ошибок
*/
const errorHandler = (err, req, res, next) => {
// console.log('errorHandler called, arguments:', arguments);
// console.log('typeof res:', typeof res, 'isFunction:', typeof res === 'function');
// console.error('errorHandler: err =', err);
// console.error('errorHandler: typeof err =', typeof err);
// console.error('errorHandler: stack =', err && err.stack);
// Логируем ошибку
logger.error(`Error: ${err.message}`, {
stack: err.stack,
url: req.originalUrl,
method: req.method,
ip: req.ip,
userId: req.session?.userId,
});
// Определяем тип ошибки
let statusCode = 500;
let errorCode = ERROR_CODES.INTERNAL_ERROR;
let errorMessage = 'Внутренняя ошибка сервера';
// Обрабатываем разные типы ошибок
if (err.name === 'UnauthorizedError' || err.status === 401) {
statusCode = 401;
errorCode = ERROR_CODES.UNAUTHORIZED;
errorMessage = 'Требуется аутентификация';
} else if (err.status === 403) {
statusCode = 403;
errorCode = ERROR_CODES.FORBIDDEN;
errorMessage = 'Доступ запрещен';
} else if (err.status === 404) {
statusCode = 404;
errorCode = ERROR_CODES.NOT_FOUND;
errorMessage = 'Ресурс не найден';
} else if (err.status === 400) {
statusCode = 400;
errorCode = ERROR_CODES.BAD_REQUEST;
errorMessage = err.message || 'Некорректный запрос';
}
// В режиме разработки возвращаем стек ошибки
const devError = process.env.NODE_ENV === 'development' ? { stack: err.stack } : {};
// Проверяем, что ответ еще не был отправлен и соединение не закрыто
if (res.headersSent || res.destroyed) {
console.error('[errorHandler] Ответ уже отправлен или соединение закрыто, пропускаем обработку ошибки');
return;
}
// Для ошибок подключения к БД возвращаем понятное сообщение
if (err.message && err.message.includes('timeout exceeded when trying to connect')) {
errorMessage = 'Ошибка подключения к базе данных. Попробуйте позже.';
statusCode = 503; // Service Unavailable
}
// Проверяем еще раз перед отправкой (может измениться состояние)
if (res.headersSent || res.destroyed) {
console.error('[errorHandler] Состояние изменилось, пропускаем отправку ответа');
return;
}
// Отправляем ответ клиенту
// Используем формат, совместимый с frontend (success: false, message: string)
try {
// Финальная проверка перед отправкой - состояние могло измениться
if (res.headersSent || res.destroyed || res.finished || !res.writable) {
console.error('[errorHandler] Финальная проверка: ответ уже отправлен, соединение закрыто или завершено');
return;
}
// Проверяем writableEnded - новый флаг в Node.js
if (res.writableEnded) {
console.error('[errorHandler] Ответ уже завершен (writableEnded)');
return;
}
res.status(statusCode).json({
success: false,
message: errorMessage,
error: {
code: errorCode,
message: errorMessage,
...devError,
},
});
} catch (sendErr) {
// Если произошла ошибка при отправке (например, соединение закрыто), просто логируем
// Игнорируем ошибки, связанные с уже отправленными заголовками или закрытым соединением
if (sendErr.code !== 'ERR_HTTP_HEADERS_SENT' &&
sendErr.code !== 'ECONNRESET' &&
sendErr.code !== 'EPIPE' &&
!sendErr.message?.includes('Cannot set headers after they are sent')) {
console.error('[errorHandler] Ошибка при отправке ответа:', sendErr.message);
}
}
}
/**
* Функция для создания ошибок с определенным статусом
* @param {string} message - Сообщение об ошибке
* @param {number} status - HTTP-статус ошибки
* @returns {Error} - Объект ошибки
*/
function createError(message, status) {
const error = new Error(message);
error.status = status;
return error;
}
module.exports = errorHandler;
// Если нужен createError для других файлов:
// module.exports.createError = createError;