5.8 KiB
Этап 5. Материалы курса
Статус: реализовано в academic-service и web (без изменения identity/progress).
Что изменилось
- Публичные
/api/v1/content/treeи/api/v1/content/{chapter}/{section}/{article}отдают только глобальные главы (course_id IS NULL) с аудиториейALL. Курсовые главы через публичный контракт больше не доступны. - Материалы курса открываются по
/api/v1/courses/{courseId}/content/...и требуют аутентификации. Доступ проверяется черезCourseAccessService: админ — любой курс, преподаватель — только назначенный, студент — только курс активной группы. - Администратор и преподаватель курса видят скрытые главы/разделы/статьи; студент — только опубликованные.
- В браузере материалы курса открываются по
/learn/course/{course_no}и/learn/course/{course_no}/{chapter}/{section}/{article}. Анонимный пользователь перенаправляется на/login, чужой курс даёт403. - Ссылки на материалы курса добавлены в кабинеты студента (карточки лабораторных), преподавателя и администратора.
- Админский просмотр и авторство глобальных материалов (
treeAll,/api/v1/content/all/...,/learnдля админа) сохранены. - Новые глобальные главы создаются и импортируются с
audience = "ALL". МиграцияV11__global_audience_all.sqlbackfill-ом проставляетALLстарым глобальным главам; курсовые главы остаются сaudience = NULL.
Видимость активов (закрыто после этапа 5)
Разрыв этапа 5 закрыт: активы привязаны к статьям и наследуют их видимость.
- Добавлены entity/repository
article_assets; импорт контента сохраняет связи для файлов, на которые ссылаются статьи. - Миграция
V12__asset_links_backfill.sqlbackfill-ом восстанавливает связи для уже загруженных статей: ищет вarticles.body_mdссылки вида/api/v1/content/assets/{name}, создаёт строкиarticle_assetsс MIME по расширению иsize = 0. Повторный запуск не создаёт дубликатов. - Публичный API
/api/v1/content/assets/{name}проверяет доступ по всем статьям, к которым привязан файл (достаточно одной видимой):- актив без связей в
article_assets— legacy-файл, публичен как раньше; - глобальная опубликованная статья
ALL— публична, в том числе анонимно; - глобальные
STUDENTS/TEACHERS— только с JWT и соответствующей ролью; - курсовой актив — только с JWT и доступом к курсу: админ видит любой курс,
преподаватель — свой (включая скрытые материалы), студент — только
опубликованные материалы своего курса. Скрытый актив для студента отдаёт
404(не раскрывает существование), чужой курс —403.
- актив без связей в
- Web-прокси
/api/v1/content/assets/{name}передаёт Bearer-токен из web-сессии, поэтому защищённые картинки на страницах/learn/...открываются, а на публичных страницах прокси остаётся анонимным.
Остаточный риск — legacy-активы без привязки. V12 закрывает только файлы,
на которые есть ссылки в markdown. Если файл был загружен, но ни одна статья на
него не ссылается (осиротевший или удалённый из текста актив), он остаётся
публичным. Нужна инвентаризация таких файлов и привязка к статье либо удаление;
до этого поведение сохраняется осознанно, чтобы не ломать старые публичные
ссылки.
Авторство материалов преподавателем (добавлено на этапе 6)
Преподаватель своего курса редактирует главы, разделы и статьи на
/teacher/courses/{id}/content: создание/переименование/удаление, markdown-
редактор с предпросмотром, публикация/снятие с публикации. API —
/api/v1/courses/{courseId}/content/**; любая изменяемая сущность проверяется
на принадлежность курсу. Глобальные материалы (course_id IS NULL,
аудитории ALL/STUDENTS/TEACHERS) по-прежнему создаёт и публикует только
админ.