fix: глубокое ревью онлайн-урока — 14 исправлений (P0-P3)

P0 — краши:
- CREATE TABLE classroom_hands в migrate.js (отсутствовала)
- emit→emitToUser для allowDraw/revokeDraw/mutePeer (WS доставка)
- deleteHistorySession обёрнут в db.transaction() + добавлена очистка hands/invites

P1 — гонки и безопасность:
- deletePage: 4 SQL в транзакции (race при параллельной записи)
- postStrokes: MAX(seq) внутрь транзакции (дубли seq)
- duplicatePage: добавлен seq в INSERT (NOT NULL crash)
- hasAccess для lowerHand/getHands/reactToMessage (утечка данных)
- loadTemplate: проверка owner шаблона
- attachment_url: только /uploads/* (XSS через javascript:/data: URI)
- wbFlushBatch: backoff при ошибке (было 12.5 req/s retry)
- pagehide leave: keepalive fetch для гарантированной доставки
- _wbOwnIds: cap 2000 (утечка памяти на длинных уроках)

P2-P3:
- simState: лимит 64KB (предотвращает OOM broadcast)
- ws-server кеши: cleanup drawCache при invalidateSession

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-04-16 09:22:39 +03:00
parent f1e6ed7f2d
commit 6cd0cf34d4
4 changed files with 92 additions and 36 deletions
+7 -1
View File
@@ -88,7 +88,13 @@ function _getMembers(sessionId) {
return entry;
}
function _invalidateSession(sessionId) { _cache.delete(sessionId); }
function _invalidateSession(sessionId) {
_cache.delete(sessionId);
// Cleanup draw cache entries for this session
for (const key of _drawCache.keys()) {
if (key.startsWith(sessionId + ':')) _drawCache.delete(key);
}
}
/* ── Draw permission cache (10s TTL) ─────────────────────────────────── */
const _drawCache = new Map();