Some checks failed
test / test (push) Failing after 24s
Реализация целевого дизайна (docs/architecture/target-design.md): - src/: модульная структура (config, logger, errors, http, core: auth/history/jobs/engines/comfy/codex/images) - Очередь заданий: FIFO, кониурентность codex=1 / comfy-upscale=2, MAX_QUEUED_JOBS=5, JSONL-журнал и восстановление после рестарта (running → interrupted, upscale requeue) - Персистентные сессии (sha256-хеши токенов), scrypt-хеш пароля, rate-limit входа, Origin-проверка, magic-byte валидация аплоадов - Атомарная запись манифеста истории, каскадное удаление, URL с фактическим расширением + 302-алиас /image.jpg, миграция history/ → data/history - Даунскейл sharp до 1024px перед Codex + масштабирование области референса - SSE /api/events с фолбэком на polling, фронтенд переведён на ES-модули (public/js/) - Тесты node:test + supertest (27), CI workflow test.yml, Docker/деплой: том kadr-data - Документация: README, docs/architecture/
81 lines
2.4 KiB
JavaScript
81 lines
2.4 KiB
JavaScript
"use strict";
|
|
|
|
const multer = require("multer");
|
|
const { AppError } = require("../../errors");
|
|
|
|
/**
|
|
* Единый middleware обработки ошибок приложения.
|
|
* @param {import('../../logger').Logger} logger
|
|
*/
|
|
function errorHandler(logger) {
|
|
// eslint-disable-next-line no-unused-vars
|
|
return (err, req, res, next) => {
|
|
const requestId = req.id || "req_unknown";
|
|
|
|
let status = 500;
|
|
let message = "Внутренняя ошибка сервера.";
|
|
let details = null;
|
|
|
|
if (err instanceof AppError) {
|
|
status = err.status;
|
|
message = err.message;
|
|
details = err.details || null;
|
|
} else if (err instanceof multer.MulterError) {
|
|
status = 400;
|
|
if (err.code === "LIMIT_FILE_SIZE") {
|
|
message = "Размер файла превышает допустимый лимит 25 МБ.";
|
|
} else if (err.code === "LIMIT_FILE_COUNT") {
|
|
message = "Превышено количество загружаемых файлов.";
|
|
} else {
|
|
message = `Ошибка загрузки файла: ${err.message}`;
|
|
}
|
|
details = { code: err.code, field: err.field };
|
|
} else if (err.type === "entity.too.large" || err.status === 413) {
|
|
status = 413;
|
|
message = "Размер переданных данных превышает лимит.";
|
|
} else if (err instanceof SyntaxError && err.status === 400 && "body" in err) {
|
|
status = 400;
|
|
message = "Некорректный JSON в теле запроса.";
|
|
} else if (err.status && typeof err.status === "number") {
|
|
status = err.status;
|
|
message = err.message || "Ошибка обработки запроса.";
|
|
}
|
|
|
|
const payload = {
|
|
ok: false,
|
|
error: message,
|
|
requestId,
|
|
};
|
|
|
|
if (details !== null && details !== undefined) {
|
|
payload.details = details;
|
|
}
|
|
|
|
if (status >= 500) {
|
|
logger.error("Необработанная ошибка сервера", {
|
|
requestId,
|
|
status,
|
|
error: err.message,
|
|
stack: err.stack,
|
|
url: req.originalUrl,
|
|
method: req.method,
|
|
});
|
|
} else {
|
|
logger.warn("Клиентская ошибка запроса", {
|
|
requestId,
|
|
status,
|
|
error: message,
|
|
details,
|
|
url: req.originalUrl,
|
|
method: req.method,
|
|
});
|
|
}
|
|
|
|
res.status(status).json(payload);
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
errorHandler,
|
|
};
|