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:
@@ -0,0 +1,44 @@
|
||||
-- 040_content_access.sql
|
||||
-- Per-class / per-student access control for textbooks and exam modules.
|
||||
--
|
||||
-- Model (chosen 2026-05-30): ALLOWLIST — content is hidden by default and must
|
||||
-- be explicitly opened for a class or a student. A student-level rule always
|
||||
-- overrides the class-level rule (точечные исключения). allow = 1 → открыт,
|
||||
-- allow = 0 → закрыт (используется только как индивидуальное исключение).
|
||||
--
|
||||
-- content_ref:
|
||||
-- • content_type='textbook' → top-level textbook slug (parent_slug IS NULL).
|
||||
-- Главы (parent_slug != NULL) наследуют доступ родителя.
|
||||
-- • content_type='exam' → exam_tracks.exam_key (например 'math9').
|
||||
|
||||
CREATE TABLE IF NOT EXISTS content_access (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
content_type TEXT NOT NULL CHECK (content_type IN ('textbook','exam')),
|
||||
content_ref TEXT NOT NULL,
|
||||
scope TEXT NOT NULL CHECK (scope IN ('class','student')),
|
||||
target_id INTEGER NOT NULL, -- class_id (scope=class) или user_id (scope=student)
|
||||
allow INTEGER NOT NULL DEFAULT 1 CHECK (allow IN (0,1)),
|
||||
created_by INTEGER REFERENCES users(id),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (content_type, content_ref, scope, target_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_content_access_lookup ON content_access (content_type, content_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_access_target ON content_access (content_type, scope, target_id);
|
||||
|
||||
-- ── Непрерывный переход ───────────────────────────────────────────────────
|
||||
-- До этой миграции всё было открыто всем. Чтобы переход на allowlist не отнял
|
||||
-- доступ задним числом, выдаём каждому существующему классу доступ ко всем
|
||||
-- активным учебникам верхнего уровня и ко всем включённым экзамен-трекам.
|
||||
-- Новый контент, добавленный позже, по умолчанию закрыт — его нужно открыть
|
||||
-- явно из админ-панели.
|
||||
|
||||
INSERT OR IGNORE INTO content_access (content_type, content_ref, scope, target_id, allow)
|
||||
SELECT 'textbook', t.slug, 'class', c.id, 1
|
||||
FROM textbooks t CROSS JOIN classes c
|
||||
WHERE t.is_active = 1 AND t.parent_slug IS NULL;
|
||||
|
||||
INSERT OR IGNORE INTO content_access (content_type, content_ref, scope, target_id, allow)
|
||||
SELECT 'exam', e.exam_key, 'class', c.id, 1
|
||||
FROM exam_tracks e CROSS JOIN classes c
|
||||
WHERE e.enabled = 1;
|
||||
Reference in New Issue
Block a user