- teacher/admin course pages, content, gradebook, profile, downloads - courseNo -> UUID только на границе REST API - обновлены e2e, шаблоны, тесты и docs - LDD-UI-002; baseline 59 claims / 385 тестов
6.3 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— secure-by-default:404для анонима, студента и преподавателя; толькоADMINможет скачать файл для инвентаризации/очистки; - глобальная опубликованная статья
ALL— публична, в том числе анонимно; - глобальные
STUDENTS/TEACHERS— только с JWT и соответствующей ролью; - курсовой актив — только с JWT и доступом к курсу: админ видит любой курс,
преподаватель — свой (включая скрытые материалы), студент — только
опубликованные материалы своего курса. Скрытый актив для студента отдаёт
404(не раскрывает существование), чужой курс —403.
- актив без связей в
- Web-прокси
/api/v1/content/assets/{name}передаёт Bearer-токен из web-сессии, поэтому защищённые картинки на страницах/learn/...открываются, а на публичных страницах прокси остаётся анонимным.
Legacy-активы без привязки (закрыто)
V12 закрывает файлы, на которые есть ссылки в markdown. Если файл был
загружен, но ни одна статья на него не ссылается, он больше не публичен:
аноним, студент и преподаватель получают 404, чтобы не раскрывать
существование файла. Админ скачивает такой файл и получает его имя на
странице /admin/legacy-assets, через GET /api/v1/content/legacy-assets
или scripts/legacy-assets.sh, после чего привязывает файл к статье или
удаляет. Старые внешние ссылки на несвязанные файлы у обычных пользователей
перестают работать — это осознанное поведение secure-by-default, а не
регрессия. Эндпоинт и страница инвентаризации защищены ROLE_ADMIN.
Авторство материалов преподавателем (добавлено на этапе 6)
Преподаватель своего курса редактирует главы, разделы и статьи на
/teacher/courses/{courseNo}/content: создание/переименование/удаление, markdown-
редактор с предпросмотром, публикация/снятие с публикации. API —
/api/v1/courses/{courseId}/content/**; любая изменяемая сущность проверяется
на принадлежность курсу. Глобальные материалы (course_id IS NULL,
аудитории ALL/STUDENTS/TEACHERS) по-прежнему создаёт и публикует только
админ.