Files
photo-editor/src/http/middleware/error-handler.js
Coder 401969cfdb
Some checks failed
test / test (push) Failing after 24s
v2: модульный монолит — очередь, персистентность, безопасность, SSE, тесты
Реализация целевого дизайна (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/
2026-08-27 12:52:27 +07:00

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,
};