v2: модульный монолит — очередь, персистентность, безопасность, SSE, тесты
Some checks failed
test / test (push) Failing after 24s
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/
This commit is contained in:
80
src/http/middleware/error-handler.js
Normal file
80
src/http/middleware/error-handler.js
Normal file
@@ -0,0 +1,80 @@
|
||||
"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,
|
||||
};
|
||||
Reference in New Issue
Block a user