fix(reliability): multer-ошибки, process-хендлеры, анти-гонка питомца, flashcards (Спринт2)

- errorHandler: MulterError → 413 «слишком большой» / 400 (а не 500).
- server: process.on(unhandledRejection/uncaughtException) — глобальная страховка
  с логированием, процесс не падает от единичной асинхронной ошибки.
- pet: атомарный CAS на кулдаунах petAction/starCatch/feedPet
  (UPDATE ... WHERE last IS ?, начисление только при changes=1) — нет двойного
  начисления при параллельных запросах. Проверено на семантике node:sqlite.
- assistant.flashcardsFromText: await callLLMFailover в try/catch → 502 вместо
  необработанного отклонения промиса.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-12 22:08:02 +03:00
parent 646e93cf46
commit 09c6c2b21d
4 changed files with 34 additions and 6 deletions
+9
View File
@@ -39,6 +39,15 @@ function requestId(req, res, next) {
* programmer (5xx) — unexpected bugs → error level, stack logged, message hidden in prod
*/
function errorHandler(err, req, res, _next) {
// Ошибки загрузки multer → внятный 4xx вместо 500.
if (err && err.name === 'MulterError') {
const tooLarge = err.code === 'LIMIT_FILE_SIZE';
if (res.headersSent) return;
return res.status(tooLarge ? 413 : 400).json({
error: tooLarge ? 'Файл слишком большой' : 'Ошибка загрузки файла',
requestId: req.requestId,
});
}
const status = err.status || err.statusCode || 500;
const isOperational = status >= 400 && status < 500;
const isProd = process.env.NODE_ENV === 'production';