Files
Learn_System/plans/my-materials/PLAN_V2.md
T
Maxim Dolgolyov 786419ce01 feat(materials): серверные миниатюры (sharp) + пагинация рендера списка
Миниатюры: uploadPersonalFile генерирует webp ≤480px (sharp), возвращает {url, thumbUrl}; колонка thumb_url (мигр.074); грид рисует <img> на миниатюре, просмотр/скачивание/аннотация — полный url. Ссылочная чистка матчит url И thumb_url; share копирует thumb; квота учитывает файл+миниатюру. Сейверы board-clip/material-save/textbook-clip/draw пробрасывают thumbUrl.

Пагинация: клиент рендерит PAGE_SIZE=60 карточек + «Показать ещё» (сброс на смену фильтра), сохраняя клиентский поиск/сортировку над полным списком.

Тесты: materials.test.js 16→19. План V2 выполнен.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 14:40:23 +03:00

6.5 KiB
Raw Blame History

«Мои материалы» — v2: харднинг и доводка

Составлен Opus 2026-06-13. Базовый план (PLAN.md, Фазы 16) полностью реализован. Его раздел «Сквозные риски» отложил ровно то, что закрывает этот план: учёт/лимиты/чистку хранилища и materials.test.js. Источник истины по текущему состоянию — код (studentMaterialsController.js, materials.js, my-materials.html, board-clip.js, material-save.js) и reference_student_materials.

Готчи проекта: новый :id-роут → // @public-by-design + проверка владельца; большие HTML — только Edit; без эмодзи (inline SVG .ic); коммит поимённо + push; перезапуск сервера при правке backend; ветка feature/sim-builder в рабочем дереве — НЕ коммитить чужие правки, только свои файлы.


Фаза 1 — Целостность и безопасность (backend, фундамент) цель этого захода

  1. Ссылочно-подсчётная чистка файлов. DELETE /:id и смена url (аннотация) сейчас оставляют файл в uploads/materials/ сиротой. share копирует url дословно → несколько строк ссылаются на ОДИН файл, поэтому unlink только когда на url не ссылается ни одна строка. Хелпер releaseFileForUrl(url) вызывается ПОСЛЕ delete/update.
  2. Allowlist схемы URL. create/update принимали любой urllink со схемой javascript: рендерится как рабочий <a href> (раздача делает это вектором учитель→ученики). Хелпер safeUrl: только http(s):// или app-relative /… (не //host); иначе 400.
  3. Квота на пользователя. Колонка bytes (мигр. 073), счёт SUM(bytes)/COUNT(*). Лимит по числу материалов — в create(); лимит по байтам — в uploadPersonalFile (до приёма файла). Конфигурируемо через MATERIALS_MAX_ITEMS / MATERIALS_MAX_BYTES (для тестов — низкий потолок).
  4. backend/tests/materials.test.js — CRUD, владелец (403/404), коллекции, share-копия + роль/owner, валидация URL, лимит числа, ссылочная чистка (прямой вызов хелпера на временном файле).

Фаза 2 — Производительность

  • GET /api/materials отдаёт обрезанный body (первые 1000 симв.) + флаг body_trunc; полный текст — ленивый GET /api/materials/:id (getOne, owner-only). Клиент ensureFullBody() подгружает перед просмотром/правкой/флешкартой (иначе правка сохранила бы усечённый текст).
  • Пагинация рендера: клиент держит весь список (поиск/фильтр/сортировка в памяти), но в DOM рисует PAGE_SIZE=60 карточек + «Показать ещё»; _shown сбрасывается на смену фильтра. Снимает стоимость рендера тысяч узлов, не ломая клиентский поиск (keyset на сервере не нужен на текущих объёмах).
  • Серверные миниатюры board/image: uploadPersonalFile (sharp → webp ≤480px) возвращает {url, thumbUrl}; колонка thumb_url (мигр. 074); грид рисует <img src=thumb_url||url>, просмотр/скачивание/аннотация — полный url. Чистится по ссылкам (releaseFileForUrl теперь матчит url и thumb_url); share копирует thumb; квота считает файл+миниатюру. Клиентские сейверы (board-clip/material-save/textbook-clip/draw) пробрасывают thumbUrl.

Фаза 3 — Доводка заложенных фич

  • UI тегов: ввод в модалках создания/правки + чипы на карточке (клик → фильтр) + пилюля активного фильтра.
  • Ссылка «открыть исходный урок» на карточке (/my-lessons?session=<id>, есть source_session_id).
  • Цвет папки (палитра 8 пресетов, тинт иконки в рейле) + сортировка папок «Выше/Ниже» в модалке правки (нормализует sort_order к индексам). safeColor гейтит inline-style инъекцию (только hex).

Фаза 4 — UX

  • Варианты сортировки (новые/старые/имя/тип) — селект в тулбаре.
  • Множественный выбор (чекбокс на карточке) + панель массовых действий (переместить/удалить, reuse per-item API).
  • Живое превью KaTeX в редакторе заметки (oninput → mmPreviewmathHtml).

Статус — ПЛАН V2 ВЫПОЛНЕН

Ф1–Ф4 . Backend: 19 тестов materials.test.js (CRUD/владелец/коллекции/share/URL-allowlist/квота/ ссылочная чистка url+thumb/round-trip thumb_url). Frontend: headless-смоук my-materials.html (синтаксис + deep-link/теги/чекбокс/bulk/тинт папки + <img> на thumb_url + пагинация «Показать ещё»). sharp-пайплайн и client-сейверы (board-clip/material-save/textbook-clip) проверены. Открытого из плана не осталось.


Порядок

Ф1 (этот заход) → Ф2 → Ф3 → Ф4. Ф1 — серверный фундамент (риск-возврат, без него фронт-фичи множат мусор). Дальше преимущественно фронтенд my-materials.html + точечные ручки API.