content/phys/ct-2024.yaml — 15 questions from ЦЭ,ЦТ 2024 across
6 topics (kinem, mol, emf, electro, magnet, optics) as proof of format.
backend/scripts/import-content.js — unified importer:
- Validates schema (subject, year, options, exactly-1-correct)
- Aliases (kinem, mol, ...) resolve to Russian topic names via get-or-create
- Deduplicates by first 80 chars of text (matches legacy seed_*.js behavior)
- Runs in a single transaction, idempotent re-runs
On fresh DB: 13 added (2 dedup collisions — same 80-char prefix, expected).
On prod DB: 0 added (all already exist from legacy seeds).
Second run on either: 0 added (dedup works).
Legacy seed_phys_ct2024.js kept as backup — see content/README.md
for migration guide.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scans all routes/*.js for :id-bearing routes without an auth-guard
(requireOwnership, requireRole, requirePermission, authMiddleware,
parentAuth, or spread middleware arrays like ...auth/...teacher).
BASELINE=56 — any new unprotected :id route causes exit(1).
Reduce BASELINE as old routes are migrated.
Usage:
npm run lint:routes
# or mark intentional public routes:
// @public-by-design: <reason>
router.get('/:token', handler);
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auto-running migrations on every server boot is dangerous — a broken
migration silently corrupts data or blocks server start. Now require
explicit `npm run migrate && npm run seed:permissions` before start.
Boot asserts schema exists (users + role_permissions tables) and
fails fast with a clear message otherwise.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Classroom performance:
- WebSocket server (ws-server.js) for low-latency cursor & stroke preview
Replaces HTTP POST per event → eliminates per-message auth overhead
Session member cache (30s TTL) avoids SQLite query per WS message
Fallback to HTTP POST when WS not connected
- Cursor throttle reduced 100ms → 33ms (~30fps)
- Stroke preview throttle reduced 50ms → 20ms
- whiteboard.js: render() is now rAF-gated (_doRender/_rafPending)
Multiple render() calls within one frame collapse into one repaint
document.hidden check — zero CPU when tab is in background
visibilitychange listener restores canvas on tab focus
Guest board:
- guestClassroom.js route: public token-based read-only access
- guest-board.html: name entry + read-only whiteboard + SSE
- SSE: addGuestClient/removeGuestClient/emitToGuests
Screen share picker:
- Discord-style modal with tab switching (screen/window/tab)
- Live video preview before confirming share
- useExistingScreenStream() in ClassroomRTC
Fullscreen exit overlay:
- #cr-fs-exit-overlay button inside cr-board-wrap
- Visible only via CSS :fullscreen selector (touchpad users)
File sharing from library:
- Teacher picks file from library, sends as styled card in chat
- crDownloadLibraryFile() fetches with Bearer auth
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>