feat(sim-builder): фаза 6 — раздача классу, клон, шаблоны, привязка к программе (custom_sims)

This commit is contained in:
Maxim Dolgolyov
2026-06-13 13:06:30 +03:00
parent 1bee332ae1
commit cbb6edf372
10 changed files with 803 additions and 30 deletions
+30 -2
View File
@@ -1,6 +1,30 @@
# Feature Context: Конструктор симуляций (SimForge)
## Current State
- **Фаза 6 РЕАЛИЗОВАНА** (в рабочем дереве, не закоммичено — коммит за оркестратором). Файлы:
`backend/src/controllers/customSimController.js` (+share/clone/related/addLink/removeLink, импорт
`pushNotif`), `backend/src/routes/customSims.js` (+POST `/:id/share`, POST `/:id/clone`, GET
`/:id/related`, POST `/:id/links`, DELETE `/:id/links/:linkId`), `js/api.js` (+`customSimShare/
Clone/Related/AddLink/DelLink`), `frontend/js/labs/lab-glue.js` (аддитивно в IIFE LabCustom:
кнопки share/clone/publish-toggle на карточках + делегат + `shareToClass/clone/setStatus`, ICON-блок),
`frontend/js/sim-builder.js` (тулбар: «Шаблон»/«Раздать»/publish-toggle; методы `setStatus/
openShareModal/openTemplateModal`; данные `TEMPLATES`×4; ICON.template/unpublish),
`backend/tests/custom-sims-share.test.js` (new, 15 it, все зелёные).
- **РЕШЕНИЕ копия-vs-доступ (зафиксировано):** published custom-sim видна ВСЕМ в каталоге /lab
(`list`/`get` отдают published любому; custom-sim НЕ гейтится allowlist'ом content_access 'sim' —
тот гейтит только legacy `lab_sims`). Поэтому «раздать классу» = (1) авто-публикация
(status→published), (2) ДОЛГОВЕЧНОЕ адресное уведомление ученикам класса через `pushNotif`
(notifications-таблица + SSE) со ссылкой `/lab?sim=custom:<id>`. БЕЗ копии (в отличие от «Моих
материалов», где оригинал приватный и копия обязательна) и БЕЗ записи content_access.
- **Привязка к программе:** переиспользован `lab_sim_links` с `sim_id='custom:<id>'` (sim_id TEXT —
отдельная таблица не нужна). Связями СВОЕЙ симуляции управляет владелец/admin (не только admin как
у lab_sims). Backend + GET `/related` готовы; UI-редактор связей + чипы в каталоге — остаток (handoff).
- **Клон:** копия spec вызвавшему как draft (title += ' (копия)', version=1). Источник: своя любая
ИЛИ чужая published (чужой draft → 403).
- Верификация: `node --check` всех 6 изм. файлов OK; эмодзи нет (скан — только `→`/`∑` в комментариях,
как в существующем коде); eval/Function нет; `npm run lint:routes` 0 unprotected (baseline 0);
`npm test` 216/224 pass (8 fail = тот же baseline: 3 auth.test + 5 page-тестов без jsdom — не моя
фаза; обе custom-sims-сьюты зелёные). git status: только мои файлы; classroom.html/lab.html не тронуты.
- **Фаза 5 РЕАЛИЗОВАНА** (в рабочем дереве, не закоммичено — коммит за оркестратором). Только
**аддитивные** правки двух файлов параллельной сессии (без рефактора их кода): рабочее дерево
по ним было ЧИСТЫМ до начала. classroom.html / backend / `_sim_deps.js` НЕ тронуты.
@@ -111,8 +135,12 @@
- Reuse > переписывание: сначала смотреть `_fx_motion`, `_graph_panel`, `graph.js`.
## RESUME STATE
- Последний коммит фичи: — (Ф0 + Ф1 + Ф2 + Ф3 + Ф4 + Ф5 реализованы, ещё не закоммичены — ждут оркестратора)
- Текущая фаза: Phase 5Каталог (✅ Implemented, pending commit) → дальше Phase 6 — Раздача / шаблоны / клон / программа
- Последний коммит фичи: — (Ф0..Ф6 реализованы, ещё не закоммичены — ждут оркестратора)
- Текущая фаза: Phase 6 — Раздача / шаблоны / клон / программа (✅ Implemented, pending commit) →
дальше Phase 7 — Доска онлайн-урока (последняя)
- Эндпоинты Ф6: share/clone/related/links на `/api/custom-sims/:id/*`; клиент `LS.customSimShare/
Clone/Related/AddLink/DelLink`. Раздача = авто-publish + pushNotif (НЕ копия). Связи — lab_sim_links
`sim_id='custom:<id>'`. Остаток Ф6: UI-редактор связей в билдере + чипы в каталоге (backend готов).
- Файлы Ф5 (аддитивные правки зоны параллельной сессии — БЕЗ рефактора): `frontend/js/labs/lab-init.js`
(+7 строк: хук `LabCustom.resolveId` в `openSim`), `frontend/js/labs/lab-glue.js` (renderSims +`!m._custom`
и вызов renderSection; init зовёт `LabCustom.init()`; новый IIFE `window.LabCustom`). `_sim_deps.js`,