- 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>
Питомец: кастомный фон (миграция 068 pet_bg_custom, POST /api/pet/bg/custom,
карточка «Свой фон (ИИ)» в гардеробной, применение картинкой).
Курсы: обложка-картинка (миграция 069 cover_image, генерация в модалке
редактирования, рендер вместо эмодзи).
Аватар: кнопка «Сгенерировать (ИИ)» в загрузке → кадрирование → модерация.
Доска (classroom): кнопка-инструмент «Сгенерировать картинку (ИИ)».
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Цвета 6→11 (розовый/оранжевый/бирюза/лайм/индиго). Узоры 5→8
(сердечки/звёздочки/клетка). Аксессуары 11→18 + новая зона «В лапах»
(бини, нимб, монокль, медаль, серёжки, палочка, шарик). Разблокировка за
учёбу: нимб (8 достижений), медаль (30 тестов). Фоны 7→11
(класс/лаборатория/зима/радуга) с градиентами и частицами. Новая вкладка
«Образы» — 4 готовых набора (Учёный/Волшебник/Чемпион/Милашка) применяют
аксессуары+узор+цвет одним кликом.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Аксессуары больше не навешиваются авто по уровню — теперь разблокируются
и НАДЕВАЮТСЯ по выбору (один на слот). Новые: шапочка выпускника, наушники,
бабочка (бесплатные — доступны даже при 0 XP). Сохранены цилиндр/корона/очки/
звезда с прежними порогами; дефолт повторяет старый вид (без сюрпризов).
Бэкенд: миграция pet_equipped, каталог ACCESSORY_CATALOG + /api/pet/equip
(валидация разблокировки и слотов). Рендер аксессуаров строго по equipped —
в обеих копиях (pet.html и pet-sprite.js для дашборда). UI гардероба на /pet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## P0
- admin.html:2608, red-book-ecosystem.html:489-495 — XSS: u.name/node.name_ru/description обернуты в LS.esc()
- classController.js getAnnouncements — добавлена проверка teacher_id (B14: учитель A не может читать объявления класса B)
## P1 — auth & validation
- authController.js — минимум пароля 6→8 символов (register + change password + login.html)
- gamificationController adminAward — валидация max XP/coins (1M), Number coercion
- shopController adminAwardCoins — валидация max + проверка changes>0
## P1 — race conditions
- petController.buyBg — atomic UPDATE WHERE coins>=? (race-safe)
- shopController.purchaseItem — atomic conditional UPDATE
- liveController — добавлен question_id в live_answers (миграция с пересозданием таблицы), история ответов сохраняется при смене вопроса учителем
- ws-server: invalidateDrawCache экспортирован, classroomController grant/revoke вызывают его → permission revoke применяется мгновенно (раньше до 10s stale)
## P1 — rate limits & retry
- rateLimit middleware: новый параметр byUser=true (использует req.user.id вместо IP — не блокирует пользователей за NAT)
- routes/classroom.js: reactionLimiter (15/5s) на /chat/:msgId/react, handLimiter (5/5s) на raise/lower hand
- api.js sendAnswer — retry 3x с exp backoff (300/1200/2700ms), не повторяет на 4xx (F5)
## P1 — performance
- classroomController.getStrokes — LIMIT 5000 + флаг hasMore (защита от OOM на 10K+ strokes)
- whiteboard.js _liveStrokes — TTL 1.5s на каждый live preview (auto-cleanup при крашe ремоут юзера)
## Infrastructure
- config.js: TURN_URL/USER/PASS env vars
- server.js: GET /api/ice-servers возвращает STUN + опциональный TURN из env
- classroom-rtc.js: фетчит /api/ice-servers вместо хардкода (поддержка TURN для NAT/CGNAT школьных сетей)
- .env.example: документация TURN
- db.js: PRAGMA synchronous=NORMAL (5x быстрее с WAL), cache_size 16MB, temp_store=MEMORY
- ws-server.js closeAll() + server.js shutdown — graceful WS shutdown при SIGTERM
## False positives (не баги, агенты ошиблись)
- assignmentController FK на tests — на самом деле users (migrate.js:317-318)
- .env в git — gitignore корректно исключает
- admin.html без requireAuth — есть LS.initPage() который вызывает requireAuth
- submissionsController IDOR — обе ручки уже проверяют teacher_id
- screenSender = null inside try/catch — на самом деле снаружи
- SSE без backoff — есть exponential 2s→30s
- sessionController NOT IN на пустом массиве — есть guard usedIds.length>0
- getChat без LIMIT — есть LIMIT 100/200
- trust proxy — установлен на server.js:105
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Безопасность:
- tests/🆔 скрыть is_correct и explanation для студентов (P0)
- SQL injection: limit/offset через placeholder вместо template literal
- Stored XSS: stripTags для lesson comments, flashcards, redBook sightings
- profile.html: escape e.message в showMsg (XSS через server error)
- attachment_url: валидация только /uploads/* путей
- requestId: генерировать UUID сервером, не доверять клиенту
- register: скрыть token_version из ответа
Надёжность:
- register: обработка UNIQUE constraint race condition
- pet buyBg: re-check баланса внутри транзакции
- DB errors: скрыть e.message в testController/questionController/courseController
- preferences: лимит 50KB на размер JSON
UX:
- board.html: debounce 250ms на search input
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>