/** * Централизованный обработчик ошибок */ const promClient = require('prom-client'); // Метрики const http304 = new promClient.Counter({ name: 'http_304_total', help: 'HTTP 304 responses' }); const http412 = new promClient.Counter({ name: 'http_412_total', help: 'HTTP 412 responses' }); const http423 = new promClient.Counter({ name: 'http_423_total', help: 'HTTP 423 responses' }); /** * Отправить успешный ответ с метаданными */ function sendOk(res, meta) { if (meta?.etag) res.set('ETag', String(meta.etag)); if (meta?.lastModified) res.set('Last-Modified', new Date(meta.lastModified).toUTCString()); if (typeof meta?.contentLength === 'number') res.set('Content-Length-Source', String(meta.contentLength)); return res.json({ ok: true, etag: meta?.etag || null, lastModified: meta?.lastModified || null, contentLength: meta?.contentLength ?? null }); } /** * Отправить ошибку */ function sendError(res, status, message, code, details) { const requestId = res.req?.id; try { if (status === 304) http304.inc(); if (status === 412) http412.inc(); if (status === 423) http423.inc(); } catch {} return res.status(status).json({ code, message, details, requestId }); } /** * Проверить If-None-Match заголовок */ function checkIfNoneMatch(req, res, etag) { const inm = req.headers && (req.headers['if-none-match'] || req.headers['If-None-Match']); if (inm && etag && String(inm) === String(etag)) { try { http304.inc(); } catch {} res.status(304).end(); return true; } return false; } /** * Централизованный error handler middleware */ function errorHandler(err, req, res, next) { const status = typeof err?.status === 'number' ? err.status : 500; const code = err?.code || 'E_INTERNAL'; const message = status === 500 && process.env.NODE_ENV === 'production' ? 'Internal Server Error' : (err?.message || 'Error'); const details = err?.details; const requestId = req?.id; try { req.log?.error({ err, code, requestId }, 'request error'); } catch {} res.status(status).json({ code, message, details, requestId }); } module.exports = { sendOk, sendError, checkIfNoneMatch, errorHandler, http304, http412, http423, };