From d6461a3a9c9128200197b262103e4e90250c84ed Mon Sep 17 00:00:00 2001 From: Vladislav Kopnin Date: Tue, 27 Oct 2026 19:41:42 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=B2=D0=B8=D0=B4=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B8=20=D0=B8=D1=82=D0=BE=D0=B3=D0=BE=D0=B2=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/01-architecture.md | 6 ++- docs/06-authoring.md | 6 ++- docs/07-groups-courses.md | 3 ++ docs/08-groups-courses-plan.md | 25 +++++----- docs/10-stage0-report.md | 3 +- docs/12-stage2-report.md | 9 +++- docs/15-stage5-course-materials.md | 35 ++++++++++---- docs/16-stage6-report.md | 76 +++++++++++++++++++----------- docs/README.md | 2 +- 9 files changed, 109 insertions(+), 56 deletions(-) diff --git a/docs/01-architecture.md b/docs/01-architecture.md index 4e59b30..5e06953 100644 --- a/docs/01-architecture.md +++ b/docs/01-architecture.md @@ -141,8 +141,10 @@ flowchart TD - Роли: `ADMIN`, `STUDENT`; конвертер ролей читает claim `role` как `ROLE_`. - Публичные пути: `/actuator/**`, `/api/v1/auth/{health,setup,login,jwks.json}`, - `/api/v1/content/tree`, `/api/v1/content/assets/**`, - `/api/v1/content/{chapter}/{section}/{article}`. + `/api/v1/content/tree`, `/api/v1/content/{chapter}/{section}/{article}`. + `/api/v1/content/assets/**` gateway пропускает без JWT (legacy/публичные + `ALL`-активы), но academic проверяет видимость статьи и для курсовых или + ролевых активов требует токен. - RSA-ключи identity хранятся в таблице `settings` и публикуются в JWKS. ## 5. Рендеринг страниц и защита форм (web) diff --git a/docs/06-authoring.md b/docs/06-authoring.md index 77101eb..19862a3 100644 --- a/docs/06-authoring.md +++ b/docs/06-authoring.md @@ -125,8 +125,10 @@ printf("Ответ: 42\n"); * Главы, которых нет в архиве совсем, не затрагиваются — их нужно удалить вручную. * Минимальный архив (например, только один раздел + `assets/`) безопасно импортировать в режиме merge: остальные материалы останутся как есть. -* Ссылки вида `assets/имя` автоматически заменяются на публичный адрес - `/api/v1/content/assets/...`; сами файлы складываются в хранилище. +* Ссылки вида `assets/имя` автоматически заменяются на адрес + `/api/v1/content/assets/...`; сами файлы складываются в хранилище. Для + курсовых статей этот адрес требует сессию пользователя курса, для публичных + `ALL`-материалов и старых несвязанных файлов остаётся публичным. ## Публикация и скрытие diff --git a/docs/07-groups-courses.md b/docs/07-groups-courses.md index a691a45..a867efd 100644 --- a/docs/07-groups-courses.md +++ b/docs/07-groups-courses.md @@ -272,6 +272,9 @@ CREATE UNIQUE INDEX chapters_slug_course_uidx ON chapters (course_id, slug) WHER Признак видимости актив наследует от статьи→раздела→главы (course/audience). Требуется проверка доступа в маршруте выдачи активов (см. §6). Отдельных колонок достаточно опционально; минимум — корректная авторизация. +> В реализации backfill связей вынесен в `V12__asset_links_backfill.sql`, а +> проверка доступа — в `/api/v1/content/assets/{name}` (см. [15](15-stage5-course-materials.md)). + **V10 — уникальное название курса (реальный файл `V10__course_title_unique.sql`)** ```sql -- Защита от дублей курса при конкурентном bootstrap и повторном создании. diff --git a/docs/08-groups-courses-plan.md b/docs/08-groups-courses-plan.md index 1505d74..a8c5ee5 100644 --- a/docs/08-groups-courses-plan.md +++ b/docs/08-groups-courses-plan.md @@ -2,7 +2,7 @@ **Статус:** план согласован, этапы 0–5 выполнены. Проверка этапа 6 выполнена (тесты, upgrade миграций, security, deploy-runbook), но этап 6 закрыт не -полностью: остаются права преподавателя на запись, видимость активов и CI — +полностью: остаются права преподавателя на запись и CI — см. § 9.1 и [16-stage6-report.md](16-stage6-report.md). **Основа:** [07-groups-courses.md](07-groups-courses.md). **Дата решений:** 2026-10-23. @@ -205,12 +205,12 @@ Номера уточняются при реализации, чтобы не конфликтовать с максимумами master (identity V3, **academic V5**, progress V12). Максимумы этой ветки: -identity V9, **academic V11**, progress V13 (+V14 при необходимости аудита). +identity V9, **academic V12**, progress V13 (+V14 при необходимости аудита). | Сервис | Миграции | Содержание | |---|---|---| | identity | V4–V9 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status` | -| academic | V6–V11 + post-bootstrap step | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `V10` — уникальный индекс `lower(title)` по курсам; `V11` — backfill `audience='ALL'` для глобальных глав; `SET NOT NULL` — post-bootstrap шаг вне Flyway | +| academic | V6–V12 + post-bootstrap step | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `V10` — уникальный индекс `lower(title)` по курсам; `V11` — backfill `audience='ALL'` для глобальных глав; `V12` — backfill связей `article_assets` из markdown-ссылок; `SET NOT NULL` — post-bootstrap шаг вне Flyway | | progress | V13 (+ V14 при необходимости аудита) | `grade_comments.author_account_id`, `lab_grades.updated_by_account_id`, индекс `lab_grades(lab_id)`; аудит скачиваний — этап 6 | ### Переход со старой feature-миграции V10 на V13 @@ -288,7 +288,9 @@ identity V9, **academic V11**, progress V13 (+V14 при необходимос - **Межсервисная авторизация.** Самый сложный участок; ADR до кода. - **Перенос данных.** Нужны preflight и копия БД; группы с нестандартными именами и дубли номеров лаб могут заблокировать `NOT NULL`. -- **Активы.** Сейчас публичны и не связаны со статьями; нужна миграция связей. +- **Legacy-активы.** Связанные активы наследуют видимость статьи (миграция `V12`), + но файлы без markdown-ссылки остаются публичными; нужна инвентаризация и + привязка или удаление. - **JWT 30 дней.** Средний вариант блокировки оставляет остаточный риск для прямого API; фиксируем в документации. - **Комментарии без автора.** Старые записи останутся с «автор не указан». @@ -320,16 +322,17 @@ identity V9, **academic V11**, progress V13 (+V14 при необходимос Отчёт: [15-stage5-course-materials.md](15-stage5-course-materials.md). - **Этап 6 — проверка выполнена, закрыт не полностью.** Проверены полный прогон тестов/линтеров, upgrade миграций с master-схемы, security-матрица, - gateway и deploy-runbook. Отчёт: [16-stage6-report.md](16-stage6-report.md). - Не закрыты: права преподавателя на запись, видимость активов, аудит - скачиваний и CI. Остальные пункты hardening (индексы, backup/restore, - документация, smoke) описаны в отчёте и чек-листе. + gateway, видимость активов и deploy-runbook. Отчёт: [16-stage6-report.md](16-stage6-report.md). + Не закрыты: права преподавателя на запись, аудит скачиваний и CI. Остальные + пункты hardening (индексы, backup/restore, документация, smoke) описаны в + отчёте и чек-листе. ### 9.1. Известные пробелы после этапа 6 -- **Активы публичны.** `/api/v1/content/assets/{name}` не проверяет курс и - статью; нужна проверка `article_assets → article → section → chapter` и - visibility-aware маршруты. +- **Legacy-активы без привязки.** Маршрут `/api/v1/content/assets/{name}` + проверяет `article_assets → article → section → chapter`, а `V12` + восстанавливает связи из markdown. Файлы без ссылок в статьях остаются + публичными; нужна инвентаризация и привязка или удаление. - **Преподаватель — в основном read-only.** Авторство лабораторных/материалов, распределение, оценки, комментарии и защита остаются admin-only в UI/API; ТЗ ожидает преподавательский self-service. diff --git a/docs/10-stage0-report.md b/docs/10-stage0-report.md index b36e625..e63bc7f 100644 --- a/docs/10-stage0-report.md +++ b/docs/10-stage0-report.md @@ -11,10 +11,11 @@ - Миграции: - identity `V4–V9`: роли, группы-когорты, статусы студентов, повышение, история членства, статусы аккаунтов; - - academic `V6–V11`: курсы, преподаватели, группы курса, `labs.course_id`, + - academic `V6–V12`: курсы, преподаватели, группы курса, `labs.course_id`, материалы курса/аудитории, индексы активов; `V10` (уникальный индекс `lower(title)` по курсам) добавлена на этапе 2, после этого отчёта; `V11` backfill-ом проставляет `audience='ALL'` глобальным главам; + `V12` backfill-ом восстанавливает связи `article_assets` из markdown-ссылок; - progress `V13`: автор комментария и последнего изменения оценки. - Preflight-скрипты: - `deploy/preflight/groups-courses.sql` — до миграций; diff --git a/docs/12-stage2-report.md b/docs/12-stage2-report.md index 3be4dee..9670e15 100644 --- a/docs/12-stage2-report.md +++ b/docs/12-stage2-report.md @@ -74,6 +74,8 @@ - `V9__article_assets_indexes.sql` — индекс `article_assets(article_id)`. - `V10__course_title_unique.sql` — уникальный индекс `lower(title)` по курсам. - `V11__global_audience_all.sql` — backfill `audience='ALL'` для глобальных глав. +- `V12__asset_links_backfill.sql` — backfill связей `article_assets` из + markdown-ссылок для видимости активов. ## 2. API @@ -137,8 +139,11 @@ - **Материалы курса и аудитории.** Схема `chapters.course_id/audience` готова (`V8`), но URL `/learn/course/{course_no}/...` и проверка аудитории ещё не реализованы: читалка материалов пока работает по прежним глобальным правилам. -- **Активы.** `V9` добавляет только индекс; наследуемая видимость активов - от статьи → раздела → главы и проверка доступа при выдаче файлов — этап 3. +- **Активы — закрыто после этапа 5.** `V9` добавил индекс; + `V12__asset_links_backfill.sql` восстановил связи `article_assets` из + markdown-ссылок, а `/api/v1/content/assets/{name}` проверяет наследуемую + видимость статьи → раздела → главы. Остаточный случай — файлы без ссылок + (см. [15](15-stage5-course-materials.md)). - **Проверка аккаунтов преподавателей.** Academic принимает UUID без обращения к identity: не проверяются существование аккаунта, роль `TEACHER` и статус `ACTIVE`. Нужна внутренняя проверка identity при назначении. diff --git a/docs/15-stage5-course-materials.md b/docs/15-stage5-course-materials.md index fd9d70b..ae590ad 100644 --- a/docs/15-stage5-course-materials.md +++ b/docs/15-stage5-course-materials.md @@ -24,15 +24,32 @@ Миграция `V11__global_audience_all.sql` backfill-ом проставляет `ALL` старым глобальным главам; курсовые главы остаются с `audience = NULL`. -## Остающийся разрыв: активы +## Видимость активов (закрыто после этапа 5) -Публичный прокси активов `/api/v1/content/assets/{name}` и прокси web -`/api/v1/content/assets/{name}` **не изменялись**. Активы по-прежнему не привязаны -к главе/статье и не наследуют курсовую видимость. Для этапа 6 нужно: +Разрыв этапа 5 закрыт: активы привязаны к статьям и наследуют их видимость. -- связать `article_assets` со статьёй/главой и проверять доступ по токену; -- отдавать картинки курсовых статей только авторизованным пользователям курса; -- предусмотреть отдельный безопасный путь для публичных `ALL`-материалов. +- Добавлены entity/repository `article_assets`; импорт контента сохраняет + связи для файлов, на которые ссылаются статьи. +- Миграция `V12__asset_links_backfill.sql` backfill-ом восстанавливает связи + для уже загруженных статей: ищет в `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/...` открываются, а на + публичных страницах прокси остаётся анонимным. -В рамках этапа 5 это осознанно не реализуется, чтобы не ломать существующие -публичные ссылки на изображения. +**Остаточный риск — legacy-активы без привязки.** `V12` закрывает только файлы, +на которые есть ссылки в markdown. Если файл был загружен, но ни одна статья на +него не ссылается (осиротевший или удалённый из текста актив), он остаётся +публичным. Нужна инвентаризация таких файлов и привязка к статье либо удаление; +до этого поведение сохраняется осознанно, чтобы не ломать старые публичные +ссылки. diff --git a/docs/16-stage6-report.md b/docs/16-stage6-report.md index ec40a34..0c79db8 100644 --- a/docs/16-stage6-report.md +++ b/docs/16-stage6-report.md @@ -3,15 +3,15 @@ **Дата:** 2026-10-27. **Ветка:** `feature/groups-courses`. **Статус:** проверка этапа 6 выполнена; этап 6 закрыт **не полностью**. -Миграции, security-матрица, upgrade-тест и deploy-runbook проверены. Открытыми -остаются права преподавателя на запись, видимость активов, аудит и CI — см. +Миграции, security-матрица, upgrade-тест, видимость активов и deploy-runbook +проверены. Открытыми остаются права преподавателя на запись, аудит и CI — см. раздел 4. ## 1. Итоговый объём этапов 0–5 | Этап | Что реализовано | Отчёт | |---|---|---| -| 0 | Схема и контракты: identity `V4–V9`, academic `V6–V11`, progress `V13`; preflight-скрипты; ADR; контракт профиля студента курса | [10](10-stage0-report.md) | +| 0 | Схема и контракты: identity `V4–V9`, academic `V6–V12`, progress `V13`; preflight-скрипты; ADR; контракт профиля студента курса | [10](10-stage0-report.md) | | 1 | Роли `ADMIN/TEACHER/STUDENT`, группы-когорты и парсер имени, статусы студентов и аккаунтов, `membership_history`, импорт v2 | [11](11-stage1-report.md) | | 2 | Курсы, `course_no` для браузерных URL, история преподавателей и групп, bootstrap курса C, привязка лабораторных к курсу, копирование лабораторных, админ-UI | [12](12-stage2-report.md) | | 3 | Аккаунты преподавателей, курсовая область видимости, внутренние API identity с `LMS_INTERNAL_TOKEN`, кабинет преподавателя, профиль студента курса и скачивание всех попыток | [13](13-stage3-report.md) | @@ -26,6 +26,9 @@ - преподаватель видит только активные назначенные курсы; студент — только курсы активной группы; админ — все курсы; - студент видит только опубликованные оценки и комментарии; +- активы наследуют видимость статьи → раздела → главы: курсовые требуют доступа + к курсу, глобальные — аудитории, а старые файлы без привязки остаются + публичными (см. раздел 4); - вход закрыт для отчисленных, выпущенных и архивных студентов, а также для архивных аккаунтов; web дополнительно перепроверяет статус; - повышение и жизненный цикл пишут историю; применение повышения идемпотентно. @@ -42,6 +45,10 @@ Результат: `BUILD SUCCESSFUL`, код возврата `0`, все модули зелёные. +Счётчики включают новые тесты видимости активов: `academic-service` +9 +(`ContentAssetAccessIntegrationTest`, импорт активов), `web` +2 +(`ApiClientContentAssetTest`). Итог — **183 теста, 0 падений**. + Для получения актуальных счётчиков тесты запущены принудительно: ```bash @@ -51,11 +58,11 @@ | Модуль | Тестов | Failures | Errors | Skipped | |---|---:|---:|---:|---:| | identity-service | 62 | 0 | 0 | 0 | -| academic-service | 53 | 0 | 0 | 0 | -| web | 40 | 0 | 0 | 0 | +| academic-service | 62 | 0 | 0 | 0 | +| web | 42 | 0 | 0 | 0 | | progress-service | 12 | 0 | 0 | 0 | | markdown | 5 | 0 | 0 | 0 | -| **Итого** | **172** | **0** | **0** | **0** | +| **Итого** | **183** | **0** | **0** | **0** | `ktlintCheck` и `detekt` прошли без замечаний. Отдельных unit-тестов у gateway и `common-http` в текущем дереве нет; маршрутизация gateway проверена @@ -73,7 +80,7 @@ статья/актив, оценка, комментарий и попытка сдачи. 3. Поверх master применены миграции ветки: - identity `V4–V9`; - - academic `V6–V11`; + - academic `V6–V12`; - progress `V13`. 4. Выполнены postflight-запросы и повторное применение `V13`. @@ -87,7 +94,10 @@ - **academic:** добавлены `labs.course_id`, `chapters.course_id/audience`, индексы `labs_number_course_uidx`, `chapters_slug_global_uidx`, `chapters_slug_course_uidx`, `article_assets_article_idx`, - `courses_title_lower_uidx`. Курс C миграциями не создаётся — его создаёт + `courses_title_lower_uidx`. `V12__asset_links_backfill.sql` + восстанавливает связи `article_assets` из markdown-ссылок + `/api/v1/content/assets/{name}`; файлы без ссылок остаются несвязанными. + Курс C миграциями не создаётся — его создаёт bootstrap после входа админа, поэтому `labs_without_course` до bootstrap ожидаемо больше нуля. - **progress:** `grade_comments.author_account_id` и @@ -106,13 +116,20 @@ ### 2.3. Security-проверки -Проверено автоматическими тестами (полный прогон 172 теста) и разбором +Проверено автоматическими тестами (полный прогон 183 теста) и разбором конфигурации gateway. | Сценарий | Ожидание | Факт | |---|---|---| | Аноним → курс/материалы курса | нет доступа | `401` на `/api/v1/courses/{id}/content/tree`; `/api/v1/courses/by-no/{no}` — `401` | | Аноним → публичные материалы | только глобальные `ALL` | публичное дерево содержит только глобальные главы с `audience = ALL`; курсовая глава через `/api/v1/content/...` — `404` | +| Аноним → актив глобальной `ALL`-статьи | публично | `200` | +| Аноним → курсовой/`STUDENTS`/`TEACHERS` актив | `404` | `404`, существование файла не раскрывается | +| Студент своего курса → актив опубликованной статьи | доступ | `200`; скрытый курсовой актив — `404` | +| Студент/преподаватель чужого курса → курсовой актив | `403` | `403` | +| Преподаватель своего курса → скрытый курсовой актив | доступ | `200`; админ видит любой актив | +| Глобальный `STUDENTS`-актив → преподаватель | `403` | `403`; студент получает `200` | +| Legacy-актив без связи со статьёй | публично | `200`; остаточный риск, раздел 4 | | Админ → курс, профиль, скачивание | полный доступ | доступ к любому курсу и студенту, скачивание любой попытки курса | | Преподаватель своего курса | курс, группы, профиль, материалы | видит опубликованные и скрытые материалы, всех студентов активной группы курса, неопубликованные оценки и все попытки | | Преподаватель чужого курса | `403` | `403` на материалы, курс по `course_no`, состав группы и профиль | @@ -124,8 +141,8 @@ | Gateway → заголовки личности | клиент не подменяет | `X-Account-Id/Login/Role` удаляются и проставляются только после проверки JWT | | Web-сессия | быстрый отзыв доступа | статус перепроверяется не реже чем раз в 45 секунд; `active=false` или `401/403` инвалидируют сессию; недоступность identity не разлогинивает всех | -Остаточные риски перечислены в разделе 4: публичные активы и 30-дневный JWT -для прямого API. +Остаточные риски перечислены в разделе 4: legacy-активы без привязки, +права преподавателя на запись и 30-дневный JWT для прямого API. ## 3. Чек-лист деплоя @@ -147,7 +164,7 @@ 1. **Flyway.** `./deploy/deploy.sh up` собирает образы и запускает `postgres → identity → academic → progress → gateway → web`. Миграции применяются автоматически при старте сервисов: - identity `V4–V9`, academic `V6–V11`, progress `V13`. + identity `V4–V9`, academic `V6–V12`, progress `V13`. Отдельный ручной запуск Flyway не нужен. 2. **Bootstrap курса C.** После входа админа открыть `/admin/courses`. Пока курсов нет, показан блок первичной настройки. Bootstrap идемпотентно @@ -193,6 +210,9 @@ материалы; видны только опубликованные оценки и комментарии. - [ ] **Материалы курса:** админ и преподаватель видят скрытые статьи, студент — только опубликованные; чужой курс даёт `403`, аноним уходит на `/login`. +- [ ] **Активы:** картинки курсовой статьи открываются у студента своего курса + и преподавателя; чужой курс — `403`, скрытая картинка у студента — `404`, + публичная `ALL` — без входа; старый файл без привязки — публичен. - [ ] **Копирование лабораторной:** админ копирует лабораторную в другой курс; создаётся черновик с уровнями и вариантами; оценки, сдачи, защиты и назначения не копируются; занятый номер даёт `409`. @@ -206,13 +226,13 @@ ## 4. Известные пробелы и follow-up -1. **Активы всё ещё публичны.** Прокси `/api/v1/content/assets/{name}` (и - web-прокси) не проверяет курс/статью: `article_assets` не связаны с - видимостью материала. Курсовые картинки может получить любой, кто знает - имя файла. Нужно использовать связь `article_assets → article → section → - chapter` для проверки видимости и сделать visibility-aware маршруты, - отдельный публичный путь для `ALL`. Осознанно - вынесено из этапа 5, зафиксировано в [15](15-stage5-course-materials.md). +1. **Legacy-активы без привязки.** Видимость связанных активов реализована: + `V12` восстанавливает `article_assets` по markdown-ссылкам, а маршрут + `/api/v1/content/assets/{name}` проверяет курс/аудиторию и публикацию. + Остаточный случай — файлы, на которые нет ссылок ни в одной статье: они + остаются публичными. Нужна инвентаризация и привязка либо удаление таких + файлов; до этого fallback сохраняется, чтобы не ломать старые ссылки. + Детали — в [15](15-stage5-course-materials.md). 2. **Права преподавателя в основном read-only.** Кабинет преподавателя даёт обзор курса, групп, лабораторных и профилей. Авторство лабораторных и материалов, распределение вариантов, выставление оценок, комментарии, @@ -249,9 +269,9 @@ authoring лабораторных/материалов, распределение вариантов, выставление оценок и комментариев, коды и действия защиты; закрыть negative-тестами «свой/чужой курс». Это закрывает основной разрыв с ТЗ. -2. **Видимость активов.** Использовать связь `article_assets → article → - section → chapter`, проверять курс и публикацию при выдаче, оставить - отдельный публичный маршрут только для `ALL`. +2. **Инвентаризация legacy-активов.** Найти файлы без строк в `article_assets`, + привязать их к статьям или удалить; после этого можно убрать публичный + fallback для несвязанных файлов. 3. **UI авторства контента.** Выбор курса и аудитории при создании глав/ статей, чтобы преподаватель и админ не работали только через импорт; сейчас глобальные главы создаются с `ALL`, курсовые — без аудитории. @@ -263,9 +283,9 @@ ## 6. Итог -Этапы 0–5 реализованы и проверены. Security-матрица, upgrade-тест миграций и -deploy-runbook этапа 6 зелёные; отчёт и чек-лист передачи готовы. Этап 6 -**нельзя считать полностью завершённым** до закрытия прав преподавателя, -видимости активов и CI. До этого релиз допустим только с пониманием, что -преподаватели работают в режиме просмотра, а активы курсовых материалов -остаются публичными. +Этапы 0–5 реализованы и проверены. Security-матрица, upgrade-тест миграций, +видимость активов и deploy-runbook этапа 6 зелёные; отчёт и чек-лист передачи +готовы. Этап 6 **нельзя считать полностью завершённым** до закрытия прав +преподавателя и CI. Видимость связанных активов реализована; публичными +остаются только старые файлы без привязки к статье (см. раздел 4), а +преподаватели пока работают в режиме просмотра. diff --git a/docs/README.md b/docs/README.md index 4b511bb..faf06c4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,7 +22,7 @@ Preview Mermaid Support) и в IntelliJ IDEA (плагин Mermaid). | [12-stage2-report.md](12-stage2-report.md) | Отчёт этапа 2: курсы, bootstrap курса C, привязка и копирование лабораторных, миграция V10 | | [13-stage3-report.md](13-stage3-report.md) | Отчёт этапа 3: преподаватели, доступ к курсам, внутренние API, кабинет и профиль студента курса | | [14-stage4-report.md](14-stage4-report.md) | Отчёт этапа 4: повышение, академотпуск, отчисление, архив, восстановление и удаление | -| [15-stage5-course-materials.md](15-stage5-course-materials.md) | Отчёт этапа 5: курсовые материалы, публичные `ALL`-главы, оставшийся разрыв по активам | +| [15-stage5-course-materials.md](15-stage5-course-materials.md) | Отчёт этапа 5: курсовые материалы, публичные `ALL`-главы, видимость активов и legacy-файлы без привязки | | [16-stage6-report.md](16-stage6-report.md) | Отчёт этапа 6: проверка, upgrade-тест миграций, security-матрица, чек-лист деплоя и оставшиеся пробелы | | [ADR 0001](adr/0001-groups-courses-foundation.md) | Ключевые архитектурные решения: курс C, миграции, доступ, статусы, повышение |