Files
Learn_System/.claude/memory/project_content_access.md
T
Maxim Dolgolyov 8a7091ddec chore(memory): снимок файлов памяти Claude в репозиторий для переноса
Копия пользовательской автопамяти (29 фактов + индекс MEMORY.md) в
.claude/memory/, чтобы переносить между машинами через git.
README.md — как восстановить в пользовательскую папку на другой машине.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 08:32:16 +03:00

11 KiB
Raw Blame History

name, description, metadata
name description metadata
project_content_access Система доступа к учебникам/экзаменам по классам и ученикам из админ-панели (allowlist, ученик > класс)
node_type type originSessionId
memory project d08c4099-7d49-4f89-b842-d9d7af56af47

Доступ к учебникам и экзамен-модулям («экзамен 9 класс» = exam_key math9) управляется из админ-панели (вкладка «Доступ к учебникам», группа «Пользователи», рядом с «Права доступа»). Реализовано 2026-05-30.

Модель: ALLOWLIST — по умолчанию закрыто, нужно явно открыть. Правило ученика важнее правила класса (точечные исключения). Управляют админ (все классы/ученики) и учителя (только свои классы и ученики своих классов / привязанные через teacher_students).

Why: так выбрал пользователь (безопаснее). Миграция 040 при внедрении выдала всем существующим классам доступ к текущему контенту, чтобы переход не отнял доступ задним числом; новый контент по умолчанию закрыт.

How to apply:

  • Таблица content_access (миграция 040): content_type ('textbook'|'exam'), content_ref (top-level slug учебника / exam_key), scope ('class'|'student'), target_id, allow (1 открыть / 0 закрыть-исключение). Главы (parent_slug != NULL) наследуют доступ хаба.
  • Резолвинг — backend/src/services/contentAccess.js (canAccessTextbook/canAccessExam/filterTextbooks/allowedRefs). Админ/учитель проходят всегда.
  • Гейты: textbooks.js фильтр каталога + router.param('slug'); exam-prep.js фильтр /tracks + router.param('examKey'). HTML-страницы не гейтятся на сервере (JWT в localStorage) — клиентский редирект на /403 в textbook-tracker.js (loadServerProgress) и exam-prep/common.js (boot).
  • API /api/access (routes/access.js, admin+teacher): GET catalog, GET targets, GET summary, GET class/:id, GET rules, POST rules.
  • Фронт: LS.accessCatalog/accessTargets/accessSummary/accessClassOpen/accessRules/accessSetRule; секция frontend/js/admin/sections/access.js — два режима «По контенту» / «По классу», массовые «Открыть всем/Закрыть у всех», бейджи N/M открытых классов.
  • При удалении класса/ученика правила чистятся вручную (нет FK): classController.deleteClass и adminController._deleteUserTx.

При добавлении нового учебника/экзамена он закрыт по умолчанию — открыть классам через админку.

РЕВЬЮ + ПЕРЕРАБОТКА (2026-06-03): проведено ревью всей системы прав (есть 2,5 системы: content_access для учебников/экзаменов по классам; role/user_permissions через [registry.js] глобально по ролям — туда входят simulations.access, испытания, магазин, manage-права; курсы — отдельно по is_published+класс). План: plans/access-redesign/PLAN.md (4 фазы). Пользователь сказал «включай всё» + «делаем как лучше».

  • Фаза 0 ГОТОВА (commit 1bbddc0): contentAccess.purgeAccessFor(scope,id) — единая чистка правил (нет FK); deleteClass и adminController._deleteUserTx переведены на неё; confirm() на массовое «Закрыть» в админ-UI; тест backend/tests/content-access.test.js (резолвер allowlist, ученик>класс, наследование главой, admin/teacher bypass, purge). Решение по kickMember: персональные правила привязаны к УЧЕНИКУ, не к членству → при исключении НЕ чистим (намеренный override).
  • Фаза 2a ГОТОВА (commit 67a70c6): режим «Матрица» в админ-секции access.js (3-й таб) — таблица контент×классы с чекбоксами + поиск (обновляет только tbody, фокус сохраняется). Backend GET /api/access/matrix (классы+карта открытого, скоуп учителя); клиент LS.accessMatrix. /api/access смонтирован в тест-харнесс setup.js. Тест 11/11.
  • Фаза 2b ГОТОВА (commit 596e8d8): поиск + подзаголовки по предмету в левой колонке (режим «По контенту», обновляет только список — фокус ввода сохраняется) + бейдж «эффективный доступ» у ученика в раскрытом классе («видит/не видит · лично|по классу|по умолч.», считается клиентски из _rules).
  • Фаза 1 (модель ДОБАВОЧНАЯ) — СИМУЛЯЦИИ ГОТОВЫ (commits 9a145e5 + 4549b4e): content_ref для sim = lab_sims.id (TEXT, напр. 'graph'). Миграция 051 пересобрала content_access с CHECK ('textbook','exam','course','sim') + мост «открыть все включённые симуляции всем существующим классам». GET /api/lab/sims (lab.js) фильтрует список для НЕпривилегированных по allowedRefs(uid,'sim'); admin/ teacher — все. Ролевой simulations.access остался «модуль вкл.» (добавочно, AND). Админ-секция «Доступ» обобщена на тип 'sim' (catalog/summary/matrix/class в access.js route + UI helpers BUCKET/KEYNAME/ CONTENT_TYPES). Тесты: lab-access 4/4, content-access 12, lab-sims переведён на admin. ВАЖНО: новый класс получает симуляции только после явного открытия в админке (allowlist) — мост покрыл лишь классы, существовавшие на момент миграции 051.
  • Фаза 1c — КУРСЫ ГОТОВЫ (commit 9b7585a): content_ref = courses.id (как TEXT). Миграция 052 — мост «открыть все опубликованные курсы всем существующим классам». courseController.list+search фильтруют для НЕпривилегированных через courseVisible(user); admin/teacher — все. catalog отдаёт курсы; CONTENT_TYPES в admin access.js = textbook,exam,sim,course (все 4 типа в UI). Тест course-access 4/4. class_courses оставлен для назначений с дедлайном (сверх видимости).
  • ФАЗА 1 ЗАВЕРШЕНА (симуляции + курсы). Backend 213 pass (3 baseline-Auth; «intro» chemistry8-page флакует под нагрузкой — НЕ про доступ, в изоляции зелёный). Харнесс setup.js монтирует /api/access, /api/lab, /api/courses. ВАЖНО (allowlist): новый класс/новый опубликованный курс/новая симуляция по умолчанию закрыты — открыть в админке; loose-ученики (без класса) не видят sim/курсы без личного правила.
  • Фаза 2c ГОТОВА (commits d1f2473, 6a874a3, b702b04, 3a59f56): массовые операции матрицы (клик по контенту/классу), «Открыть весь предмет классу» (режим «По классу»), история правил (GET /api/access/log, admin-only, из admin_audit_log; кнопка «История изменений» в режиме «По контенту»; клиент LS.accessLog), пресет «Скопировать доступ из класса» (режим «По классу»), объединение вкладок по смыслу («Доступ · контент» + «Доступ · роли» рядом в admin.html). content-access тест 13/13. Полное слияние двух вкладок в одну с под-вкладками НЕ делалось (структурно крупнее, оставлено на потом).
  • Фаза 3 — ОТЛОЖЕНА ОСОЗНАННО (низкий ROI, решение пользователя 2026-06-03). Серверный гейт HTML /textbook/:slug, /exam-prep/:examKey (сейчас отдаются всем; блок только клиентским редиректом на /403, ДАННЫЕ через API уже гейтятся). Чтобы гейтить сам HTML на сервере, нужен переход с JWT-в-localStorage на httpOnly-cookie сессию — переделка ВСЕЙ аутентификации (логин/каждый запрос/logout/token_version/CSRF/ мобилка), большой риск ради крошечной выгоды (видно лишь пустой каркас страницы, не контент). Это школьная платформа, не ПДн/финансы. ДЕЛАТЬ ТОЛЬКО при конкретном требовании приватности контента или комплаенсе. План: plans/access-redesign/PLAN.md Фаза 3. Отдельная ветка feature/html-access-gate.

Возможные улучшения (старое, до ревью — теперь решено ДЕЛАТЬ, см. план):

  1. Единая per-class модель для всего контента. Сейчас неоднородность: учебники/экзамены гейтятся по классам (content_access), а теория/курсы (theory.access) и симуляции (simulations.access) — глобально через role-permissions (см. registry.js). Можно расширить content_access типами course/sim, чтобы их тоже можно было открывать/закрывать по классам. Решили пока НЕ делать (меняет поведение двух работающих типов контента).
  2. Серверный гейт HTML-страниц. /textbook/:slug и /exam-prep/* отдают статический HTML без проверки токена (JWT в localStorage, не cookie) — защита только на API + клиентский редирект на /403. Неподделываемая блокировка самих страниц требует cookie-аутентификации (крупная отдельная задача).