feat(access): доступ к учебникам и экзаменам по классам/ученикам из админ-панели

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

- миграция 040: таблица content_access + непрерывный переход
  (всем существующим классам открыт текущий контент)
- сервис contentAccess: резолвинг доступа, главы наследуют хаб
- API /api/access (catalog/targets/rules) для admin+teacher
- гейты: каталог учебников, router.param slug/examKey, фильтр tracks
- клиентские редиректы на /403 (textbook-tracker, exam-prep boot)
- раздел админки «Доступ к учебникам»: классы + ученики (tri-state)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-30 12:33:05 +03:00
parent 98f955a85e
commit 471171b77c
12 changed files with 564 additions and 4 deletions
+28
View File
@@ -956,6 +956,9 @@
<button class="admin-nav-item" data-tab="topics" onclick="switchTab(this)">
<i data-lucide="list-tree" style="width:15px;height:15px"></i> Темы
</button>
<button class="admin-nav-item" data-tab="access" onclick="switchTab(this)">
<i data-lucide="book-lock" style="width:15px;height:15px"></i> Доступ к учебникам
</button>
</div>
</div>
@@ -1509,6 +1512,30 @@
<div id="topics-list"></div>
</div>
<!-- ── Доступ к учебникам / экзаменам ── -->
<div class="tab-pane" id="tab-access">
<div class="section-title">Доступ к учебникам и экзаменам</div>
<p style="color:var(--muted);font-size:13px;margin:4px 0 16px;max-width:720px">
По умолчанию доступ <b>закрыт</b>. Откройте учебник или экзамен-модуль нужным классам.
Внутри класса можно сделать точечное исключение для отдельного ученика —
индивидуальное правило важнее правила класса.
</p>
<div class="acc-layout" style="display:flex;gap:20px;align-items:flex-start;flex-wrap:wrap">
<div class="acc-list adm-panel" style="flex:0 0 280px;max-width:320px;padding:10px">
<div class="acc-list-head" style="font-weight:600;font-size:13px;color:var(--text-3);padding:6px 8px">Учебники</div>
<div id="acc-textbooks"></div>
<div class="acc-list-head" style="font-weight:600;font-size:13px;color:var(--text-3);padding:12px 8px 6px">Экзамены</div>
<div id="acc-exams"></div>
</div>
<div class="acc-detail adm-panel" style="flex:1;min-width:340px;padding:18px">
<div id="acc-detail-empty" style="color:var(--muted);font-size:14px">
Выберите учебник или экзамен слева, чтобы настроить доступ.
</div>
<div id="acc-detail" style="display:none"></div>
</div>
</div>
</div>
<!-- ── Рассылка ── -->
<div class="tab-pane" id="tab-broadcast">
<div class="section-title">Рассылка уведомлений</div>
@@ -2009,6 +2036,7 @@
<script src="/js/admin/sections/sessions.js"></script>
<script src="/js/admin/sections/user-detail.js"></script>
<script src="/js/admin/sections/session-detail.js"></script>
<script src="/js/admin/sections/access.js"></script>
<script src="/js/admin/palette.js"></script>
<script src="/js/admin/admin.js"></script>
</div>